Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions quest/m3/ts-runtime-throws.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ The rest are gaps. At `main` they include a panic its hook cannot locate
([#556](https://github.com/moq-dev/rust2/pull/556)), a pointer cast to `()` that is not an
`Arc`'s or `Rc`'s (`rs.arcErase`, [#562](https://github.com/moq-dev/rust2/pull/562),
[#565](https://github.com/moq-dev/rust2/pull/565)), `offset_from` of one Rust allocation
JS keeps in two arrays, a pointer moved past the array JS keeps it in (`AtIndex.reach`, a
`Vec`'s spare capacity, which [its own quest](/quest/m3/ts-unsafe/vec-spare-capacity.md)
owns, or a pointer kept in a field), `Bytes::slice_ref` of other storage, `alloc`'s bytes read as
JS keeps in two arrays, a pointer moved past the array JS keeps it in (`AtIndex.reach`, such
as one kept in a field), `Bytes::slice_ref` of other storage, `alloc`'s bytes read as
values, ranges stepped without their type, and fmt widths written as a placeholder.
`git grep 'rust2-ts: ' ts/runtime` lists them; classify each before fixing. The pointer
comparisons [#584](https://github.com/moq-dev/rust2/pull/584) refuses at translation, where
Expand Down
1 change: 0 additions & 1 deletion quest/m3/ts-unsafe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ Public API: the translator's output.

## Required

- [A Vec's spare capacity](/quest/m3/ts-unsafe/vec-spare-capacity.md) - writes through `as_mut_ptr` past the length, committed by `set_len`, translate for any `Vec<T>`
- [A raw pointer behind a reference](/quest/m3/ts-unsafe/raw-behind-ref.md) - `&raw const *r` of a value JS copies points where the reference came from
- [Vec::from_raw_parts of a typed allocation](/quest/m3/ts-unsafe/vec-from-raw-parts.md) - a `Vec` adopts an `alloc`ed array of `T`s, with its length, capacity, and drops
- [Unions over a type parameter](/quest/m3/ts-unsafe/generic-unions.md) - each instance of a generic union is laid out with its own offsets
Expand Down
4 changes: 0 additions & 4 deletions quest/m3/ts-unsafe/binary-heap-mir.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,3 @@ No consumer at the moq pin; the port works but must track std by hand. The B-tre
[#577](https://github.com/moq-dev/rust2/pull/577) is the precedent.

Public API: the translator's output.

## Required

- [A Vec's spare capacity](/quest/m3/ts-unsafe/vec-spare-capacity.md) - std's `PeekMut` calls `set_len`
4 changes: 0 additions & 4 deletions quest/m3/ts-unsafe/vec-drain-mir.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,3 @@ to std rather than a blocker: the port must track std by hand. The B-tree port o
bench's drain-heavy workloads against the port.

Public API: the translator's output.

## Required

- [A Vec's spare capacity](/quest/m3/ts-unsafe/vec-spare-capacity.md) - std's `Drain` calls `set_len`
25 changes: 0 additions & 25 deletions quest/m3/ts-unsafe/vec-spare-capacity.md

This file was deleted.

17 changes: 14 additions & 3 deletions ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ crate translation reaches is refused, and so are two versions of one crate it re
| `Vec<T>` | `rs.Vec<T>`: its items, the array `items`, and the capacity std's holds; `rs.vecFrom(items)` makes one of an array with room for exactly them. `rs.ByteVec` of bytes |
| `Cow<[T]>`, `Cow<str>` | `rs.Cow<T>`: the `Vec` it owns, or an `rs.Borrowed` holding the slice it borrows; `rs.ByteCow` for bytes, an `rs.ByteVec` or an `rs.ByteSlice`; `rs.StrCow` for text, the `rs.String` it owns or an `rs.Borrowed` holding the string it borrows, whose text `rs.strCowStr` reads |
| `&T`, `&mut T`, `Box<T>`, `Reverse<T>`, `NonZero<T>`, `AssertUnwindSafe<T>` | the value; a `&mut` to a value JS copies, such as a number, an `Option`, or a reference, or to an `Arc`, a `Waker`, or a `LocalKey`, whose copies share what it points at, is an `rs.Place<T>`, which for what a box holds is the place holding the box |
| `*const T`, `*mut T`, `NonNull<T>` | what a `&mut T` is: the object, an `rs.Slice` of a `[T; N]`'s elements or an `rs.ByteSlice` of a `[u8; N]`'s bytes, which compares by the array or buffer and offset they are at, refused for an array of one, which `array::from_ref`, `from_mut` of a variable, and `Option::as_slice` make of their own, or an `rs.Place<T>` to a value JS copies and to a library type such as a `Vec` or an `Arc`, which into an array, slice, or `Vec` is the `rs.AtIndex` of its element, which `add`, `sub`, `offset`, their wrapping forms, `offset_from`, `ptr::copy`, 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, as is comparing pointers to other values JS copies, each a place JS makes of its own; `rs.NULL` for null and `rs.DANGLING` for dangling. A `*const ()` is an identity, such as a waker's data, and `rs.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A pointer to a value JS keeps as an object cast to one, by `as` or `cast`, is the same object, which `rs.ptrErase` notes the type of, so a cast back to a pointer to another type fails at run time, and null and dangling stay so, dangling at the alignment of the type it was cast from; one to a value JS copies is the same place where an `Arc`'s or `Rc`'s `into_raw` or `as_ptr` gave it, its value's one place, which `rs.arcErase` checks where the body doesn't show it, and refused where the body shows a borrow's, a new place each borrow, as is casting one to a view of bytes, a new one each borrow, or one of no fixed size. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, the state an `rs` class such as `rs.LazyCell` keeps private included, so a pointer to a part kept as an object, such as a field of a tuple or struct type, is refused wherever the crate may put another object there while a pointer into that memory may still be read, which a write into a fresh allocation, or a variable before any pointer into it is taken, can't, whether by index or through std's iterators over its slots; a read copies what the crate writes into in place |
| `*const T`, `*mut T`, `NonNull<T>` | what a `&mut T` is: the object, an `rs.Slice` of a `[T; N]`'s elements or an `rs.ByteSlice` of a `[u8; N]`'s bytes, which compares by the array or buffer and offset they are at, refused for an array of one, which `array::from_ref`, `from_mut` of a variable, and `Option::as_slice` make of their own, or an `rs.Place<T>` to a value JS copies and to a library type such as a `Vec` or an `Arc`, which into an array, slice, or `Vec` is the `rs.AtIndex` of its element, which `add`, `sub`, `offset`, their wrapping forms, `offset_from`, `ptr::copy`, 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 but for a `Vec`'s room up to its capacity, fails at run time; a pointer into an array of values JS keeps as objects is refused, as is comparing pointers to other values JS copies, each a place JS makes of its own; `rs.NULL` for null and `rs.DANGLING` for dangling. A `*const ()` is an identity, such as a waker's data, and `rs.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A pointer to a value JS keeps as an object cast to one, by `as` or `cast`, is the same object, which `rs.ptrErase` notes the type of, so a cast back to a pointer to another type fails at run time, and null and dangling stay so, dangling at the alignment of the type it was cast from; one to a value JS copies is the same place where an `Arc`'s or `Rc`'s `into_raw` or `as_ptr` gave it, its value's one place, which `rs.arcErase` checks where the body doesn't show it, and refused where the body shows a borrow's, a new place each borrow, as is casting one to a view of bytes, a new one each borrow, or one of no fixed size. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, the state an `rs` class such as `rs.LazyCell` keeps private included, so a pointer to a part kept as an object, such as a field of a tuple or struct type, is refused wherever the crate may put another object there while a pointer into that memory may still be read, which a write into a fresh allocation, or a variable before any pointer into it is taken, can't, whether by index or through std's iterators over its slots; a read copies what the crate writes into in place |
| `MaybeUninit<T>` | `T`: `undefined` until written, `0` for a byte, or an object whose parts kept as objects are there, holding `undefined`, as each of `Box::new_uninit_slice`'s is; a slice's `write_clone_of_slice` clones into its slots without dropping what they held |
| `ManuallyDrop<T>` | `T`, which nothing drops but its own `drop`; its `Debug`, comparisons, and constants are refused |
| `union` | an object of slots named by offset, `at0` and `at8`, each holding the field part kept there; fields must overlap in the same type, or in a non-null pointer and an `Option` of it |
Expand Down Expand Up @@ -1041,8 +1041,19 @@ they need.
`push/vec`, and tracking it on each pop up to 2.8x on `push/stack` (decided with the
maintainer on 2026-11-03). A `Vec<u8>`'s is its buffer's length, so its
`spare_capacity_mut` is an `rs.ByteSlice` of the buffer past its length, which `set_len`
takes in, panicking past the capacity as std's check does, which can't unwind; any other `Vec`'s is refused,
as no JS array holds room past its length. Their `try_reserve` and `try_reserve_exact`, and a
takes in, panicking past the capacity as std's check does, which can't unwind. Any other
`Vec`'s items are a JS array as long as it is, which a write past its end would lengthen,
so the runtime keeps what Rust holds past its length apart, in an `rs.Room` by index. A
write past the length, up to the capacity, through a pointer from `as_ptr` or
`as_mut_ptr`, by `ptr::copy`, through a slice of the pointers, or through a slot of
`spare_capacity_mut`, a view of the items and the room, lands there; `set_len` takes slots
in and gives items up to it, so a pointer reads them back past the length, as std's
`Drain` and `retain` do. A slot never written holds what an unwritten `MaybeUninit` is,
which a copy keeps. A step other than `set_len` that shortens it, such as `pop` or
`truncate`, leaves the room as it was: std's `Vec` makes no promise to keep what it
removes, so the slot it gave up is uninitialized, and `set_len` requires every slot it
takes in to be initialized, which makes lengthening over that slot undefined in Rust
(decided with the maintainer on 2026-11-07). Their `try_reserve` and `try_reserve_exact`, and a
`String`'s, give `Ok` where `reserve` makes room and `TryReserveError`'s
`CapacityOverflow` where it panics, and `AllocError`, of the layout std's asks for, only
where JS fails to allocate a byte `Vec`'s or `VecDeque`'s `Uint8Array` (decided with the
Expand Down
7 changes: 7 additions & 0 deletions ts/fixtures/lang/elements/expected/core/bool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@ export function thenSome$06c3xh5(self: boolean, t: [rs.u8, rs.u8]): [rs.u8, rs.u
return t;
}

export function thenSome$1gvhopg(self: boolean, t: rs.u16): rs.u16 | undefined {
if (!self) {
return undefined;
}
return t;
}

//# sourceMappingURL=bool.ts.map
7 changes: 7 additions & 0 deletions ts/fixtures/lang/elements/expected/core/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,11 @@ export function mapOr$13kt6x8(self: [rs.u8, rs.u8] | undefined, default_: Uint8A
return f(self);
}

export function mapOr$17z0l8m(self: rs.u16 | undefined, default_: Uint8Array, _: (a0: rs.u16) => Uint8Array): Uint8Array {
if (self === undefined) {
return default_;
}
return rs.u16ToBeBytes(self).reverse();
}

//# sourceMappingURL=option.ts.map
14 changes: 7 additions & 7 deletions ts/fixtures/lang/elements/expected/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function vecs(data: rs.ByteSlice, out: rs.ByteVec): void {
rs.iterExtend(v, rs.iterSlice([7, 8]), undefined, 4, true);
const len: rs.usize = v.items.length;
const at: rs.usize = rs.usizeRem(option_unwrapOr$1fr498u(rs.byteSliceFirst(data), 0) as rs.usize, rs.usizeSub(len, 1));
const p: rs.Place<rs.u32> = rs.arrayAsPtr(v.items);
const p: rs.Place<rs.u32> = rs.vecAsPtr(v, 4);
rs.ptrCopy(rs.ptrAdd(p, at), rs.ptrAdd(p, rs.usizeAdd(at, 1)), rs.usizeSub(rs.usizeSub(len, at), 1));
rs.ptrAdd(p, at).v = 1000;
emit(rs.ptrSlice(p, len), out);
Expand All @@ -41,9 +41,9 @@ export function vecs(data: rs.ByteSlice, out: rs.ByteVec): void {
part.data[part.start] = rs.u32Add(part.data[part.start], 3);
rs.sliceReverse(part);
const w: rs.Vec<rs.u32> = rs.vecFromElem(0, len, undefined);
rs.ptrCopy(p, rs.arrayAsPtr(w.items), len);
rs.ptrCopy(p, rs.ptrAdd(rs.arrayAsPtr(w.items), at), 1);
rs.ptrCopy(p, rs.ptrAdd(rs.arrayAsPtr(w.items), rs.usizeSub(len, 1)), 1);
rs.ptrCopy(p, rs.vecAsPtr(w, 4), len);
rs.ptrCopy(p, rs.ptrAdd(rs.vecAsPtr(w, 4), at), 1);
rs.ptrCopy(p, rs.ptrAdd(rs.vecAsPtr(w, 4), rs.usizeSub(len, 1)), 1);
emit(rs.sliceFrom(w.items), out);
emit(rs.sliceFrom(v.items), out);
rs.byteVecPush(out, rs.ptrAdd(rs.sliceAsPtr(rs.sliceFrom(v.items)), rs.usizeDiv(len, 2)).v & 0xff);
Expand Down Expand Up @@ -182,7 +182,7 @@ export function inline(data: rs.ByteSlice, out: rs.ByteVec): void {
return [_22, 4] as [rs.u8, rs.u8];
})]).map(copy$Option_tuple2_u8_u8));
const dst: rs.Vec<[rs.u8, rs.u8] | undefined> = rs.vecFromElem(undefined as [rs.u8, rs.u8] | undefined, 3, copy$Option_tuple2_u8_u8);
rs.ptrCopy(rs.arrayAsPtr(src.items), rs.arrayAsPtr(dst.items), 3, copy$Option_tuple2_u8_u8);
rs.ptrCopy(rs.vecAsPtr(src, 3), rs.vecAsPtr(dst, 3), 3, copy$Option_tuple2_u8_u8);
const at2 = rs.idx(dst.items, 0);
if (dst.items[at2] !== undefined) {
dst.items[at2][0] = 9;
Expand Down Expand Up @@ -343,7 +343,7 @@ export function rawSlices(data: rs.ByteSlice, out: rs.ByteVec): void {
return _2 as rs.u32;
}), rs.iterSlice([5, 6, 7])), undefined, 4, true);
const len: rs.usize = v.items.length;
const p: rs.RawSlice<rs.u32> = new rs.RawSlice(rs.arrayAsPtr(v.items), len);
const p: rs.RawSlice<rs.u32> = new rs.RawSlice(rs.vecAsPtr(v, 4), len);
rs.idx(p, 0);
const slice = p.deref();
const _19: rs.u32 = rs.u32Add(slice.data[slice.start], 1);
Expand Down Expand Up @@ -381,7 +381,7 @@ export function rawSlices(data: rs.ByteSlice, out: rs.ByteVec): void {
const long: rs.RawSlice<rs.u32> = new rs.RawSlice(rs.sliceAsPtr(rs.sliceFrom(short)), 10);
rs.byteVecPush(out, long.length & 0xff);
rs.byteVecPush(out, rs.ptrAdd(long.ptr, 1).v & 0xff);
const pointer = rs.arrayAsPtr(v.items);
const pointer = rs.vecAsPtr(v, 4);
const nn: rs.RawSlice<rs.u32> = new rs.RawSlice(rs.optionUnwrap(pointer === rs.NULL ? undefined : pointer), 2);
rs.byteVecPush(out, nn.length & 0xff);
const _103: rs.RawSlice<rs.u32> = nn;
Expand Down
28 changes: 20 additions & 8 deletions ts/fixtures/lang/elements/expected/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import * as rs from "./rs.ts";
import { vecs, arrays, segments, inline, edges, rawSlices, emptyParts } from "./elements.ts";
import * as Inline from "./elements.Inline.ts";
import { spares } from "./spare.ts";

export function copy$Option_tuple2_u8_u8(v: [rs.u8, rs.u8] | undefined): [rs.u8, rs.u8] | undefined {
return v === undefined ? v : copy$tuple2_u8_u8(v);
Expand All @@ -26,6 +27,17 @@ export function copy$mem_MaybeUninit_array_Option_tuple2_u8_u8(v: Array<[rs.u8,
return v.map(copy$mem_MaybeUninit_Option_tuple2_u8_u8);
}

export function copy$mem_MaybeUninit_tuple2_u8_u32(v: [rs.u8, rs.u32]): [rs.u8, rs.u32] {
if (v === undefined) {
return v;
}
return [v[0], v[1]];
}

export function copy$tuple2_u8_u32(v: [rs.u8, rs.u32]): [rs.u8, rs.u32] {
return [v[0], v[1]];
}

export function copy$tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs.u8] {
return [v[0], v[1]];
}
Expand All @@ -43,21 +55,21 @@ export function drop$elements_Inline_sync_Arc_u8(_1: Inline.Inline<rs.Arc<rs.u8>
* their count, so a case added at the end moves no other.
*/
export function cases(): rs.Vec<[string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]> {
return rs.vecFrom([["elements::vecs", (_24: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): undefined => {
return rs.vecFrom([["elements::vecs", (_27: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): undefined => {
vecs(data, out);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::arrays", (_25: rs.u8, data2: rs.ByteSlice, out2: rs.ByteVec): undefined => {
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::arrays", (_28: rs.u8, data2: rs.ByteSlice, out2: rs.ByteVec): undefined => {
arrays(data2, out2);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::segments", (_26: rs.u8, data3: rs.ByteSlice, out3: rs.ByteVec): undefined => {
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::segments", (_29: rs.u8, data3: rs.ByteSlice, out3: rs.ByteVec): undefined => {
segments(data3, out3);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::inline", (_27: rs.u8, data4: rs.ByteSlice, out4: rs.ByteVec): undefined => {
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::inline", (_210: rs.u8, data4: rs.ByteSlice, out4: rs.ByteVec): undefined => {
inline(data4, out4);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::edges", (_28: rs.u8, data5: rs.ByteSlice, out5: rs.ByteVec): undefined => {
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::edges", (_211: rs.u8, data5: rs.ByteSlice, out5: rs.ByteVec): undefined => {
edges(data5, out5);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::raw_slices", (_29: rs.u8, data6: rs.ByteSlice, out6: rs.ByteVec): undefined => {
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::raw_slices", (_212: rs.u8, data6: rs.ByteSlice, out6: rs.ByteVec): undefined => {
rawSlices(data6, out6);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::empty_parts", (_210: rs.u8, data7: rs.ByteSlice, out7: rs.ByteVec): undefined => {
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["elements::empty_parts", (_213: rs.u8, data7: rs.ByteSlice, out7: rs.ByteVec): undefined => {
emptyParts(data7, out7);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]]);
}] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["spare::spares", spares as (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]]);
}

//# sourceMappingURL=lib.ts.map
Loading