From ceaac71c01f8d9968938c1b0212fd281b5a81310 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:07:43 -0800 Subject: [PATCH 01/10] chore(quest): claim quest/m3/ts-runtime-throws.md Quest-Claim: 7214e1e20bba0da1 From 8126163c4c8a964a5182b255f892650f0236e465 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:15:15 -0800 Subject: [PATCH 02/10] fix(ts): locate bytes' panics from its compiled code, as rustc names them under any remap Co-Authored-By: Claude Opus 5.5 --- .../lang/panics/hook/expected/bytes.ts | 2 +- .../hook/expected/bytes/buf/take.Take.ts | 14 +- .../bytes/buf/uninit_slice.UninitSlice.ts | 6 +- .../lang/panics/hook/expected/hooked.ts | 6 +- ts/src/lower/caller.rs | 178 +++++++----------- ts/tests/fixtures.rs | 27 ++- 6 files changed, 105 insertions(+), 128 deletions(-) diff --git a/ts/fixtures/lang/panics/hook/expected/bytes.ts b/ts/fixtures/lang/panics/hook/expected/bytes.ts index be64a50ee..02ac6c41d 100644 --- a/ts/fixtures/lang/panics/hook/expected/bytes.ts +++ b/ts/fixtures/lang/panics/hook/expected/bytes.ts @@ -8,7 +8,7 @@ export function panicAdvance(errorInfo: TryGetError.TryGetError): never { const _8: rs.Fmt = (f: rs.Formatter) => rs.usizeDisplay(arg, f); const arg2 = errorInfo.requested; const args3 = [_8, (f: rs.Formatter) => rs.usizeDisplay(arg2, f)]; - rs.panicFmt(["advance out of bounds: the len is ", args3[0], " but advancing by ", args3[1]], { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs", line: 207, col: 5 }); + rs.panicFmt(["advance out of bounds: the len is ", args3[0], " but advancing by ", args3[1]], { file: "bytes/source/lib.rs", line: 207, col: 5 }); } //# sourceMappingURL=bytes.ts.map diff --git a/ts/fixtures/lang/panics/hook/expected/bytes/buf/take.Take.ts b/ts/fixtures/lang/panics/hook/expected/bytes/buf/take.Take.ts index df0c613a5..ad76820b1 100644 --- a/ts/fixtures/lang/panics/hook/expected/bytes/buf/take.Take.ts +++ b/ts/fixtures/lang/panics/hook/expected/bytes/buf/take.Take.ts @@ -72,8 +72,8 @@ export function tryCopyToSlice$bytes_Buf_bytes_Bytes(self: Take, d while (!(dst.length === 0)) { const src: rs.ByteSlice = chunk$bytes_Buf_bytes_Bytes_mut(self); const cnt: rs.usize = rs.min(src.length, dst.length); - rs.byteSliceCopyFromSlice(rs.byteSliceRange(dst, 0, cnt, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_impl.rs", line: 1178, col: 16 }), rs.byteSliceRange(src, 0, cnt, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_impl.rs", line: 1178, col: 44 }), { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_impl.rs", line: 1178, col: 24 }); - dst = rs.byteSliceRange(dst, cnt, undefined, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_impl.rs", line: 1179, col: 27 }); + rs.byteSliceCopyFromSlice(rs.byteSliceRange(dst, 0, cnt, { file: "bytes/source/buf/buf_impl.rs", line: 1178, col: 16 }), rs.byteSliceRange(src, 0, cnt, { file: "bytes/source/buf/buf_impl.rs", line: 1178, col: 44 }), { file: "bytes/source/buf/buf_impl.rs", line: 1178, col: 24 }); + dst = rs.byteSliceRange(dst, cnt, undefined, { file: "bytes/source/buf/buf_impl.rs", line: 1179, col: 27 }); advance$bytes_Buf_bytes_Bytes(self, cnt); } return rs.OK; @@ -225,7 +225,7 @@ export function remaining$bytes_Buf_bytes_Bytes(self: Take): rs.us */ export function chunk$bytes_Buf_bytes_Bytes(self: Take): rs.ByteSlice { const bytes: rs.ByteSlice = self.inner; - return rs.byteSliceRange(bytes, 0, min$00xoube(bytes.length, self.limit), { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/take.rs", line: 142, col: 15 }); + return rs.byteSliceRange(bytes, 0, min$00xoube(bytes.length, self.limit), { file: "bytes/source/buf/take.rs", line: 142, col: 15 }); } /** @@ -262,10 +262,10 @@ export function chunk$bytes_Buf_bytes_Bytes(self: Take): rs.ByteSl */ export function advance$bytes_Buf_bytes_Bytes(self: Take, cnt: rs.usize): void { if (!(cnt <= self.limit)) { - rs.panic("assertion failed: cnt <= self.limit", { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/take.rs", line: 146, col: 9 }); + rs.panic("assertion failed: cnt <= self.limit", { file: "bytes/source/buf/take.rs", line: 146, col: 9 }); } else { rs.bytesAdvance(rs.place(() => self.inner, (value) => self.inner = value), cnt); - self.limit = rs.usizeSub(self.limit, cnt, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/take.rs", line: 148, col: 9 }); + self.limit = rs.usizeSub(self.limit, cnt, { file: "bytes/source/buf/take.rs", line: 148, col: 9 }); } } @@ -292,10 +292,10 @@ export function advance$bytes_Buf_bytes_Bytes(self: Take, cnt: rs. */ export function copyToBytes$bytes_Buf_bytes_Bytes(self: Take, len: rs.usize): rs.ByteSlice { if (!(len <= remaining$bytes_Buf_bytes_Bytes_mut(self))) { - rs.panicFmt(["`len` greater than remaining"], { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/take.rs", line: 152, col: 9 }); + rs.panicFmt(["`len` greater than remaining"], { file: "bytes/source/buf/take.rs", line: 152, col: 9 }); } else { const r: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => self.inner, (value) => self.inner = value), len); - self.limit = rs.usizeSub(self.limit, len, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/take.rs", line: 155, col: 9 }); + self.limit = rs.usizeSub(self.limit, len, { file: "bytes/source/buf/take.rs", line: 155, col: 9 }); return r; } } diff --git a/ts/fixtures/lang/panics/hook/expected/bytes/buf/uninit_slice.UninitSlice.ts b/ts/fixtures/lang/panics/hook/expected/bytes/buf/uninit_slice.UninitSlice.ts index ef9af030b..ea878829b 100644 --- a/ts/fixtures/lang/panics/hook/expected/bytes/buf/uninit_slice.UninitSlice.ts +++ b/ts/fixtures/lang/panics/hook/expected/bytes/buf/uninit_slice.UninitSlice.ts @@ -64,9 +64,9 @@ export function uninit(slice: rs.ByteSlice): rs.ByteSlice { */ export function writeByte(self: rs.ByteSlice, index: rs.usize, byte: rs.u8): void { if (!(index < len(self))) { - rs.panic("assertion failed: index < self.len()", { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/uninit_slice.rs", line: 108, col: 9 }); + rs.panic("assertion failed: index < self.len()", { file: "bytes/source/buf/uninit_slice.rs", line: 108, col: 9 }); } else { - asMutPtr(indexMut$ops_IndexMut_ops_RangeFrom_usize(self, new rs.RangeFrom(index, rs.usizeStep), { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/uninit_slice.rs", line: 110, col: 22 })).v = byte; + asMutPtr(indexMut$ops_IndexMut_ops_RangeFrom_usize(self, new rs.RangeFrom(index, rs.usizeStep), { file: "bytes/source/buf/uninit_slice.rs", line: 110, col: 22 })).v = byte; } } @@ -96,7 +96,7 @@ export function copyFromSlice(self: rs.ByteSlice, src: rs.ByteSlice): void { const _5: rs.usize = len(self); const _8: rs.usize = src.length; if (!(_5 === _8)) { - rs.assertFailed("==", (f: rs.Formatter) => rs.usizeDebug(_5, f), (f: rs.Formatter) => rs.usizeDebug(_8, f), undefined as rs.Arguments | undefined, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/uninit_slice.rs", line: 137, col: 9 }); + rs.assertFailed("==", (f: rs.Formatter) => rs.usizeDebug(_5, f), (f: rs.Formatter) => rs.usizeDebug(_8, f), undefined as rs.Arguments | undefined, { file: "bytes/source/buf/uninit_slice.rs", line: 137, col: 9 }); } else { rs.ptrCopy(rs.byteSliceAsPtr(src), asMutPtr(self), len(self)); } diff --git a/ts/fixtures/lang/panics/hook/expected/hooked.ts b/ts/fixtures/lang/panics/hook/expected/hooked.ts index ef810dc01..881cf3e9f 100644 --- a/ts/fixtures/lang/panics/hook/expected/hooked.ts +++ b/ts/fixtures/lang/panics/hook/expected/hooked.ts @@ -87,7 +87,7 @@ export function hook(): void { captured(info); }, (): void => { rs.drop(captured); - }), { capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/raw_vec/mod.rs", line: 29, col: 5 }, trusted_capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/spec_from_iter_nested.rs", line: 57, col: 18 }, swap_remove: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2178, col: 13 }, vec_set_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2138, col: 9 }, into_flattened: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 3667, col: 36 }, rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3902, col: 9 }, rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3948, col: 9 }, ord_violation: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/sort/shared/smallsort.rs", line: 854, col: 5 }, ord_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cmp.rs", line: 1166, col: 9 }, f32_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f32.rs", line: 1665, col: 9 }, f64_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f64.rs", line: 1643, col: 9 }, encode_utf8: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2507, col: 9 }, encode_utf16: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2601, col: 13 }, format: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/fmt.rs", line: 657, col: 14 }, to_string: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/string.rs", line: 2997, col: 14 }, debug_struct_fields: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/fmt/mod.rs", line: 2572, col: 9 }, exact_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/exact_size.rs", line: 122, col: 9 }, join_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/str.rs", line: 177, col: 10 }, int_sum: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/accum.rs", line: 206, col: 1 }, int_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 554, col: 1 }, char_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 119, col: 45 }, hash_capacity: { file: "/cargo/registry/25cdd57fae9f0462/hashbrown-0.17.1/src/raw.rs", line: 36, col: 40 }, btree_range_equal_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 110, col: 21 }, btree_range_equal_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 112, col: 21 }, btree_range_order_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 119, col: 21 }, btree_range_order_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 121, col: 21 }, deque_insert: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2612, col: 9 }, deque_swap_i: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1062, col: 9 }, deque_swap_j: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1063, col: 9 }, deque_rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3198, col: 9 }, deque_rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3243, col: 9 }, deque_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2724, col: 9 }, list_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/linked_list.rs", line: 1015, col: 9 }, mutex_lock: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sys/sync/mutex/no_threads.rs", line: 28, col: 9 }, once_force: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sync/once.rs", line: 227, col: 20 }, reentrant_init: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/once.rs", line: 325, col: 66 }, lazy_poisoned: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/lazy.rs", line: 386, col: 5 }, ready_poll: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 23, col: 35 }, ready_into_inner: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 46, col: 16 }, duration_new: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 281, col: 18 }, duration_from_mins: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 540, col: 13 }, duration_from_hours: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 510, col: 13 }, duration_add: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1345, col: 31 }, duration_sub: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1365, col: 31 }, duration_mul: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1385, col: 31 }, zip_next_back: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/adapters/zip.rs", line: 213, col: 5 }, bytes_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs", line: 207, col: 5 }, bytes_does_not_fit: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs", line: 215, col: 5 }, bytes_slice: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 374, col: 28 }, bytes_slice_ref_start: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 426, col: 9 }, bytes_slice_ref_end: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 432, col: 9 }, bytes_split_off: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 481, col: 9 }, bytes_split_to: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 533, col: 9 }, bytes_buf_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 694, col: 9 }, bytes_mut_split_off: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 329, col: 9 }, bytes_mut_split_to: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 408, col: 9 }, bytes_mut_set_len: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 545, col: 9 }, bytes_mut_overflow: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 700, col: 21 }, bytes_mut_overflow_offset: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 720, col: 29 }, bytes_mut_buf_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 1246, col: 9 } }); + }), { capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/raw_vec/mod.rs", line: 29, col: 5 }, trusted_capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/spec_from_iter_nested.rs", line: 57, col: 18 }, swap_remove: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2178, col: 13 }, vec_set_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2138, col: 9 }, into_flattened: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 3667, col: 36 }, rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3902, col: 9 }, rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3948, col: 9 }, ord_violation: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/sort/shared/smallsort.rs", line: 854, col: 5 }, ord_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cmp.rs", line: 1166, col: 9 }, f32_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f32.rs", line: 1665, col: 9 }, f64_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f64.rs", line: 1643, col: 9 }, encode_utf8: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2507, col: 9 }, encode_utf16: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2601, col: 13 }, format: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/fmt.rs", line: 657, col: 14 }, to_string: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/string.rs", line: 2997, col: 14 }, debug_struct_fields: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/fmt/mod.rs", line: 2572, col: 9 }, exact_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/exact_size.rs", line: 122, col: 9 }, join_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/str.rs", line: 177, col: 10 }, int_sum: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/accum.rs", line: 206, col: 1 }, int_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 554, col: 1 }, char_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 119, col: 45 }, hash_capacity: { file: "/cargo/registry/25cdd57fae9f0462/hashbrown-0.17.1/src/raw.rs", line: 36, col: 40 }, btree_range_equal_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 110, col: 21 }, btree_range_equal_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 112, col: 21 }, btree_range_order_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 119, col: 21 }, btree_range_order_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 121, col: 21 }, deque_insert: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2612, col: 9 }, deque_swap_i: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1062, col: 9 }, deque_swap_j: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1063, col: 9 }, deque_rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3198, col: 9 }, deque_rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3243, col: 9 }, deque_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2724, col: 9 }, list_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/linked_list.rs", line: 1015, col: 9 }, mutex_lock: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sys/sync/mutex/no_threads.rs", line: 28, col: 9 }, once_force: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sync/once.rs", line: 227, col: 20 }, reentrant_init: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/once.rs", line: 325, col: 66 }, lazy_poisoned: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/lazy.rs", line: 386, col: 5 }, ready_poll: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 23, col: 35 }, ready_into_inner: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 46, col: 16 }, duration_new: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 281, col: 18 }, duration_from_mins: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 540, col: 13 }, duration_from_hours: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 510, col: 13 }, duration_add: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1345, col: 31 }, duration_sub: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1365, col: 31 }, duration_mul: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1385, col: 31 }, zip_next_back: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/adapters/zip.rs", line: 213, col: 5 }, bytes_advance: { file: "bytes/source/lib.rs", line: 207, col: 5 }, bytes_does_not_fit: { file: "bytes/source/lib.rs", line: 215, col: 5 }, bytes_slice: { file: "bytes/source/bytes.rs", line: 374, col: 28 }, bytes_slice_ref_start: { file: "bytes/source/bytes.rs", line: 426, col: 9 }, bytes_slice_ref_end: { file: "bytes/source/bytes.rs", line: 432, col: 9 }, bytes_split_off: { file: "bytes/source/bytes.rs", line: 481, col: 9 }, bytes_split_to: { file: "bytes/source/bytes.rs", line: 533, col: 9 }, bytes_buf_advance: { file: "bytes/source/bytes.rs", line: 694, col: 9 }, bytes_mut_split_off: { file: "bytes/source/bytes_mut.rs", line: 329, col: 9 }, bytes_mut_split_to: { file: "bytes/source/bytes_mut.rs", line: 408, col: 9 }, bytes_mut_set_len: { file: "bytes/source/bytes_mut.rs", line: 545, col: 9 }, bytes_mut_overflow: { file: "bytes/source/bytes_mut.rs", line: 700, col: 21 }, bytes_mut_overflow_offset: { file: "bytes/source/bytes_mut.rs", line: 720, col: 29 }, bytes_mut_buf_advance: { file: "bytes/source/bytes_mut.rs", line: 1246, col: 9 } }); } export function tracked(n: rs.u8, caller: rs.Location): rs.u8 { @@ -220,7 +220,7 @@ export function aborted(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void } }, (): void => { rs.drop(captured2); - }), { capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/raw_vec/mod.rs", line: 29, col: 5 }, trusted_capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/spec_from_iter_nested.rs", line: 57, col: 18 }, swap_remove: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2178, col: 13 }, vec_set_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2138, col: 9 }, into_flattened: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 3667, col: 36 }, rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3902, col: 9 }, rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3948, col: 9 }, ord_violation: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/sort/shared/smallsort.rs", line: 854, col: 5 }, ord_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cmp.rs", line: 1166, col: 9 }, f32_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f32.rs", line: 1665, col: 9 }, f64_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f64.rs", line: 1643, col: 9 }, encode_utf8: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2507, col: 9 }, encode_utf16: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2601, col: 13 }, format: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/fmt.rs", line: 657, col: 14 }, to_string: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/string.rs", line: 2997, col: 14 }, debug_struct_fields: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/fmt/mod.rs", line: 2572, col: 9 }, exact_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/exact_size.rs", line: 122, col: 9 }, join_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/str.rs", line: 177, col: 10 }, int_sum: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/accum.rs", line: 206, col: 1 }, int_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 554, col: 1 }, char_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 119, col: 45 }, hash_capacity: { file: "/cargo/registry/25cdd57fae9f0462/hashbrown-0.17.1/src/raw.rs", line: 36, col: 40 }, btree_range_equal_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 110, col: 21 }, btree_range_equal_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 112, col: 21 }, btree_range_order_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 119, col: 21 }, btree_range_order_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 121, col: 21 }, deque_insert: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2612, col: 9 }, deque_swap_i: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1062, col: 9 }, deque_swap_j: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1063, col: 9 }, deque_rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3198, col: 9 }, deque_rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3243, col: 9 }, deque_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2724, col: 9 }, list_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/linked_list.rs", line: 1015, col: 9 }, mutex_lock: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sys/sync/mutex/no_threads.rs", line: 28, col: 9 }, once_force: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sync/once.rs", line: 227, col: 20 }, reentrant_init: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/once.rs", line: 325, col: 66 }, lazy_poisoned: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/lazy.rs", line: 386, col: 5 }, ready_poll: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 23, col: 35 }, ready_into_inner: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 46, col: 16 }, duration_new: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 281, col: 18 }, duration_from_mins: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 540, col: 13 }, duration_from_hours: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 510, col: 13 }, duration_add: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1345, col: 31 }, duration_sub: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1365, col: 31 }, duration_mul: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1385, col: 31 }, zip_next_back: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/adapters/zip.rs", line: 213, col: 5 }, bytes_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs", line: 207, col: 5 }, bytes_does_not_fit: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs", line: 215, col: 5 }, bytes_slice: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 374, col: 28 }, bytes_slice_ref_start: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 426, col: 9 }, bytes_slice_ref_end: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 432, col: 9 }, bytes_split_off: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 481, col: 9 }, bytes_split_to: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 533, col: 9 }, bytes_buf_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 694, col: 9 }, bytes_mut_split_off: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 329, col: 9 }, bytes_mut_split_to: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 408, col: 9 }, bytes_mut_set_len: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 545, col: 9 }, bytes_mut_overflow: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 700, col: 21 }, bytes_mut_overflow_offset: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 720, col: 29 }, bytes_mut_buf_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 1246, col: 9 } }); + }), { capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/raw_vec/mod.rs", line: 29, col: 5 }, trusted_capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/spec_from_iter_nested.rs", line: 57, col: 18 }, swap_remove: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2178, col: 13 }, vec_set_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2138, col: 9 }, into_flattened: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 3667, col: 36 }, rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3902, col: 9 }, rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3948, col: 9 }, ord_violation: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/sort/shared/smallsort.rs", line: 854, col: 5 }, ord_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cmp.rs", line: 1166, col: 9 }, f32_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f32.rs", line: 1665, col: 9 }, f64_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f64.rs", line: 1643, col: 9 }, encode_utf8: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2507, col: 9 }, encode_utf16: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2601, col: 13 }, format: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/fmt.rs", line: 657, col: 14 }, to_string: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/string.rs", line: 2997, col: 14 }, debug_struct_fields: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/fmt/mod.rs", line: 2572, col: 9 }, exact_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/exact_size.rs", line: 122, col: 9 }, join_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/str.rs", line: 177, col: 10 }, int_sum: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/accum.rs", line: 206, col: 1 }, int_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 554, col: 1 }, char_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 119, col: 45 }, hash_capacity: { file: "/cargo/registry/25cdd57fae9f0462/hashbrown-0.17.1/src/raw.rs", line: 36, col: 40 }, btree_range_equal_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 110, col: 21 }, btree_range_equal_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 112, col: 21 }, btree_range_order_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 119, col: 21 }, btree_range_order_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 121, col: 21 }, deque_insert: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2612, col: 9 }, deque_swap_i: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1062, col: 9 }, deque_swap_j: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1063, col: 9 }, deque_rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3198, col: 9 }, deque_rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3243, col: 9 }, deque_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2724, col: 9 }, list_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/linked_list.rs", line: 1015, col: 9 }, mutex_lock: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sys/sync/mutex/no_threads.rs", line: 28, col: 9 }, once_force: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sync/once.rs", line: 227, col: 20 }, reentrant_init: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/once.rs", line: 325, col: 66 }, lazy_poisoned: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/lazy.rs", line: 386, col: 5 }, ready_poll: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 23, col: 35 }, ready_into_inner: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 46, col: 16 }, duration_new: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 281, col: 18 }, duration_from_mins: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 540, col: 13 }, duration_from_hours: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 510, col: 13 }, duration_add: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1345, col: 31 }, duration_sub: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1365, col: 31 }, duration_mul: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1385, col: 31 }, zip_next_back: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/adapters/zip.rs", line: 213, col: 5 }, bytes_advance: { file: "bytes/source/lib.rs", line: 207, col: 5 }, bytes_does_not_fit: { file: "bytes/source/lib.rs", line: 215, col: 5 }, bytes_slice: { file: "bytes/source/bytes.rs", line: 374, col: 28 }, bytes_slice_ref_start: { file: "bytes/source/bytes.rs", line: 426, col: 9 }, bytes_slice_ref_end: { file: "bytes/source/bytes.rs", line: 432, col: 9 }, bytes_split_off: { file: "bytes/source/bytes.rs", line: 481, col: 9 }, bytes_split_to: { file: "bytes/source/bytes.rs", line: 533, col: 9 }, bytes_buf_advance: { file: "bytes/source/bytes.rs", line: 694, col: 9 }, bytes_mut_split_off: { file: "bytes/source/bytes_mut.rs", line: 329, col: 9 }, bytes_mut_split_to: { file: "bytes/source/bytes_mut.rs", line: 408, col: 9 }, bytes_mut_set_len: { file: "bytes/source/bytes_mut.rs", line: 545, col: 9 }, bytes_mut_overflow: { file: "bytes/source/bytes_mut.rs", line: 700, col: 21 }, bytes_mut_overflow_offset: { file: "bytes/source/bytes_mut.rs", line: 720, col: 29 }, bytes_mut_buf_advance: { file: "bytes/source/bytes_mut.rs", line: 1246, col: 9 } }); rs.byteVecPush(out, n); const _13: rs.u8 = rs.u8Rem(kind, 5, { file: "fixtures/lang/panics/src/hooked.rs", line: 174, col: 11 }); if (_13 === 3 || _13 === 4) { @@ -661,7 +661,7 @@ export function stock(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void { rs.stringPushStr(note, line.v); }, { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/thread/local.rs", line: 721, col: 38 }); rs.panicFmt(["unread"], { file: "fixtures/lang/panics/src/hooked.rs", line: 466, col: 13 }); - }, { capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/raw_vec/mod.rs", line: 29, col: 5 }, trusted_capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/spec_from_iter_nested.rs", line: 57, col: 18 }, swap_remove: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2178, col: 13 }, vec_set_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2138, col: 9 }, into_flattened: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 3667, col: 36 }, rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3902, col: 9 }, rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3948, col: 9 }, ord_violation: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/sort/shared/smallsort.rs", line: 854, col: 5 }, ord_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cmp.rs", line: 1166, col: 9 }, f32_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f32.rs", line: 1665, col: 9 }, f64_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f64.rs", line: 1643, col: 9 }, encode_utf8: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2507, col: 9 }, encode_utf16: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2601, col: 13 }, format: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/fmt.rs", line: 657, col: 14 }, to_string: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/string.rs", line: 2997, col: 14 }, debug_struct_fields: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/fmt/mod.rs", line: 2572, col: 9 }, exact_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/exact_size.rs", line: 122, col: 9 }, join_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/str.rs", line: 177, col: 10 }, int_sum: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/accum.rs", line: 206, col: 1 }, int_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 554, col: 1 }, char_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 119, col: 45 }, hash_capacity: { file: "/cargo/registry/25cdd57fae9f0462/hashbrown-0.17.1/src/raw.rs", line: 36, col: 40 }, btree_range_equal_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 110, col: 21 }, btree_range_equal_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 112, col: 21 }, btree_range_order_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 119, col: 21 }, btree_range_order_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 121, col: 21 }, deque_insert: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2612, col: 9 }, deque_swap_i: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1062, col: 9 }, deque_swap_j: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1063, col: 9 }, deque_rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3198, col: 9 }, deque_rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3243, col: 9 }, deque_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2724, col: 9 }, list_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/linked_list.rs", line: 1015, col: 9 }, mutex_lock: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sys/sync/mutex/no_threads.rs", line: 28, col: 9 }, once_force: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sync/once.rs", line: 227, col: 20 }, reentrant_init: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/once.rs", line: 325, col: 66 }, lazy_poisoned: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/lazy.rs", line: 386, col: 5 }, ready_poll: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 23, col: 35 }, ready_into_inner: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 46, col: 16 }, duration_new: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 281, col: 18 }, duration_from_mins: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 540, col: 13 }, duration_from_hours: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 510, col: 13 }, duration_add: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1345, col: 31 }, duration_sub: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1365, col: 31 }, duration_mul: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1385, col: 31 }, zip_next_back: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/adapters/zip.rs", line: 213, col: 5 }, bytes_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs", line: 207, col: 5 }, bytes_does_not_fit: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/lib.rs", line: 215, col: 5 }, bytes_slice: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 374, col: 28 }, bytes_slice_ref_start: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 426, col: 9 }, bytes_slice_ref_end: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 432, col: 9 }, bytes_split_off: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 481, col: 9 }, bytes_split_to: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 533, col: 9 }, bytes_buf_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes.rs", line: 694, col: 9 }, bytes_mut_split_off: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 329, col: 9 }, bytes_mut_split_to: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 408, col: 9 }, bytes_mut_set_len: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 545, col: 9 }, bytes_mut_overflow: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 700, col: 21 }, bytes_mut_overflow_offset: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 720, col: 29 }, bytes_mut_buf_advance: { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/bytes_mut.rs", line: 1246, col: 9 } }); + }, { capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/raw_vec/mod.rs", line: 29, col: 5 }, trusted_capacity: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/spec_from_iter_nested.rs", line: 57, col: 18 }, swap_remove: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2178, col: 13 }, vec_set_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 2138, col: 9 }, into_flattened: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/vec/mod.rs", line: 3667, col: 36 }, rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3902, col: 9 }, rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/mod.rs", line: 3948, col: 9 }, ord_violation: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/slice/sort/shared/smallsort.rs", line: 854, col: 5 }, ord_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cmp.rs", line: 1166, col: 9 }, f32_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f32.rs", line: 1665, col: 9 }, f64_clamp: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/num/f64.rs", line: 1643, col: 9 }, encode_utf8: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2507, col: 9 }, encode_utf16: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/char/methods.rs", line: 2601, col: 13 }, format: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/fmt.rs", line: 657, col: 14 }, to_string: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/string.rs", line: 2997, col: 14 }, debug_struct_fields: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/fmt/mod.rs", line: 2572, col: 9 }, exact_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/exact_size.rs", line: 122, col: 9 }, join_len: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/str.rs", line: 177, col: 10 }, int_sum: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/traits/accum.rs", line: 206, col: 1 }, int_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 554, col: 1 }, char_forward: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/range.rs", line: 119, col: 45 }, hash_capacity: { file: "/cargo/registry/25cdd57fae9f0462/hashbrown-0.17.1/src/raw.rs", line: 36, col: 40 }, btree_range_equal_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 110, col: 21 }, btree_range_equal_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 112, col: 21 }, btree_range_order_BTreeSet: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 119, col: 21 }, btree_range_order_BTreeMap: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/btree/search.rs", line: 121, col: 21 }, deque_insert: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2612, col: 9 }, deque_swap_i: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1062, col: 9 }, deque_swap_j: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 1063, col: 9 }, deque_rotate_left: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3198, col: 9 }, deque_rotate_right: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 3243, col: 9 }, deque_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/vec_deque/mod.rs", line: 2724, col: 9 }, list_split_off: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/alloc/src/collections/linked_list.rs", line: 1015, col: 9 }, mutex_lock: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sys/sync/mutex/no_threads.rs", line: 28, col: 9 }, once_force: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/std/src/sync/once.rs", line: 227, col: 20 }, reentrant_init: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/once.rs", line: 325, col: 66 }, lazy_poisoned: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/cell/lazy.rs", line: 386, col: 5 }, ready_poll: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 23, col: 35 }, ready_into_inner: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/future/ready.rs", line: 46, col: 16 }, duration_new: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 281, col: 18 }, duration_from_mins: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 540, col: 13 }, duration_from_hours: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 510, col: 13 }, duration_add: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1345, col: 31 }, duration_sub: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1365, col: 31 }, duration_mul: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/time.rs", line: 1385, col: 31 }, zip_next_back: { file: "/rustc/7df81381d027710708209f36b98d11b1c88f8d2e/library/core/src/iter/adapters/zip.rs", line: 213, col: 5 }, bytes_advance: { file: "bytes/source/lib.rs", line: 207, col: 5 }, bytes_does_not_fit: { file: "bytes/source/lib.rs", line: 215, col: 5 }, bytes_slice: { file: "bytes/source/bytes.rs", line: 374, col: 28 }, bytes_slice_ref_start: { file: "bytes/source/bytes.rs", line: 426, col: 9 }, bytes_slice_ref_end: { file: "bytes/source/bytes.rs", line: 432, col: 9 }, bytes_split_off: { file: "bytes/source/bytes.rs", line: 481, col: 9 }, bytes_split_to: { file: "bytes/source/bytes.rs", line: 533, col: 9 }, bytes_buf_advance: { file: "bytes/source/bytes.rs", line: 694, col: 9 }, bytes_mut_split_off: { file: "bytes/source/bytes_mut.rs", line: 329, col: 9 }, bytes_mut_split_to: { file: "bytes/source/bytes_mut.rs", line: 408, col: 9 }, bytes_mut_set_len: { file: "bytes/source/bytes_mut.rs", line: 545, col: 9 }, bytes_mut_overflow: { file: "bytes/source/bytes_mut.rs", line: 700, col: 21 }, bytes_mut_overflow_offset: { file: "bytes/source/bytes_mut.rs", line: 720, col: 29 }, bytes_mut_buf_advance: { file: "bytes/source/bytes_mut.rs", line: 1246, col: 9 } }); const subset: rs.ByteSlice = rs.bytesSlice(whole, 0, 1); if (k === 0) { const _22: rs.ByteSlice = rs.bytesSlice(whole, rs.usizeAdd(1, rs.usizeRem(at, 2, { file: "fixtures/lang/panics/src/hooked.rs", line: 470, col: 34 }), { file: "fixtures/lang/panics/src/hooked.rs", line: 470, col: 30 })); diff --git a/ts/src/lower/caller.rs b/ts/src/lower/caller.rs index 17c1c6338..e3afb2d8f 100644 --- a/ts/src/lower/caller.rs +++ b/ts/src/lower/caller.rs @@ -9,14 +9,16 @@ //! A program that reaches `panic::set_hook` passes where each panic is, which its hook //! reads: each statement's own, the caller's location to each std function that takes it, //! and the call of the macro that defines one, as an integer's methods. A panic in std's -//! own code is where the toolchain's source has it, and one in `bytes`' where the -//! program's `bytes` has it, named as the session names that file, which `rs.setHook` is -//! given. One in tokio's is where its compiled code has it, passed at the call. +//! own code is where the toolchain's source has it, which `rs.setHook` is given, with one in +//! `bytes`' where its compiled code has it. One in tokio's is where its compiled code has +//! it, passed at the call. use std::sync::OnceLock; use rustc_attr_ir::LangItem; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::{Instance, InstanceKind, TyCtxt}; use rustc_span::{RemapPathScopeComponents, SourceFile, Span}; @@ -480,60 +482,25 @@ const CALLEES: &[(&str, &str, &str, &str)] = &[ ]; /// Where `bytes`' code panics, for each of its panics the runtime makes, by the key it -/// passes in place of a location: a file under the crate's `src/`, the texts before it in -/// turn, and the text the call that panics starts with. None of its public functions takes -/// its caller's location, so each panic is where its source has it. -const BYTES: &[(&str, &str, &[&str], &str)] = &[ - ("bytes_advance", "lib.rs", &["fn panic_advance("], "panic!("), - ("bytes_does_not_fit", "lib.rs", &["fn panic_does_not_fit("], "panic!("), +/// passes in place of a location: the function, by its path, and which of its calls that +/// panic it is, in the order of its source, from 0. None of its public functions takes its +/// caller's location, so each panic is where its compiled code has it. +const BYTES: &[(&str, &str, usize)] = &[ + ("bytes_advance", "bytes::panic_advance", 0), + ("bytes_does_not_fit", "bytes::panic_does_not_fit", 0), // `range` takes its caller's location, so its panics are at its call. - ("bytes_slice", "bytes.rs", &["pub fn slice(&self"], "crate::range("), - ("bytes_slice_ref_start", "bytes.rs", &["pub fn slice_ref("], "assert!("), - ( - "bytes_slice_ref_end", - "bytes.rs", - &["pub fn slice_ref(", "assert!("], - "assert!(", - ), - ("bytes_split_off", "bytes.rs", &["pub fn split_off("], "assert!("), - ("bytes_split_to", "bytes.rs", &["pub fn split_to("], "assert!("), - ( - "bytes_buf_advance", - "bytes.rs", - &["impl Buf for Bytes", "fn advance("], - "assert!(", - ), - ( - "bytes_mut_split_off", - "bytes_mut.rs", - &["pub fn split_off("], - "assert!(", - ), - ("bytes_mut_split_to", "bytes_mut.rs", &["pub fn split_to("], "assert!("), - ( - "bytes_mut_set_len", - "bytes_mut.rs", - &["pub unsafe fn set_len("], - "debug_assert!(", - ), - ( - "bytes_mut_overflow", - "bytes_mut.rs", - &["fn reserve_inner(", "len.checked_add(additional)"], - "panic!(", - ), - ( - "bytes_mut_overflow_offset", - "bytes_mut.rs", - &["fn reserve_inner(", "new_cap.checked_add(offset)"], - "panic!(", - ), - ( - "bytes_mut_buf_advance", - "bytes_mut.rs", - &["impl Buf for BytesMut", "fn advance("], - "assert!(", - ), + ("bytes_slice", "bytes::Bytes::slice", 0), + ("bytes_slice_ref_start", "bytes::Bytes::slice_ref", 0), + ("bytes_slice_ref_end", "bytes::Bytes::slice_ref", 1), + ("bytes_split_off", "bytes::Bytes::split_off", 0), + ("bytes_split_to", "bytes::Bytes::split_to", 0), + ("bytes_buf_advance", "::advance", 0), + ("bytes_mut_split_off", "bytes::BytesMut::split_off", 0), + ("bytes_mut_split_to", "bytes::BytesMut::split_to", 0), + ("bytes_mut_set_len", "bytes::BytesMut::set_len", 0), + ("bytes_mut_overflow", "bytes::BytesMut::reserve_inner", 0), + ("bytes_mut_overflow_offset", "bytes::BytesMut::reserve_inner", 1), + ("bytes_mut_buf_advance", "::advance", 0), ]; /// The `rs.Sites` of the toolchain's std, which `rs.setHook` takes: where each panic @@ -566,18 +533,26 @@ fn bytes_sites(t: &mut Translator<'_, '_>, span: Span) -> Vec<(Key, Expr)> { if tcx.crate_name(krate).as_str() != "bytes" { continue; } - tcx.import_source_files(krate); + let mut fns = rustc_data_structures::fx::FxHashMap::default(); + let count = rustc_metadata::creader::CStore::from_tcx(tcx).num_def_ids_untracked(krate); + for index in 0..count { + let def_id = DefId { + krate, + index: rustc_hir::def_id::DefIndex::from_usize(index), + }; + if matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { + fns.insert(rustc_middle::ty::print::with_no_trimmed_paths!(tcx.def_path_str(def_id)), def_id); + } + } let mut props = Vec::new(); - for &(key, file, after, needle) in BYTES { - match bytes_find(tcx, krate, file, after, needle) { - Some(at) => props.push((Key::Name(key.into()), at)), + for &(key, path, nth) in BYTES { + let found = fns.get(path).and_then(|&f| panics(tcx, f, true).into_iter().nth(nth)); + match found { + Some(at) => props.push((Key::Name(key.into()), location(tcx, at))), None => { - let after = after.join(" then "); t.err( span, - format!( - "the bytes crate's source has no `{needle}` in `src/{file}` after `{after}`, where it panics" - ), + format!("the bytes crate's compiled code has no panic {nth} in `{path}`, counting from 0"), ); } } @@ -595,29 +570,38 @@ fn bytes_sites(t: &mut Translator<'_, '_>, span: Span) -> Vec<(Key, Expr)> { tables.pop().unwrap_or_default() } -/// The `rs.Location` of the first `needle` after each of `after` in turn in `file`, under -/// `krate`'s `src/`, named as the session names it. -fn bytes_find( - tcx: TyCtxt<'_>, - krate: rustc_span::def_id::CrateNum, - file: &str, - after: &[&str], - needle: &str, -) -> Option { - let map = tcx.sess.source_map(); - let suffix = format!("/src/{file}"); - let source = (map.files().iter()) - .find(|f| f.cnum == krate && self::file(tcx, f).ends_with(&suffix)) - .cloned()?; - if !map.ensure_source_file_source_present(&source) { - return None; +/// The calls in the MIR `def_id`'s crate compiled for it that panic, outside what that +/// inlined, in the order of its source, by the macro call each came from: of std's `panic!`, +/// which `assert!` makes too, and if `tracked`, of a function of its own crate that takes its +/// caller's location, whose panics are at the call. None without that MIR. +fn panics(tcx: TyCtxt<'_>, def_id: DefId, tracked: bool) -> Vec { + if !tcx.is_mir_available(def_id) { + return Vec::new(); } - let text = match &source.src { - Some(src) => src.to_string(), - None => source.external_src.read().get_source()?.to_string(), - }; - let (line, col) = position(&text, after, needle)?; - Some(object(self::file(tcx, &source), line, col)) + let mir = tcx.optimized_mir(def_id); + let mut spans: Vec = (mir.basic_blocks.iter()) + .filter_map(|block| { + let terminator = block.terminator(); + let TerminatorKind::Call { func, .. } = &terminator.kind else { + return None; + }; + let (callee, _) = func.const_fn_def()?; + let panics = [LangItem::PanicFmt, LangItem::Panic, LangItem::PanicDisplay] + .into_iter() + .any(|item| tcx.is_lang_item(callee, item)) + || (tracked + && callee.krate == def_id.krate + && tcx.codegen_fn_attrs(callee).flags.contains(CodegenFnAttrFlags::TRACK_CALLER)); + let own = (terminator.source_info.scope) + .inlined_instance(&mir.source_scopes) + .is_none(); + let span = terminator.source_info.span; + (panics && own).then(|| span.ctxt().outer_expn().expansion_cause().unwrap_or(span)) + }) + .collect(); + spans.sort_by_key(|span| span.lo()); + spans.dedup(); + spans } /// Where `def_id`, a dependency's function that takes no caller's location, makes its one @@ -625,26 +609,10 @@ fn bytes_find( /// named as the native build names it under any `--remap-path-prefix`. `None` without that /// MIR, or with other than one such call. pub fn own_panic(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - if !tcx.is_mir_available(def_id) { - return None; + match panics(tcx, def_id, false)[..] { + [span] => Some(location(tcx, span)), + _ => None, } - let mir = tcx.optimized_mir(def_id); - let mut spans = mir.basic_blocks.iter().filter_map(|block| { - let terminator = block.terminator(); - let TerminatorKind::Call { func, .. } = &terminator.kind else { - return None; - }; - let (callee, _) = func.const_fn_def()?; - let panics = [LangItem::PanicFmt, LangItem::Panic, LangItem::PanicDisplay] - .into_iter() - .any(|item| tcx.is_lang_item(callee, item)); - let own = (terminator.source_info.scope) - .inlined_instance(&mir.source_scopes) - .is_none(); - (panics && own).then_some(terminator.source_info.span) - }); - let span = spans.next()?; - spans.next().is_none().then(|| location(tcx, span)) } /// Where std's `def_id` panics, if [`CALLEES`] names it. diff --git a/ts/tests/fixtures.rs b/ts/tests/fixtures.rs index 0f3465a7b..3912ce838 100644 --- a/ts/tests/fixtures.rs +++ b/ts/tests/fixtures.rs @@ -61,8 +61,8 @@ fn driver_at(manifest: &Path, config: &Path, args: &[&std::ffi::OsStr], build: & /// The flags that name cargo's home `/cargo` in both builds, so the files of the registry's /// crates, which a hook's locations name, are named alike on any host, and tokio's `sync` -/// module `tokio/channels`, a remap of a directory within a crate, which the location of -/// tokio's panic must follow as native does. +/// module `tokio/channels` and bytes' `src` `bytes/source`, remaps of a directory within a +/// crate, which the locations of their panics must follow as native does. fn remap() -> String { static FLAGS: std::sync::OnceLock = std::sync::OnceLock::new(); FLAGS.get_or_init(remap_flags).clone() @@ -84,16 +84,25 @@ fn remap_flags() -> String { .join(".cargo"), }; let lock = std::fs::read_to_string(manifest_dir().join("Cargo.lock")).unwrap(); - let (_, tokio) = lock - .split_once("name = \"tokio\"\nversion = \"") - .expect("tokio in the lockfile"); - let tokio = format!("tokio-{}", &tokio[..tokio.find('"').unwrap()]); + let version = |name: &str| { + let (_, rest) = lock + .split_once(&format!("name = \"{name}\"\nversion = \"")) + .unwrap_or_else(|| panic!("{name} in the lockfile")); + format!("{name}-{}", &rest[..rest.find('"').unwrap()]) + }; + let dirs = [ + (version("tokio") + "/src/sync", "tokio/channels"), + (version("bytes") + "/src", "bytes/source"), + ]; // The later flag wins where both match. let mut flags = format!("--remap-path-prefix={}=/cargo", home.display()); for index in std::fs::read_dir(home.join("registry/src")).into_iter().flatten() { - let sync = index.unwrap().path().join(&tokio).join("src/sync"); - if sync.is_dir() { - flags += &format!(" --remap-path-prefix={}=tokio/channels", sync.display()); + let index = index.unwrap().path(); + for (dir, to) in &dirs { + let dir = index.join(dir); + if dir.is_dir() { + flags += &format!(" --remap-path-prefix={}={to}", dir.display()); + } } } flags From 8ed30ad1465dbe192cc42bd979969c5c9185d978 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:21:18 -0800 Subject: [PATCH 03/10] feat(ts)!: refuse Bytes::slice_ref of bytes the body doesn't show borrowed from that Bytes Co-Authored-By: Claude Opus 5.5 --- ts/README.md | 5 +- ts/fixtures/reject/expected.txt | 3 + ts/fixtures/reject/src/buffers.rs | 11 ++++ ts/fixtures/reject/ts.toml | 1 + ts/fixtures/stock/expected/buf.Fixed.ts | 2 +- ts/fixtures/stock/expected/buf.Spare.ts | 2 +- ts/fixtures/stock/expected/buf.ts | 51 ++++++++-------- .../stock/expected/bytes/buf/buf_mut.ts | 48 +++++++-------- .../bytes/buf/uninit_slice.UninitSlice.ts | 2 +- ts/fixtures/stock/src/buf.rs | 7 ++- ts/runtime/rs/bytes.ts | 8 +-- ts/src/lower/mir/call.rs | 9 +++ ts/src/lower/mir/origin.rs | 58 +++++++++++++++++-- 13 files changed, 142 insertions(+), 65 deletions(-) diff --git a/ts/README.md b/ts/README.md index ca986b487..36826f2be 100644 --- a/ts/README.md +++ b/ts/README.md @@ -765,8 +765,9 @@ of them: `rs/bytes.ts`, panicking as `bytes` does, and so do `split_to`, `split_off`, `truncate`, `clear`, and `slice_ref`, whose panic names no addresses. A `Bytes` of a `String` takes storage of its capacity, as `bytes` does. `slice_ref` of - bytes from other storage throws, since Rust may share what JS copied, as a `&'static - str`'s bytes that `as_bytes` encodes anew. `Bytes::from_owner` + bytes the body doesn't show borrowed from that `Bytes`, through `Deref`, `as_ref`, or a + slice of one, is refused, since Rust may share what JS copied, as a `&'static str`'s + bytes that `as_bytes` encodes anew. `Bytes::from_owner` reads its owner's bytes once, as `bytes` does, and an owner that drops is kept in the `rs.Shared` they count, which drops it with the last `Bytes` sharing them; one of an owner is never unique, so taking it back copies. diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index ff32bc79b..5b8b942d3 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -74,6 +74,9 @@ fixtures/reject/src/alloc.rs:wrapped_cells+12:3: a whole write of `std::mem::Man fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:zeroed+1:13: unsupported call to `std::alloc::alloc_zeroed` fixtures/reject/src/buffers.rs:blank_bytes+1:2: `Buf::copy_to_bytes` of `buffers::Blank`, a value JS copies, which is not supported +fixtures/reject/src/buffers.rs:sliced_elsewhere+5:4: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them +fixtures/reject/src/buffers.rs:sliced_elsewhere+5:47: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them +fixtures/reject/src/buffers.rs:sliced_elsewhere+5:76: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them fixtures/reject/src/clones.rs:greatest_slot+6:13: a raw pointer to `(u8, u8)` in `([clones::Greatest; 2], (u8, u8))`, which the crate may put another object in place of fixtures/reject/src/clones.rs:stashed_slots+5:19: a raw pointer to `(u8, u8)` in `([u8; 2], (u8, u8))`, which the crate may put another object in place of fixtures/reject/src/clones.rs:tagged_slots!+2:23: a raw pointer to `(u8, u8)` in `([clones::Cloned; 2], (u8, u8))`, which the crate may put another object in place of diff --git a/ts/fixtures/reject/src/buffers.rs b/ts/fixtures/reject/src/buffers.rs index 77360c755..4a405d218 100644 --- a/ts/fixtures/reject/src/buffers.rs +++ b/ts/fixtures/reject/src/buffers.rs @@ -23,3 +23,14 @@ impl bytes::Buf for Blank { pub fn blank_bytes(len: usize) -> usize { bytes::Buf::copy_to_bytes(&mut Blank::Empty, len).len() } + +/// `slice_ref` of bytes the body doesn't show borrowed from that `Bytes`: a `&'static str`'s, +/// which Rust shares and JS encodes anew, of another `Bytes` of them, and of a clone, which +/// shares the storage in both. +pub fn sliced_elsewhere(n: usize) -> usize { + let text = "shared"; + let a = bytes::Bytes::from_static(text.as_bytes()); + let b = bytes::Bytes::from_static(text.as_bytes()); + let c = a.clone(); + a.slice_ref(&text.as_bytes()[..n]).len() + a.slice_ref(&b[..n]).len() + a.slice_ref(c.as_ref()).len() +} diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index ec178a04e..1c99773dd 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -29,6 +29,7 @@ roots = [ "alloc::zeroed", "bound_ranges", "buffers::blank_bytes", + "buffers::sliced_elsewhere", "clones::cloned_slots", "clones::dropped_slots", "clones::ranked_slots", diff --git a/ts/fixtures/stock/expected/buf.Fixed.ts b/ts/fixtures/stock/expected/buf.Fixed.ts index 5ecbd401f..03460e912 100644 --- a/ts/fixtures/stock/expected/buf.Fixed.ts +++ b/ts/fixtures/stock/expected/buf.Fixed.ts @@ -177,7 +177,7 @@ export function putSlice$bytes_BufMut(self: Fixed, src: rs.ByteSlice): void { while (!(src.length === 0)) { const dst: rs.ByteSlice = chunkMut$bytes_BufMut(self); const cnt: rs.usize = rs.min(src.length, UninitSlice.len(dst)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(dst, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 258, col: 16 }), rs.byteSliceRange(src, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(dst, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 258, col: 16 }), rs.byteSliceRange(src, 0, cnt)); src = rs.byteSliceRange(src, cnt); advanceMut$bytes_BufMut(self, cnt); } diff --git a/ts/fixtures/stock/expected/buf.Spare.ts b/ts/fixtures/stock/expected/buf.Spare.ts index 710f56295..609d64975 100644 --- a/ts/fixtures/stock/expected/buf.Spare.ts +++ b/ts/fixtures/stock/expected/buf.Spare.ts @@ -186,7 +186,7 @@ export function putSlice$bytes_BufMut(self: Spare, src: rs.ByteSlice): void { while (!(src.length === 0)) { const dst: rs.ByteSlice = chunkMut$bytes_BufMut(self); const cnt: rs.usize = rs.min(src.length, UninitSlice.len(dst)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(dst, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 258, col: 16 }), rs.byteSliceRange(src, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(dst, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 258, col: 16 }), rs.byteSliceRange(src, 0, cnt)); src = rs.byteSliceRange(src, cnt); advanceMut$bytes_BufMut(self, cnt); } diff --git a/ts/fixtures/stock/expected/buf.ts b/ts/fixtures/stock/expected/buf.ts index 0b4efb470..1efcea11e 100644 --- a/ts/fixtures/stock/expected/buf.ts +++ b/ts/fixtures/stock/expected/buf.ts @@ -4795,22 +4795,22 @@ export function owners(dataData: Uint8Array, dataStart: number, dataLen: number, handles.items.push(part); noted = new rs.ByteVec(); } else if (_74 === 2) { - const at12 = rs.idx(handles.items, i); - const part2: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at12], (value6) => handles.items[at12] = value6), at); + const at11 = rs.idx(handles.items, i); + const part2: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at11], (value6) => handles.items[at11] = value6), at); handles.items.push(part2); noted = new rs.ByteVec(); } else if (_74 === 3) { - const at11 = rs.idx(handles.items, i); - const part3: rs.ByteSlice = rs.bytesSplitOff(rs.place(() => handles.items[at11], (value5) => handles.items[at11] = value5), at); + const at10 = rs.idx(handles.items, i); + const part3: rs.ByteSlice = rs.bytesSplitOff(rs.place(() => handles.items[at10], (value5) => handles.items[at10] = value5), at); handles.items.push(part3); noted = new rs.ByteVec(); } else if (_74 === 4) { - const at10 = rs.idx(handles.items, i); - rs.bytesTruncate(rs.place(() => handles.items[at10], (value4) => handles.items[at10] = value4), at); + const at9 = rs.idx(handles.items, i); + rs.bytesTruncate(rs.place(() => handles.items[at9], (value4) => handles.items[at9] = value4), at); noted = new rs.ByteVec(); } else if (_74 === 5) { - const at9 = rs.idx(handles.items, i); - rs.bytesTruncate(rs.place(() => handles.items[at9], (value3) => handles.items[at9] = value3), 0); + const at8 = rs.idx(handles.items, i); + rs.bytesTruncate(rs.place(() => handles.items[at8], (value3) => handles.items[at8] = value3), 0); noted = new rs.ByteVec(); } else if (_74 === 6) { noted = rs.byteVecFrom(Uint8Array.of(rs.bytesIsUnique(handles.items[rs.idx(handles.items, i)]) ? 1 : 0)); @@ -4834,46 +4834,47 @@ export function owners(dataData: Uint8Array, dataStart: number, dataLen: number, rs.bytesDrop(rs.vecRemove(handles, i)); noted = new rs.ByteVec(); } else if (_74 === 11) { - const at7 = rs.idx(handles.items, i); - rs.bytesAdvance(rs.place(() => handles.items[at7], (value2) => handles.items[at7] = value2), at); + const at6 = rs.idx(handles.items, i); + rs.bytesAdvance(rs.place(() => handles.items[at6], (value2) => handles.items[at6] = value2), at); noted = new rs.ByteVec(); } else if (_74 === 12) { - const at6 = rs.idx(handles.items, i); - const part4: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at6], (value) => handles.items[at6] = value), at); + const at5 = rs.idx(handles.items, i); + const part4: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at5], (value) => handles.items[at5] = value), at); handles.items.push(part4); noted = new rs.ByteVec(); } else if (_74 === 13) { - const sub: rs.ByteSlice = rs.byteSliceRange(handles.items[rs.idx(handles.items, i)], at); - const part5: rs.ByteSlice = rs.bytesSliceRef(handles.items[rs.idx(handles.items, i)], sub); + const at4 = rs.idx(handles.items, i); + const _142: rs.ByteSlice = rs.byteSliceRange(handles.items[at4], at); + const part5: rs.ByteSlice = rs.bytesSliceRef(handles.items[at4], _142); handles.items.push(part5); noted = new rs.ByteVec(); } else if (_74 === 14) { const buf: rs.BytesMut = rs.bytesMutWithCapacity(0); - const _152: rs.ByteSlice = rs.vecRemove(handles, i); - rs.bytesMutPutStorage(buf, _152); + const _150: rs.ByteSlice = rs.vecRemove(handles, i); + rs.bytesMutPutStorage(buf, _150); noted = rs.byteSliceToByteVec(rs.bytesMutView(buf)); buf[Symbol.dispose](); } else { - const _156: rs.ByteSlice = rs.bytesNew(); + const _154: rs.ByteSlice = rs.bytesNew(); const at3 = rs.idx(handles.items, i); rs.bytesDrop(handles.items[at3]); - handles.items[at3] = _156; + handles.items[at3] = _154; noted = new rs.ByteVec(); } const log2: rs.Mutex = rs.mutexLock(log.value); rs.byteVecPush(log2.value, op); rs.byteVecPush(log2.value, shared.strong & 0xff); - const _175: rs.ByteSlice = rs.byteVecSlice(noted); - rs.byteVecExtendFromSlice(log2.value, _175); + const _173: rs.ByteSlice = rs.byteVecSlice(noted); + rs.byteVecExtendFromSlice(log2.value, _173); rs.mutexUnlock(log2); } - for (const _180 of handles.items) { - rs.byteVecExtendFromSlice(out, rs.stringAsBytes(rs.fmtFormat([(f: rs.Formatter) => rs.bytesDebug(_180, f)]).v)); + for (const _178 of handles.items) { + rs.byteVecExtendFromSlice(out, rs.stringAsBytes(rs.fmtFormat([(f: rs.Formatter) => rs.bytesDebug(_178, f)]).v)); } rs.dropVec(handles, rs.bytesDrop); - const _202: rs.Mutex = rs.mutexLock(log.value); - rs.byteVecExtendFromSlice(out, rs.byteVecSlice(_202.value)); - rs.mutexUnlock(_202); + const _200: rs.Mutex = rs.mutexLock(log.value); + rs.byteVecExtendFromSlice(out, rs.byteVecSlice(_200.value)); + rs.mutexUnlock(_200); rs.byteVecPush(out, shared.strong & 0xff); rs.byteVecPush(out, log.strong & 0xff); shared[Symbol.dispose](); diff --git a/ts/fixtures/stock/expected/bytes/buf/buf_mut.ts b/ts/fixtures/stock/expected/bytes/buf/buf_mut.ts index fefbb1ce1..4af45e578 100644 --- a/ts/fixtures/stock/expected/bytes/buf/buf_mut.ts +++ b/ts/fixtures/stock/expected/bytes/buf/buf_mut.ts @@ -132,7 +132,7 @@ export function put$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8_buf_Pieces(sel const s: rs.ByteSlice = Pieces.chunk$bytes_Buf(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self, cnt); Pieces.advance$bytes_Buf(src, cnt); } @@ -169,7 +169,7 @@ export function put$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8_buf_Provided(s const s: rs.ByteSlice = Provided.chunk$bytes_Buf(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self, cnt); Provided.advance$bytes_Buf(src, cnt); } @@ -206,7 +206,7 @@ export function put$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8_bytes_Bytes(se const s: rs.ByteSlice = src; const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self, cnt); rs.bytesAdvance(rs.place(() => src, (value) => src = value), cnt); } @@ -244,7 +244,7 @@ export function put$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8_bytes_BytesMut const s: rs.ByteSlice = rs.bytesMutView(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self, cnt); rs.bytesMutAdvance(src, cnt); } @@ -282,7 +282,7 @@ export function put$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8_bytes_buf_Chai const s: rs.ByteSlice = Chain.chunk$bytes_Buf_bytes_Bytes_ref_slice_u8(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self, cnt); Chain.advance$bytes_Buf_bytes_Bytes_ref_slice_u8(src, cnt); } @@ -320,7 +320,7 @@ export function put$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8_ref_slice_u8(s const s: rs.ByteSlice = src; const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_mem_MaybeUninit_u8(self, cnt); rs.byteSliceAdvance(rs.place(() => src, (value) => src = value), cnt); } @@ -357,7 +357,7 @@ export function put$bytes_BufMut_mut_mut_slice_u8_buf_Pieces(self: rs.Place src, (value) => src = value), cnt); } @@ -469,7 +469,7 @@ export function put$bytes_BufMut_mut_mut_slice_u8_bytes_BytesMut(self: rs.Place< const s: rs.ByteSlice = rs.bytesMutView(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_u8(self, cnt); rs.bytesMutAdvance(src, cnt); } @@ -507,7 +507,7 @@ export function put$bytes_BufMut_mut_mut_slice_u8_bytes_buf_Chain_bytes_Bytes_re const s: rs.ByteSlice = Chain.chunk$bytes_Buf_bytes_Bytes_ref_slice_u8(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_mut_slice_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_mut_slice_u8(self, cnt); Chain.advance$bytes_Buf_bytes_Bytes_ref_slice_u8(src, cnt); } @@ -545,7 +545,7 @@ export function put$bytes_BufMut_mut_mut_slice_u8_ref_slice_u8(self: rs.Place src, (value) => src = value), cnt); } @@ -582,7 +582,7 @@ export function put$bytes_BufMut_mut_slice_mem_MaybeUninit_u8_buf_Pieces(self: r const s: rs.ByteSlice = Pieces.chunk$bytes_Buf(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self, cnt); Pieces.advance$bytes_Buf(src, cnt); } @@ -619,7 +619,7 @@ export function put$bytes_BufMut_mut_slice_mem_MaybeUninit_u8_buf_Provided(self: const s: rs.ByteSlice = Provided.chunk$bytes_Buf(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self, cnt); Provided.advance$bytes_Buf(src, cnt); } @@ -656,7 +656,7 @@ export function put$bytes_BufMut_mut_slice_mem_MaybeUninit_u8_bytes_Bytes(self: const s: rs.ByteSlice = src; const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self, cnt); rs.bytesAdvance(rs.place(() => src, (value) => src = value), cnt); } @@ -694,7 +694,7 @@ export function put$bytes_BufMut_mut_slice_mem_MaybeUninit_u8_bytes_BytesMut(sel const s: rs.ByteSlice = rs.bytesMutView(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self, cnt); rs.bytesMutAdvance(src, cnt); } @@ -732,7 +732,7 @@ export function put$bytes_BufMut_mut_slice_mem_MaybeUninit_u8_bytes_buf_Chain_by const s: rs.ByteSlice = Chain.chunk$bytes_Buf_bytes_Bytes_ref_slice_u8(src); const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self, cnt); Chain.advance$bytes_Buf_bytes_Bytes_ref_slice_u8(src, cnt); } @@ -770,7 +770,7 @@ export function put$bytes_BufMut_mut_slice_mem_MaybeUninit_u8_ref_slice_u8(self: const s: rs.ByteSlice = src; const d: rs.ByteSlice = chunkMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self); const cnt: rs.usize = rs.min(s.length, UninitSlice.len(d)); - UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); + UninitSlice.copyFromSlice(UninitSlice.indexMut$ops_IndexMut_ops_RangeTo_usize(d, { end: cnt }, { file: "bytes/source/buf/buf_mut.rs", line: 218, col: 14 }), rs.byteSliceRange(s, 0, cnt)); advanceMut$bytes_BufMut_mut_slice_mem_MaybeUninit_u8(self, cnt); rs.byteSliceAdvance(rs.place(() => src, (value) => src = value), cnt); } @@ -807,7 +807,7 @@ export function put$bytes_BufMut_mut_slice_u8_buf_Pieces(self: rs.Place src, (value) => src = value), cnt); } @@ -919,7 +919,7 @@ export function put$bytes_BufMut_mut_slice_u8_bytes_BytesMut(self: rs.Place src, (value) => src = value), cnt); } diff --git a/ts/fixtures/stock/expected/bytes/buf/uninit_slice.UninitSlice.ts b/ts/fixtures/stock/expected/bytes/buf/uninit_slice.UninitSlice.ts index 161a41f91..851b8e73b 100644 --- a/ts/fixtures/stock/expected/bytes/buf/uninit_slice.UninitSlice.ts +++ b/ts/fixtures/stock/expected/bytes/buf/uninit_slice.UninitSlice.ts @@ -110,7 +110,7 @@ export function writeByte(self: rs.ByteSlice, index: rs.usize, byte: rs.u8): voi if (!(index < len(self))) { rs.panic("assertion failed: index < self.len()"); } else { - asMutPtr(indexMut$ops_IndexMut_ops_RangeFrom_usize(self, new rs.RangeFrom(index, rs.usizeStep), { file: "/cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bytes-1.12.1/src/buf/uninit_slice.rs", line: 110, col: 22 })).v = byte; + asMutPtr(indexMut$ops_IndexMut_ops_RangeFrom_usize(self, new rs.RangeFrom(index, rs.usizeStep), { file: "bytes/source/buf/uninit_slice.rs", line: 110, col: 22 })).v = byte; } } diff --git a/ts/fixtures/stock/src/buf.rs b/ts/fixtures/stock/src/buf.rs index c1b165e24..133579d25 100644 --- a/ts/fixtures/stock/src/buf.rs +++ b/ts/fixtures/stock/src/buf.rs @@ -972,7 +972,8 @@ pub fn edits(data: &[u8], out: &mut Vec) { Bytes::new() } 4 => { - let sub = &bytes[a.min(rest.len())..]; + let all: &[u8] = bytes.as_ref(); + let sub = &all[a.min(rest.len())..]; bytes.slice_ref(sub) } 5 => { @@ -1105,8 +1106,8 @@ pub fn owners(data: &[u8], out: &mut Vec) { vec![] } 13 => { - let sub = &handles[i][at..]; - let part = handles[i].slice_ref(sub); + let handle = &handles[i]; + let part = handle.slice_ref(&handle[at..]); handles.push(part); vec![] } diff --git a/ts/runtime/rs/bytes.ts b/ts/runtime/rs/bytes.ts index e3930dc8e..ec66683ae 100644 --- a/ts/runtime/rs/bytes.ts +++ b/ts/runtime/rs/bytes.ts @@ -250,14 +250,14 @@ export function bytesTruncate(p: Place, len: usize): void { /** * `Bytes::slice_ref`: the bytes of `b` that `subset` is, by where they are in memory. Its - * panic names no addresses, which JS has none of. Bytes of other storage throw instead: - * Rust may share what JS copied, such as a `&'static str`'s bytes, which `as_bytes` - * encodes anew each time. + * panic names no addresses, which JS has none of. Translation refuses a `subset` the body + * doesn't show borrowed from `b`, as Rust may share what JS copied, such as a `&'static + * str`'s bytes, which `as_bytes` encodes anew each time. */ export function bytesSliceRef(b: Bytes, subset: ByteSlice): Bytes { if (subset.length === 0) return bytesNew(); if (subset.data.buffer !== b.data.buffer) { - throw new Error("rust2-ts: `Bytes::slice_ref` of bytes from other storage, which Rust may share where JS copied"); + throw new Error("rust2-ts: `Bytes::slice_ref` of bytes from other storage, which translation refuses"); } const pos = subset.data.byteOffset + subset.start; const from = b.data.byteOffset + b.start; diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 2923d0db0..089e30d0e 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -1203,6 +1203,15 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { self.slots[local] = Slot::Alias(Some(place)); Some(None) } + // Bytes from other storage, which JS may keep apart where Rust shares them, as a + // `&'static str`'s bytes that `as_bytes` encodes anew. + ("Bytes", "slice_ref") if bytes && !self.bytes_of(&args[1].node, &args[0].node) => done( + self.err( + span, + "`Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them", + ), + true, + ), // A `Bytes`'s slice by a range's bounds, read where the range is made. Its bounds // check panics, so it runs even if the slice goes unused. ("Bytes", "slice") diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs index ef3826c59..02b3a3ae8 100644 --- a/ts/src/lower/mir/origin.rs +++ b/ts/src/lower/mir/origin.rs @@ -492,15 +492,23 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { return None; } let owner = self.tcx.item_name(adt.did()).to_string(); - let arc = &args.first()?.node; - let place = match self.traced(arc) { + Some((self.referent(&args.first()?.node)?, owner)) + } + + /// The place the shared reference `r` borrows, if the body shows it and that it holds the + /// same value while the borrow lives: one a borrow here took, or what a reference it is + /// given or assigns once points at. + fn referent(&self, r: &Operand<'tcx>) -> Option> { + let place = match self.traced(r) { + Some(r) if self.def_at[r].is_none() => return Some(self.tcx.mk_place_deref(mir::Place::from(r))), Some(r) => match self.def_rvalue(r)? { + Rvalue::Use(op, _) => return self.referent(op), Rvalue::Ref(_, _, place) => *place, _ => return None, }, // A reference the function is given and never assigns. None => { - let r = arc.place()?.as_local()?; + let r = r.place()?.as_local()?; if !self.is_arg(r) || self.defs[r] != 0 { return None; } @@ -528,7 +536,49 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { } _ => false, }); - stays.then_some((place, owner)) + stays.then_some(place) + } + + /// Whether the shared slice `subset` is of the bytes of the `Bytes` the shared reference + /// `bytes` is to, as the body shows: through its `Deref` or `as_ref`, or a slice of one. + /// Only then does JS keep both in one buffer where Rust does. + pub(super) fn bytes_of(&self, subset: &Operand<'tcx>, bytes: &Operand<'tcx>) -> bool { + let Some(local) = self.traced(subset) else { + return false; + }; + if self.def_at[local].is_some() { + return match self.def_rvalue(local) { + Some(Rvalue::Use(op, _)) => self.bytes_of(op, bytes), + // `&*s`, of the shared slice `s`. + Some(Rvalue::Ref(_, mir::BorrowKind::Shared, place)) + if let [ProjectionElem::Deref] = place.projection.as_slice() => + { + self.bytes_of(&Operand::Copy(mir::Place::from(place.local)), bytes) + } + _ => false, + }; + } + let Some((def_id, gargs, args)) = self.def_call(local) else { + return false; + }; + let Some(of) = args.first().map(|a| &a.node) else { + return false; + }; + let Some(self_ty) = gargs.types().next() else { + return false; + }; + match library::key(self.tcx, def_id) { + Some((owner, name)) => match (owner.as_str(), name.as_str()) { + ("Index", "index") if self_ty.is_slice() => self.bytes_of(of, bytes), + ("Deref", "deref") | ("AsRef", "as_ref") + if matches!(self_ty.kind(), ty::Adt(adt, _) if Std::of(self.tcx, adt.did()) == Some(Std::Bytes)) => + { + self.referent(of).is_some_and(|place| self.referent(bytes) == Some(place)) + } + _ => false, + }, + None => false, + } } /// `Arc::as_ptr` of the `Arc` or `Rc` whose value of a type JS copies the shared From f899bbcb2a83e3c0d69b5151dc671a994a569d21 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:24:49 -0800 Subject: [PATCH 04/10] feat(ts)!: refuse alloc and realloc whose byte pointer the body passes on, and realloc to another type Co-Authored-By: Claude Opus 5.5 --- ts/README.md | 2 +- ts/fixtures/reject/expected.txt | 5 + ts/fixtures/reject/src/alloc.rs | 28 +++++ ts/fixtures/reject/ts.toml | 2 + ts/runtime/rs/alloc.ts | 11 +- ts/src/lower/mir/alloc.rs | 180 ++++++++++++++++++++++++++++++-- ts/src/lower/mir/call.rs | 1 + 7 files changed, 217 insertions(+), 12 deletions(-) diff --git a/ts/README.md b/ts/README.md index 36826f2be..d15af07e0 100644 --- a/ts/README.md +++ b/ts/README.md @@ -123,7 +123,7 @@ crate translation reaches is refused, and so are two versions of one crate it re | `Ordering`, `DefaultHasher`, `RandomState`, `BuildHasherDefault` | `rs.Ordering`, which is `-1`, `0`, or `1`; `rs.Hasher`; `rs.RandomState`, the keys of the `DefaultHasher` it builds, never changed once made; `undefined`, as `PhantomData` is, whose `build_hasher` is `H::default()` | | `Duration` | `rs.Duration`: `{ secs, nanos }`, std's whole seconds as an `rs.u64` and the nanoseconds past them, never changed once made | | `TypeId` | `rs.TypeId`: rustc's 128-bit hash of the type, which std's holds at run time, as 32 hex digits, equal by `===`; ordered as std's derive orders the hash's pointer-sized words, lowest first, hashed as its high 64 bits, and written as `TypeId(0x…)`, as std's are | -| `Layout`, `alloc::alloc` | `rs.Layout`: `{ size, align }`, never changed once made, checked as std's constructors check it; `alloc` gives an `rs.Block` of its bytes, which a cast of its pointer to `*mut T` the body shows came from `alloc` or `realloc` makes a JS array of `T`s, values JS copies, as an `rs.AtIndex` of its first; a cast back to `*mut u8` only as what `dealloc` or `realloc` takes, which free the array or copy it into a new one, and abort on memory `alloc` didn't give, as Rust leaves that undefined; its bytes, read, written, or cast to any other type, are refused, and fail at run time through a pointer from elsewhere | +| `Layout`, `alloc::alloc` | `rs.Layout`: `{ size, align }`, never changed once made, checked as std's constructors check it; `alloc` gives an `rs.Block` of its bytes, which a cast of its pointer to `*mut T` the body shows came from `alloc` or `realloc` makes a JS array of `T`s, values JS copies, as an `rs.AtIndex` of its first; a cast back to `*mut u8` only as what `dealloc` or `realloc` takes, which free the array or copy it into a new one, and abort on memory `alloc` didn't give, as Rust leaves that undefined; its bytes, read, written, or cast to any other type, are refused, as are a call of `alloc` or `realloc` whose pointer the body passes on as bytes elsewhere, such as out of the body, and a `realloc` whose result it casts to a type other than the one the memory it grows was cast from, so no pointer to its bytes leaves the body that made it | | closure | an arrow function, with `[Symbol.dispose]` if it owns a value that drops, and what makes its clone if a clone can't be itself | | `fn` pointer, function item as a value | the function, or std's an arrow that calls it as a written call does, as a closure is typed: a `()` result is `undefined`, where a declared function returns `void` | | `&dyn Trait`, `&mut dyn Trait`, `Box`, `Arc`, `Rc`, `Weak`, `Pin

` of one | an object with the trait's methods, its supertraits' included: `shape.area$Shape()`; behind an `Arc` or an `Rc`, an `rs.ArcDyn` sharing the count of the `Arc` it was made from | diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 5b8b942d3..942cc7d37 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -3,6 +3,9 @@ no item `clones::slice_of_object` in the crate no item `lopsided` in the crate no item `missing` in the crate fixtures/reject/src/alloc.rs:addressed+1:17: a pointer's address, which JS has no counterpart of +fixtures/reject/src/alloc.rs:alloc_leaked+4:11: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as +fixtures/reject/src/alloc.rs:alloc_leaked+6:8: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as +fixtures/reject/src/alloc.rs:alloc_leaked+8:3: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as fixtures/reject/src/alloc.rs:alloc_retyped+4:7: memory from `alloc` cast to pointers to `u32` and to `i32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:alloc_retyped+5:11: memory from `alloc` cast to pointers to `i32` and to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:alloc_retyped+7:17: memory from `alloc` cast to pointers to `i32` and to `u32`, which reads the bytes of one type as another @@ -16,6 +19,7 @@ fixtures/reject/src/alloc.rs:beside_reference+2:11: a read or write reaching pas fixtures/reject/src/alloc.rs:beside_reference+2:32: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:beside_reference+2:44: a slice of a length the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, which JS keeps as the values a cast of its pointer types it as +fixtures/reject/src/alloc.rs:bytes_as_values+3:19: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another @@ -52,6 +56,7 @@ fixtures/reject/src/alloc.rs:past_reference+1:30: a pointer reaching past a valu fixtures/reject/src/alloc.rs:peeked_moved+3:26: a read moving out a `std::iter::Peekable>>`, a library object whose inline state only its runtime class knows, which no copy splits from the original as Rust's read does fixtures/reject/src/alloc.rs:pinned_lent+1:32: `UnsafeCell::get_mut` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address fixtures/reject/src/alloc.rs:pinned_lent+2:43: `UnsafeCell::as_mut_unchecked` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address +fixtures/reject/src/alloc.rs:realloc_retyped+2:11: `realloc` of memory cast from a pointer to `u32` whose result is cast to one to `i32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:recast_reference+4:11: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:recast_reference+4:48: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:recast_reference+4:76: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index 0daaa6a71..521b682a3 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -339,3 +339,31 @@ pub fn alloc_retyped() -> i32 { pub fn zeroed() -> u32 { unsafe { *(std::alloc::alloc_zeroed(std::alloc::Layout::new::()) as *mut u32) } } + +/// Memory from `alloc` whose byte pointer leaves the body untyped: returned, kept in a +/// field, or given to a function of the crate's own, which JS can't read as bytes. +pub fn alloc_leaked(n: u8) -> *mut u8 { + let layout = std::alloc::Layout::new::(); + unsafe { + let held = Held { + bytes: std::alloc::alloc(layout), + }; + fill(std::alloc::alloc(layout), n); + std::alloc::dealloc(held.bytes, layout); + std::alloc::alloc(layout) + } +} + +pub struct Held { + bytes: *mut u8, +} + +unsafe fn fill(p: *mut u8, n: u8) { + unsafe { *p = n }; +} + +/// `realloc` of memory a pointer to `u32`s points at, whose result is read as `i32`s. +pub fn realloc_retyped(words: *mut u32) -> i32 { + let layout = std::alloc::Layout::new::(); + unsafe { std::alloc::realloc(words as *mut u8, layout, 8).cast::().read() } +} diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index 1c99773dd..ede59d287 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -1,6 +1,7 @@ package = "reject-fixture" roots = [ "alloc::addressed", + "alloc::alloc_leaked", "alloc::alloc_retyped", "alloc::allocated", "alloc::arrays_boxed", @@ -20,6 +21,7 @@ roots = [ "alloc::peeked_moved", "alloc::pinned_lent", "alloc::recast_reference", + "alloc::realloc_retyped", "alloc::reinterpreted", "alloc::unfixed_reference", "alloc::uninit_array_lent", diff --git a/ts/runtime/rs/alloc.ts b/ts/runtime/rs/alloc.ts index 297fd904f..63d04bf84 100644 --- a/ts/runtime/rs/alloc.ts +++ b/ts/runtime/rs/alloc.ts @@ -1,9 +1,10 @@ // `std::alloc`, for rust2-ts output: a `Layout`, and memory from `alloc` that a cast of its // pointer to one to `T` makes a JS array of `T`s, as many as fit. `rs.ts` re-exports all of it. // -// JS keeps values, not bytes, so the memory is untyped until that cast, and a read or -// write of its bytes fails. `realloc` keeps the values in a new array, and `dealloc` lets -// the array go; a pointer into it after either reads what Rust leaves undefined. +// JS keeps values, not bytes, so the memory is untyped until that cast, and translation +// refuses a read or write of its bytes, and a pointer to them leaving the body that made +// it. `realloc` keeps the values in a new array, and `dealloc` lets the array go; a pointer +// into it after either reads what Rust leaves undefined. import { Abort, Err, type Fmt, type FmtResult, type Formatter, fmtDebugStructFields, fmtPad, fmtWriteFmt, Ok, type Place, type Result, violated } from "../rs.ts"; import { AtIndex } from "./collections.ts"; @@ -128,7 +129,7 @@ export class Block implements Place { } function bytes(): never { - throw new Error("rust2-ts: a read or write of the bytes of memory from `alloc`, which JS keeps as values"); + throw new Error("rust2-ts: a read or write of the bytes of memory from `alloc`, which translation refuses"); } /** The block each array a cast of one's pointer made is in. */ @@ -145,6 +146,7 @@ export function alloc(layout: Layout): Block { * it holds already, of values of that type. */ export function allocAs(p: Place, elem: number, type: string): Place { + // Translation casts only a pointer the body shows `alloc` or `realloc` returned. if (!(p instanceof Block)) { throw new Error("rust2-ts: a pointer cast from one to bytes to one to values, which isn't memory from `alloc`"); } @@ -155,6 +157,7 @@ export function allocAs(p: Place, elem: number, type: string): Place< p.type = type; BLOCKS.set(p.array, p); } + // Translation refuses casting memory from one `alloc` to two types, and from `realloc` to another than its argument's. if (p.type !== type) throw new Error(`rust2-ts: memory from \`alloc\` holding \`${p.type}\`s, as \`${type}\`s`); return new AtIndex(p.array as T[], 0); } diff --git a/ts/src/lower/mir/alloc.rs b/ts/src/lower/mir/alloc.rs index 59ceab692..d53fae1cd 100644 --- a/ts/src/lower/mir/alloc.rs +++ b/ts/src/lower/mir/alloc.rs @@ -2,9 +2,10 @@ //! `*mut u8` to `*mut T` the body shows came from `alloc` or `realloc` makes the memory a JS //! array of `T`s, and one back to `*mut u8` is the block, only as what `dealloc` or //! `realloc` takes. Any other cast between them reads the bytes of one type as another, -//! which stays refused. +//! which stays refused, and so does a call of `alloc` or `realloc` whose byte pointer the +//! body passes on elsewhere, so no pointer to a block's bytes leaves the body that made it. -use rustc_middle::mir::{self, Local, Operand, ProjectionElem, Rvalue, TerminatorKind}; +use rustc_middle::mir::{self, Local, Operand, ProjectionElem, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, Spanned}; @@ -17,6 +18,19 @@ fn byte(ty: Ty<'_>) -> bool { matches!(ty.kind(), ty::Uint(ty::UintTy::U8)) } +/// Whether std's `owner::name` gives the byte pointer its first argument holds on, as a +/// pointer, a `NonNull`, or the payload of an `Option`, a `Result`, or `?`'s `ControlFlow`. +fn step(owner: &str, name: &str) -> bool { + matches!( + (owner, name), + ("NonNull", "new" | "new_unchecked" | "as_ptr") + | ("ptr" | "NonNull", "cast" | "cast_const" | "cast_mut") + | ("Option", "ok_or" | "ok_or_else" | "unwrap" | "expect" | "unwrap_unchecked") + | ("Result", "unwrap" | "expect" | "unwrap_unchecked") + | ("Try", "branch") + ) +} + /// Why the bytes of memory from `alloc` are refused. pub(super) const BYTES: &str = "a byte of memory from `alloc`, which JS keeps as the values a cast of its pointer types it as"; @@ -168,11 +182,163 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { match (owner.as_str(), name.as_str()) { ("alloc", "alloc") => library::is_std(self.tcx, def_id).then_some(local), ("alloc", "realloc") if library::is_std(self.tcx, def_id) => self.alloc_root(&args[0].node).or(Some(local)), - ("NonNull", "new" | "new_unchecked" | "as_ptr") - | ("ptr" | "NonNull", "cast" | "cast_const" | "cast_mut") - | ("Option", "ok_or" | "ok_or_else" | "unwrap" | "expect" | "unwrap_unchecked") - | ("Result", "unwrap" | "expect" | "unwrap_unchecked") - | ("Try", "branch") => self.alloc_root(&args[0].node), + (owner, name) if step(owner, name) => self.alloc_root(&args[0].node), + _ => None, + } + } + + /// Why the call of std's `def_id`, if `alloc` or `realloc`, can't be: the body passes the + /// byte pointer it returns into `dest` on elsewhere than a cast to a pointer to values JS + /// copies, `dealloc`, `realloc`, or the steps [`step`] names, such as into a field, out + /// of the body, or to any other function; or, of `realloc`, casts it to a pointer to a type + /// other than the one the memory it grows was cast from. A byte of the memory read or + /// written here is refused where it is. + pub(super) fn alloc_leak( + &self, + def_id: rustc_hir::def_id::DefId, + args: &[Spanned>], + dest: mir::Place<'tcx>, + ) -> Option { + if !library::is_std(self.tcx, def_id) { + return None; + } + let (owner, name) = library::key(self.tcx, def_id)?; + if owner != "alloc" || !matches!(name.as_str(), "alloc" | "realloc") { + return None; + } + let Some(types) = dest.as_local().and_then(|local| self.alloc_uses(local)) else { + return Some(format!( + "`{name}` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as" + )); + }; + // Memory from an `alloc` in the body, whose casts `alloc_cast` checks against each other. + if name != "realloc" || self.alloc_root(&args.first()?.node).is_some() { + return None; + } + let from = self.freed_type(&args.first()?.node)?; + let to = types.into_iter().find(|t| *t != from)?; + Some(format!( + "`realloc` of memory cast from a pointer to `{from}` whose result is cast to one to `{to}`, which reads the bytes of one type as another" + )) + } + + /// The types the casts in the body of the byte pointer in `root` type it as, if the body + /// passes it on no further than [`Self::alloc_leak`] allows. + fn alloc_uses(&self, root: Local) -> Option>> { + let (decls, tcx) = (&self.mir.local_decls, self.tcx); + // Whether a value of `ty` may hold the pointer: one to bytes, or what holds one. + let holds = |ty: Ty<'tcx>| { + ty.walk().any(|arg| { + arg.as_type().is_some_and(|t| match t.kind() { + ty::RawPtr(pointee, _) => byte(*pointee), + ty::Adt(adt, args) => { + library::Std::of(tcx, adt.did()) == Some(library::Std::NonNull) && byte(args.type_at(0)) + } + _ => false, + }) + }) + }; + // Whether `op` reads what `local` holds, or a payload of it, but not through it. + let reads = |op: &Operand<'tcx>, local: Local| { + op.place().is_some_and(|p| p.local == local && !p.projection.contains(&ProjectionElem::Deref)) + }; + let mut types = Vec::new(); + let mut seen = vec![root]; + let mut todo = vec![root]; + while let Some(local) = todo.pop() { + // Out of the body, as what it returns or into what it was given. + if local == RETURN_PLACE || self.is_arg(local) { + return None; + } + let mut carry = |to: mir::Place<'tcx>| -> Option<()> { + if !holds(to.ty(decls, tcx).ty) { + return Some(()); + } + let to = to.as_local()?; + if !seen.contains(&to) { + seen.push(to); + todo.push(to); + } + Some(()) + }; + for data in self.mir.basic_blocks.iter() { + for stmt in &data.statements { + let StatementKind::Assign(assign) = &stmt.kind else { + continue; + }; + let (dest, rvalue) = &**assign; + match rvalue { + Rvalue::Cast(mir::CastKind::PtrToPtr, op, to) if reads(op, local) => { + match to.builtin_deref(true) { + Some(pointee) if !byte(pointee) => types.push(self.alloc_key(pointee)), + _ => carry(*dest)?, + } + } + Rvalue::Use(op, _) | Rvalue::Cast(_, op, _) if reads(op, local) => carry(*dest)?, + Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) + if place.local == local && !place.projection.contains(&ProjectionElem::Deref) => + { + return None; + } + Rvalue::Aggregate(_, ops) if ops.iter().any(|op| reads(op, local)) => return None, + _ => {} + } + } + let TerminatorKind::Call { + func, + args, + destination, + .. + } = &data.terminator().kind + else { + continue; + }; + let Some(at) = args.iter().position(|a| reads(&a.node, local)) else { + continue; + }; + let ty::FnDef(def_id, gargs) = *func.ty(decls, tcx).kind() else { + return None; + }; + let (owner, name) = library::key(tcx, def_id).filter(|_| library::is_std(tcx, def_id))?; + match (owner.as_str(), name.as_str()) { + ("alloc", "dealloc" | "realloc") if at == 0 => {} + ("ptr" | "NonNull", "is_null") => {} + ("ptr" | "NonNull", "cast") => match gargs.skip_binder().types().nth(1) { + Some(to) if !byte(to) => types.push(self.alloc_key(to)), + _ => carry(*destination)?, + }, + (owner, name) if at == 0 && step(owner, name) => carry(*destination)?, + _ => return None, + } + } + } + Some(types) + } + + /// The type of the values the pointer `op`, which `dealloc` or `realloc` takes, was cast + /// from in the body, as `alloc_key` names it. + fn freed_type(&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, _) => self.freed_type(op), + Rvalue::Cast(mir::CastKind::PtrToPtr, op, _) => { + let from = op.ty(&self.mir.local_decls, self.tcx).builtin_deref(true)?; + (!byte(from)).then(|| self.alloc_key(from)) + } + _ => None, + }; + } + let (def_id, gargs, args) = self.def_call(local)?; + match library::key(self.tcx, def_id)? { + (owner, name) if matches!(owner.as_str(), "ptr" | "NonNull") && name == "cast" => { + let from = gargs.types().next()?; + match byte(from) { + true => self.freed_type(&args.first()?.node), + false => Some(self.alloc_key(from)), + } + } + (owner, name) if owner == "NonNull" && name == "as_ptr" => self.freed_type(&args.first()?.node), _ => None, } } diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 089e30d0e..1e6c6c6c0 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -107,6 +107,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { 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, _) if let Some(why) = self.alloc_leak(def_id, args, dest) => (self.err(span, why), true), ty::FnDef(def_id, gargs) if let Some(cast) = self.alloc_cast_call(def_id, gargs.skip_binder(), args, span, &mut pre) => { From ec3607e0a827707a39642926771ca970d2314cd4 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:27:06 -0800 Subject: [PATCH 05/10] fix(ts): compare a copy of an as_chunks run where the run is, as ptr::eq does Co-Authored-By: Claude Opus 5.5 --- ts/README.md | 3 +- .../lang/pointers/expected/pointers.ts | 31 ++++++++++++++++++ ts/fixtures/lang/pointers/src/pointers.rs | 11 +++++++ ts/runtime/rs.ts | 32 +++++++++++++++++-- ts/src/lower/array_slice.rs | 6 ++-- 5 files changed, 76 insertions(+), 7 deletions(-) diff --git a/ts/README.md b/ts/README.md index d15af07e0..af36b3543 100644 --- a/ts/README.md +++ b/ts/README.md @@ -193,7 +193,8 @@ where the arrays are, whether each is a JS array of its own, as a `Vec<[T; N]>` split it. A caller passes one as `rs.arraySliceFrom(arrays, rs.sliceFrom)`, `rs.byteSliceFrom` for bytes and `rs.arraySliceOf(rs.sliceFrom)` for arrays of arrays, and reads one as `rs.arraySliceItems(slice)`: the arrays themselves, or copies of the ones a split of one -slice holds, which have no array of their own. std's methods that lend arrays or parts of +slice holds, which have no array of their own, each remembered as its run, so `ptr::eq` +compares a pointer to one where the run is. std's methods that lend arrays or parts of the slice, such as `iter_mut`, `split_at_mut`, `chunks_mut`, and `swap`, take them where they are; any other reads them as `rs.arraySliceItems` does, or for a `&mut` one, such as `sort` or `rotate_left`, reads copies of the arrays, which it writes back once it returns, diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index db0886202..efc4d3268 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -2986,6 +2986,37 @@ export function lent(n: rs.u8): rs.ByteVec { const nested: rs.u32[][] = [[w, 1], [2, 3]]; const _296: rs.u8 = rs.sliceSame(rs.optionUnwrap(rs.arraySliceLast(rs.arraySliceFrom(nested, rs.sliceFrom))), rs.optionUnwrap(rs.iterNth(rs.arraySliceIter(rs.arraySliceFrom(nested, rs.sliceFrom)), 1))) ? 1 : 0; rs.byteVecPush(o, _296); + const _312: [rs.ArraySlice>, rs.Slice] = rs.sliceAsChunks(rs.sliceFrom(Array.from({ length: 6 }, (): rs.u32 => { + return w; + })), 2); + const last: rs.Slice = rs.optionUnwrap(rs.arraySliceLast(_312[0])); + const _321: (a0: rs.Slice) => boolean = (run: rs.Slice): boolean => { + return !rs.sliceSame(run, last); + }; + const _319: rs.u8 = rs.slicePartitionPoint(rs.arraySliceAsSlice(_312[0]), (held2) => _321(rs.sliceFrom(held2))) & 0xff; + rs.byteVecPush(o, _319); + const bytes: Uint8Array = rs.bytes(6).fill(n); + const _326: rs.ByteSlice = rs.byteSliceFrom(bytes); + const _333: rs.ByteSlice = rs.byteSliceFrom(bytes); + const _324: [rs.ArraySlice, rs.ByteSlice] = rs.sliceAsChunks(rs.byteSliceRange(_326, 1), 2); + const _332: (a0: rs.ByteSlice) => boolean = (run2: rs.ByteSlice): boolean => { + return !rs.byteSliceSame(run2, rs.optionUnwrap(rs.byteSliceGetRange(rs.byteSliceRange(_333, 3), 0, 2))); + }; + const _330: rs.u8 = rs.slicePartitionPoint(rs.arraySliceAsSlice(_324[0]), (held3) => _332(rs.byteSliceFrom(held3))) & 0xff; + rs.byteVecPush(o, _330); + const _335: rs.u32[] = Array.from({ length: 2 }, (): rs.u32 => { + return w; + }); + const pairs2: rs.u32[][] = Array.from({ length: 4 }, (): rs.u32[] => { + return copy$array_u32(_335); + }); + const _345: rs.ArraySlice> = rs.arraySliceFrom(pairs2, rs.sliceFrom); + const _337: [rs.ArraySlice>>, rs.ArraySlice>] = rs.sliceAsChunks(rs.arraySliceFrom(pairs2, rs.sliceFrom), 2); + const _344: (a0: rs.ArraySlice>) => boolean = (run3: rs.ArraySlice>): boolean => { + return !rs.arraySliceSame(run3, rs.optionUnwrap(rs.arraySliceLastChunk(_345, 2))); + }; + const _342: rs.u8 = rs.slicePartitionPoint(rs.arraySliceAsSlice(_337[0]), (held4) => _344(rs.arraySliceOf(rs.sliceFrom)(held4))) & 0xff; + rs.byteVecPush(o, _342); un[Symbol.dispose](); held[Symbol.dispose](); rs.refRelease(_240); diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index a5b9b71a7..152e4fa76 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2407,6 +2407,17 @@ fn lent(n: u8) -> Vec { o.push(u8::from(ptr::eq(un.first_chunk::<2>().unwrap(), &*held))); let nested = [[w, 1], [2, 3]]; o.push(u8::from(ptr::eq(nested.last().unwrap(), nested.iter().nth(1).unwrap()))); + // Runs of equal elements a callback of std's tells apart only by where each is. + let same = [w; 6]; + let (runs, _) = same.as_chunks::<2>(); + let last = runs.last().unwrap(); + o.push(runs.partition_point(|run| !ptr::eq(run, last)) as u8); + let bytes = [n; 6]; + let (runs, _) = bytes[1..].as_chunks::<2>(); + o.push(runs.partition_point(|run| !ptr::eq(run, bytes[3..].first_chunk::<2>().unwrap())) as u8); + let pairs = [[w; 2]; 4]; + let (runs, _) = pairs.as_chunks::<2>(); + o.push(runs.partition_point(|run| !ptr::eq(run, pairs.last_chunk::<2>().unwrap())) as u8); o } diff --git a/ts/runtime/rs.ts b/ts/runtime/rs.ts index 4281a336f..87dc5430d 100644 --- a/ts/runtime/rs.ts +++ b/ts/runtime/rs.ts @@ -968,6 +968,8 @@ export function boxArrayFromRaw(s: Slice): T[] { */ export function sliceSame(a: Slice, b: Slice): boolean { if (!(a instanceof Slice && b instanceof Slice)) return a === b; + a = arrayRefWhere(a); + b = arrayRefWhere(b); return (roomItems(a.data) ?? a.data) === (roomItems(b.data) ?? b.data) && a.start === b.start; } @@ -1275,6 +1277,8 @@ export function arraySliceCopy(r: ArraySlice, copy?: Elem */ export function arraySliceSame(a: ArraySlice, b: ArraySlice): boolean { if (!(a instanceof ArraySlice && b instanceof ArraySlice)) return a === b; + a = arrayRefWhere(a); + b = arrayRefWhere(b); if (a.items !== undefined || b.items !== undefined) return a.items === b.items && a.start === b.start; return arrayRefSame(arrayRefPart(a.flat!, a.start * a.n, 0), arrayRefPart(b.flat!, b.start * b.n, 0)); } @@ -1289,10 +1293,11 @@ function arrayRefSame(a: ArrayRef, b: ArrayRef): boolean { /** * The arrays of `r` as a JS array of arrays, for what only reads them: the arrays themselves * where each array is one, so a callback's `ptr::eq` sees the array it was lent, or else copies - * of the arrays `as_chunks` split from one flat slice, which have no array of their own. + * of the arrays `as_chunks` split from one flat slice, which have no array of their own, as + * `arraySliceRuns` makes them. */ export function arraySliceItems(r: ArraySlice): LentArray[] { - if (r.items === undefined) return arraySliceCopy(r); + if (r.items === undefined) return arraySliceRuns(r); if (r.start === 0 && r.length === r.items.length) return r.items as LentArray[]; return r.items.slice(r.start, r.start + r.length) as LentArray[]; } @@ -1310,7 +1315,26 @@ export function arraySliceSplitLastChunk(r: ArraySlice, n /** The arrays of `r` as a slice of arrays, as `arraySliceItems` reads them: a slice of the arrays where each array is one. */ export function arraySliceAsSlice(r: ArraySlice): Slice> { if (r.items !== undefined) return new Slice(r.items as LentArray[], r.start, r.length); - return sliceFrom(arraySliceCopy(r)); + return sliceFrom(arraySliceRuns(r)); +} + +/** Each run `arraySliceRuns` copied, by the array the copy is. */ +const RUNS = /* @__PURE__ */ new WeakMap(); + +/** + * Copies of the arrays of `r`, runs `as_chunks` split from one flat slice, for what only + * reads them, each remembered as its run, so a pointer into a copy compares where the run is. + */ +function arraySliceRuns(r: ArraySlice): LentArray[] { + const out = arraySliceCopy(r); + for (let i = 0; i < out.length; i++) RUNS.set(out[i] as object, arraySliceAt(r, i)); + return out; +} + +/** Where `s`, the slice a pointer to a `[T; N]` is, points: into the run it copies if it is of a copy `arraySliceRuns` made. */ +function arrayRefWhere(s: S): S { + const run = RUNS.get((s instanceof ArraySlice ? s.items : s.data) ?? s) as S | undefined; + return run === undefined ? s : arrayRefPart(arrayRefWhere(run), s.start, s.length); } /** @@ -2203,6 +2227,8 @@ export function byteSliceSplitLastMut(s: ByteSlice): [Place, ByteSlice] */ export function byteSliceSame(a: ByteSlice, b: ByteSlice): boolean { if (!(a instanceof ByteSlice && b instanceof ByteSlice)) return a === b; + a = arrayRefWhere(a); + b = arrayRefWhere(b); return a.data.buffer === b.data.buffer && a.data.byteOffset + a.start === b.data.byteOffset + b.start; } diff --git a/ts/src/lower/array_slice.rs b/ts/src/lower/array_slice.rs index 205d33afd..f7af00439 100644 --- a/ts/src/lower/array_slice.rs +++ b/ts/src/lower/array_slice.rs @@ -3,9 +3,9 @@ //! `Vec<[T; N]>` holds them, or parts of one slice, as `as_chunks` splits it. The runtime's //! `arraySlice` functions map std's methods that lend arrays or parts. Any other method of a //! slice of arrays reads it as an `rs.Slice` of JS arrays: shared, of its arrays themselves, or -//! of copies of those `as_chunks` split from one slice, which have no array of their own; and -//! mutable, of copies that `rs.arraySliceBy` writes back once it returns, as Rust moves their -//! elements. +//! of copies of those `as_chunks` split from one slice, which have no array of their own and +//! compare where their runs are; and mutable, of copies that `rs.arraySliceBy` writes back +//! once it returns, as Rust moves their elements. use rustc_middle::ty::{self, Ty, TyCtxt}; From 1e248608c214d7371e60d954c41c89840d5f5911 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:27:41 -0800 Subject: [PATCH 06/10] quest: finish ts-runtime-throws Co-Authored-By: Claude Opus 5.5 --- quest/m3/README.md | 1 - quest/m3/ts-array-ref-conversions.md | 4 -- quest/m3/ts-runtime-throws.md | 71 ---------------------------- ts/README.md | 5 +- 4 files changed, 2 insertions(+), 79 deletions(-) delete mode 100644 quest/m3/ts-runtime-throws.md diff --git a/quest/m3/README.md b/quest/m3/README.md index a8bdd7e11..1af7c61a8 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -39,7 +39,6 @@ memory model for values whose address is reinterpreted, without a consumer. - [A Pin of a crate's own Deref type](/quest/m3/ts-pin-own-deref.md) - `Pin

` over a crate's own `Deref` derefs through its impl and type-checks - [Waiting: drop the lang fixture's mul_add allowance](/quest/m3/ts-fma-allowance.md) - the differential fails on any `mul_add` difference again once wasm32's `fma` rounds exactly - [Waiting: type-check in process with tsrs](/quest/m3/ts-tsrs.md) - rust2-ts checks bindings through tsrs instead of `tsc` once it is a stable library -- [Throw only where Rust panics](/quest/m3/ts-runtime-throws.md) - no runtime `rust2-ts:` error where native Rust goes on: exact, or refused at translation - [rust2-ts with no ts.toml](/quest/m3/ts-configless.md) - a crate's whole `pub` API translates with no config and no bound items - [Browser APIs through wasm-bindgen](/quest/m3/ts-wasm-bindgen/README.md) - web-sys and js-sys imports become direct JS calls, so idiomatic crates such as web-rs's web-codecs translate as stock crates - [`u64` wrapping arithmetic in V8](/quest/m3/ts-u64-wrapping.md) - `rs.u64WrappingAdd` and its kin cost near a plain `+` while the value is a safe integer diff --git a/quest/m3/ts-array-ref-conversions.md b/quest/m3/ts-array-ref-conversions.md index ee764bbe3..75fdee6b0 100644 --- a/quest/m3/ts-array-ref-conversions.md +++ b/quest/m3/ts-array-ref-conversions.md @@ -18,7 +18,3 @@ and lends parts of arrays where they are: its tests use `first_chunk_mut` in pla Decided with the maintainer on 2026-11-07: ✅ plan it, in m3 (from #662's follow-ups). Public API: the translator's output. - -## Related - -- [Throw only where Rust panics](/quest/m3/ts-runtime-throws.md) - its exact `ptr::eq` for `as_chunks` copies changes how an array taken from a flat slice is represented, on the same `rs.ArraySlice` diff --git a/quest/m3/ts-runtime-throws.md b/quest/m3/ts-runtime-throws.md deleted file mode 100644 index 05fb9d0f8..000000000 --- a/quest/m3/ts-runtime-throws.md +++ /dev/null @@ -1,71 +0,0 @@ -# [M] Throw only where Rust panics - -## Goal - -Code [rust2-ts](/ts/README.md) generates fails at run time only where the Rust source -does: no `rust2-ts:` error the runtime throws where native Rust would go on. Each such -construct translates exactly, or is refused at translation with a span. Proven by -differential tests against native Rust for every construct that now translates. - -## Plan - -Decided with the maintainer on 2026-11-04: "we should not panic when Rust would not -panic", and runtime throws that copy Rust's behaviour are fine. A throw that guards Rust -UB stays, as an abort worded like std's `unsafe precondition(s) violated` check. - -Decided with the maintainer on 2026-11-07, in -[moq-dev/rust2#661](https://github.com/moq-dev/rust2/pull/661), for the throws that fire -only because translation can't see where a pointer came from: - -- ✅ Refuse at translation, with a span, where the body doesn't show the pointer fits: - `rs.arcErase` (done in #661) and `alloc`'s bytes. -- ✅ Keep as loud run-time checks: `ptrUnerase`, `offset_from` across arrays, - `AtIndex.reach`, and raw slices of a field's byte, which AGENTS.md allows under m3's - [unsafe policy](/quest/m3/README.md). - Two pointers made from one reference, one through a branch or loop, may each be in - an array of one of its own; keep the loud check rather than trace joins (decided - 2026-11-07 in [#621](https://github.com/moq-dev/rust2/pull/621)). -- Rejected: refusing all of them at translation, which refuses pointer arithmetic on any - pointer parameter; keeping all as run-time checks. - -Decided with the maintainer on 2026-11-07, in the quest audit: ✅ `Bytes::slice_ref` of -bytes from other storage is refused at translation, since it is safe code and AGENTS.md -allows a run-time check only for an `unsafe` pointer. Rejected: keeping its run-time -check, as #661 had, and widening AGENTS.md's exception to safe code with no exact answer -while a `&str` stays a JS string. - -What remains: - -- `alloc`'s bytes: a byte of memory from `alloc` read or written, or the memory cast to a - type other than the one it holds, throws in `rs/alloc.ts` when the byte pointer reached - there from elsewhere. Refuse at the `alloc` or `realloc` call whose byte pointer the - body doesn't show going only to a typed cast, `dealloc`, `realloc`, or the - `NonNull`/`Option` steps [`alloc_root`](/ts/src/lower/mir/alloc.rs) already follows, so - no untyped pointer leaves the body; and a `realloc` of memory the body cast from one type - whose result it casts to another. This needs a use analysis of that pointer, which the - translator doesn't have yet. The throws in `rs/alloc.ts` are then translator guards. -- `ptr::eq` of an array `as_chunks` split from one flat slice, which std's callback gets a - copy of, answers `false` where Rust answers `true`, silently (moved here 2026-11-07 in - [#608](https://github.com/moq-dev/rust2/pull/608)). A copy that remembers the slice and - offset it came from, compared through that, is exact; it builds on the `rs.ArraySlice` - [#662](https://github.com/moq-dev/rust2/pull/662) left, where `rs.arraySliceItems` still - copies. -- `Bytes::slice_ref` of bytes from other storage throws in `bytesSliceRef` - (`ts/runtime/rs/bytes.ts`), since Rust may share what JS copied, such as a - `&'static str`'s bytes that `as_bytes` encodes anew. Refuse the call whose `subset` the - body doesn't show borrowed from that same `Bytes`, through `Deref`, `as_ref`, or a slice - of one; the throw is then a translator guard, and `ts/README.md` says so. -- `bytes`' panics in a hook program: `BYTES` in [`caller.rs`](/ts/src/lower/caller.rs) - finds each by searching the source of the program's `bytes` for text, so a - `--remap-path-prefix` of a directory inside `bytes` fails translation of a hook program. - Take each panic's location from `bytes`' compiled code for the function that panics, as - [`tokio.rs`](/ts/src/lower/tokio.rs) does for tokio's oneshot `poll`, so rustc names the - file as native does under any remap, and delete the source search (decided with the - maintainer on 2026-11-07, from [#661](https://github.com/moq-dev/rust2/pull/661)'s - follow-ups). - -Public API: the translator's output, and which crates translate. - -## Related - -- [Array references from slices and cells](/quest/m3/ts-array-ref-conversions.md) - also changes how an array taken from a flat slice is represented, on the same `rs.ArraySlice` diff --git a/ts/README.md b/ts/README.md index af36b3543..56dada118 100644 --- a/ts/README.md +++ b/ts/README.md @@ -214,9 +214,8 @@ with its payload if that is text, or `Box` as std's hook writes any oth A program that reaches `set_hook` has each panic carry the `Location` native code gives it, std's own at the `/rustc//library/...` path its build names, read from the toolchain's source, and runs its hook with a `PanicHookInfo` before throwing; a panic in -the hook throws `rs.Abort`, as std aborts. A panic of `bytes`' own code is where the -program's `bytes` has it, and one of tokio's where its compiled code has it, named as the -session names that file: by the host's cargo registry, unless the build remaps it, as +the hook throws `rs.Abort`, as std aborts. A panic of `bytes`' or tokio's own code is +where its compiled code has it, named as the session names that file: by the host's cargo registry, unless the build remaps it, as `--remap-path-prefix` does natively. The default hook writes nothing, as wasm32 has no stderr. A program without one passes no locations, and a bundler drops the hook's runtime, `rs/hook.ts`, from it; what stays is the location From af3213cd24d802d198bac14408934e091ed80d8d Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 08:36:23 -0800 Subject: [PATCH 07/10] fix(ts): accept slice_ref of a Bytes sharing storage through slice or clone, which slice_ref's panics need Co-Authored-By: Claude Opus 5.5 --- ts/README.md | 6 ++--- ts/fixtures/reject/census.txt | 7 +++++ ts/fixtures/reject/expected.txt | 5 ++-- ts/fixtures/reject/src/buffers.rs | 8 +++--- ts/fixtures/stock/expected/buf.ts | 28 +++++++++++--------- ts/fixtures/stock/src/buf.rs | 2 +- ts/runtime/rs/bytes.ts | 2 +- ts/src/census.rs | 6 +++++ ts/src/lower/mir/call.rs | 2 +- ts/src/lower/mir/origin.rs | 44 ++++++++++++++++++++++++++++--- 10 files changed, 79 insertions(+), 31 deletions(-) diff --git a/ts/README.md b/ts/README.md index 56dada118..481e0aa15 100644 --- a/ts/README.md +++ b/ts/README.md @@ -765,9 +765,9 @@ of them: `rs/bytes.ts`, panicking as `bytes` does, and so do `split_to`, `split_off`, `truncate`, `clear`, and `slice_ref`, whose panic names no addresses. A `Bytes` of a `String` takes storage of its capacity, as `bytes` does. `slice_ref` of - bytes the body doesn't show borrowed from that `Bytes`, through `Deref`, `as_ref`, or a - slice of one, is refused, since Rust may share what JS copied, as a `&'static str`'s - bytes that `as_bytes` encodes anew. `Bytes::from_owner` + bytes the body doesn't show borrowed, through `Deref`, `as_ref`, or a slice of one, from + that `Bytes` or one sharing its storage through `slice` or `clone`, is refused, since Rust + may share what JS copied, as a `&'static str`'s bytes that `as_bytes` encodes anew. `Bytes::from_owner` reads its owner's bytes once, as `bytes` does, and an owner that drops is kept in the `rs.Shared` they count, which drops it with the last `Bytes` sharing them; one of an owner is never unique, so taking it back copies. diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index f9a4ec104..87ed5d41e 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -1,4 +1,7 @@ fixtures/reject/src/alloc.rs:addressed+1:17: a pointer's address, which JS has no counterpart of +fixtures/reject/src/alloc.rs:alloc_leaked+4:11: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as +fixtures/reject/src/alloc.rs:alloc_leaked+6:8: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as +fixtures/reject/src/alloc.rs:alloc_leaked+8:3: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as fixtures/reject/src/alloc.rs:alloc_retyped+4:7: memory from `alloc` cast to pointers to `u32` and to `i32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:alloc_retyped+5:11: memory from `alloc` cast to pointers to `i32` and to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:alloc_retyped+7:17: memory from `alloc` cast to pointers to `i32` and to `u32`, which reads the bytes of one type as another @@ -12,6 +15,7 @@ fixtures/reject/src/alloc.rs:beside_reference+2:11: a read or write reaching pas fixtures/reject/src/alloc.rs:beside_reference+2:32: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:beside_reference+2:44: a slice of a length the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, which JS keeps as the values a cast of its pointer types it as +fixtures/reject/src/alloc.rs:bytes_as_values+3:19: `alloc` whose pointer the body passes on as bytes, other than to a cast to a pointer to values, `dealloc`, or `realloc`, which JS keeps as the values a cast types the memory as fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another @@ -48,6 +52,7 @@ fixtures/reject/src/alloc.rs:past_reference+1:30: a pointer reaching past a valu fixtures/reject/src/alloc.rs:peeked_moved+3:26: a read moving out a `std::iter::Peekable>>`, a library object whose inline state only its runtime class knows, which no copy splits from the original as Rust's read does fixtures/reject/src/alloc.rs:pinned_lent+1:32: `UnsafeCell::get_mut` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address fixtures/reject/src/alloc.rs:pinned_lent+2:43: `UnsafeCell::as_mut_unchecked` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address +fixtures/reject/src/alloc.rs:realloc_retyped+2:11: `realloc` of memory cast from a pointer to `u32` whose result is cast to one to `i32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:recast_reference+4:11: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:recast_reference+4:48: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:recast_reference+4:76: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust @@ -70,6 +75,8 @@ fixtures/reject/src/alloc.rs:wrapped_cells+12:3: a whole write of `std::mem::Man fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:zeroed+1:13: unsupported call to `std::alloc::alloc_zeroed` fixtures/reject/src/buffers.rs:blank_bytes+1:2: `Buf::copy_to_bytes` of `buffers::Blank`, a value JS copies, which is not supported +fixtures/reject/src/buffers.rs:sliced_elsewhere+4:4: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`'s storage, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them +fixtures/reject/src/buffers.rs:sliced_elsewhere+4:47: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`'s storage, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them fixtures/reject/src/clones.rs:given_slots+6:2: a raw pointer to `(u8, u8)` in `([clones::Touched; 2], (u8, u8))`, which the crate may put another object in place of fixtures/reject/src/clones.rs:greatest_slot+6:13: a raw pointer to `(u8, u8)` in `([clones::Greatest; 2], (u8, u8))`, which the crate may put another object in place of fixtures/reject/src/clones.rs:kept_slots+5:2: a raw pointer to `(u8, u8)` in `([clones::Cloned; 2], (u8, u8))`, which the crate may put another object in place of diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 942cc7d37..e2420b562 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -79,9 +79,8 @@ fixtures/reject/src/alloc.rs:wrapped_cells+12:3: a whole write of `std::mem::Man fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:zeroed+1:13: unsupported call to `std::alloc::alloc_zeroed` fixtures/reject/src/buffers.rs:blank_bytes+1:2: `Buf::copy_to_bytes` of `buffers::Blank`, a value JS copies, which is not supported -fixtures/reject/src/buffers.rs:sliced_elsewhere+5:4: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them -fixtures/reject/src/buffers.rs:sliced_elsewhere+5:47: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them -fixtures/reject/src/buffers.rs:sliced_elsewhere+5:76: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them +fixtures/reject/src/buffers.rs:sliced_elsewhere+4:4: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`'s storage, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them +fixtures/reject/src/buffers.rs:sliced_elsewhere+4:47: `Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`'s storage, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them fixtures/reject/src/clones.rs:greatest_slot+6:13: a raw pointer to `(u8, u8)` in `([clones::Greatest; 2], (u8, u8))`, which the crate may put another object in place of fixtures/reject/src/clones.rs:stashed_slots+5:19: a raw pointer to `(u8, u8)` in `([u8; 2], (u8, u8))`, which the crate may put another object in place of fixtures/reject/src/clones.rs:tagged_slots!+2:23: a raw pointer to `(u8, u8)` in `([clones::Cloned; 2], (u8, u8))`, which the crate may put another object in place of diff --git a/ts/fixtures/reject/src/buffers.rs b/ts/fixtures/reject/src/buffers.rs index 4a405d218..fff954350 100644 --- a/ts/fixtures/reject/src/buffers.rs +++ b/ts/fixtures/reject/src/buffers.rs @@ -24,13 +24,11 @@ pub fn blank_bytes(len: usize) -> usize { bytes::Buf::copy_to_bytes(&mut Blank::Empty, len).len() } -/// `slice_ref` of bytes the body doesn't show borrowed from that `Bytes`: a `&'static str`'s, -/// which Rust shares and JS encodes anew, of another `Bytes` of them, and of a clone, which -/// shares the storage in both. +/// `slice_ref` of bytes the body doesn't show of that `Bytes`'s storage: a `&'static str`'s, +/// which Rust shares and JS encodes anew, and of another `Bytes` of them. pub fn sliced_elsewhere(n: usize) -> usize { let text = "shared"; let a = bytes::Bytes::from_static(text.as_bytes()); let b = bytes::Bytes::from_static(text.as_bytes()); - let c = a.clone(); - a.slice_ref(&text.as_bytes()[..n]).len() + a.slice_ref(&b[..n]).len() + a.slice_ref(c.as_ref()).len() + a.slice_ref(&text.as_bytes()[..n]).len() + a.slice_ref(&b[..n]).len() } diff --git a/ts/fixtures/stock/expected/buf.ts b/ts/fixtures/stock/expected/buf.ts index 1efcea11e..6396881b4 100644 --- a/ts/fixtures/stock/expected/buf.ts +++ b/ts/fixtures/stock/expected/buf.ts @@ -4844,37 +4844,39 @@ export function owners(dataData: Uint8Array, dataStart: number, dataLen: number, noted = new rs.ByteVec(); } else if (_74 === 13) { const at4 = rs.idx(handles.items, i); - const _142: rs.ByteSlice = rs.byteSliceRange(handles.items[at4], at); - const part5: rs.ByteSlice = rs.bytesSliceRef(handles.items[at4], _142); + const _143: rs.ByteSlice = rs.bytesClone(handles.items[at4]); + const _144: rs.ByteSlice = rs.byteSliceRange(handles.items[at4], at); + const part5: rs.ByteSlice = rs.bytesSliceRef(_143, _144); + rs.bytesDrop(_143); handles.items.push(part5); noted = new rs.ByteVec(); } else if (_74 === 14) { const buf: rs.BytesMut = rs.bytesMutWithCapacity(0); - const _150: rs.ByteSlice = rs.vecRemove(handles, i); - rs.bytesMutPutStorage(buf, _150); + const _152: rs.ByteSlice = rs.vecRemove(handles, i); + rs.bytesMutPutStorage(buf, _152); noted = rs.byteSliceToByteVec(rs.bytesMutView(buf)); buf[Symbol.dispose](); } else { - const _154: rs.ByteSlice = rs.bytesNew(); + const _156: rs.ByteSlice = rs.bytesNew(); const at3 = rs.idx(handles.items, i); rs.bytesDrop(handles.items[at3]); - handles.items[at3] = _154; + handles.items[at3] = _156; noted = new rs.ByteVec(); } const log2: rs.Mutex = rs.mutexLock(log.value); rs.byteVecPush(log2.value, op); rs.byteVecPush(log2.value, shared.strong & 0xff); - const _173: rs.ByteSlice = rs.byteVecSlice(noted); - rs.byteVecExtendFromSlice(log2.value, _173); + const _175: rs.ByteSlice = rs.byteVecSlice(noted); + rs.byteVecExtendFromSlice(log2.value, _175); rs.mutexUnlock(log2); } - for (const _178 of handles.items) { - rs.byteVecExtendFromSlice(out, rs.stringAsBytes(rs.fmtFormat([(f: rs.Formatter) => rs.bytesDebug(_178, f)]).v)); + for (const _180 of handles.items) { + rs.byteVecExtendFromSlice(out, rs.stringAsBytes(rs.fmtFormat([(f: rs.Formatter) => rs.bytesDebug(_180, f)]).v)); } rs.dropVec(handles, rs.bytesDrop); - const _200: rs.Mutex = rs.mutexLock(log.value); - rs.byteVecExtendFromSlice(out, rs.byteVecSlice(_200.value)); - rs.mutexUnlock(_200); + const _202: rs.Mutex = rs.mutexLock(log.value); + rs.byteVecExtendFromSlice(out, rs.byteVecSlice(_202.value)); + rs.mutexUnlock(_202); rs.byteVecPush(out, shared.strong & 0xff); rs.byteVecPush(out, log.strong & 0xff); shared[Symbol.dispose](); diff --git a/ts/fixtures/stock/src/buf.rs b/ts/fixtures/stock/src/buf.rs index 133579d25..1068769e3 100644 --- a/ts/fixtures/stock/src/buf.rs +++ b/ts/fixtures/stock/src/buf.rs @@ -1107,7 +1107,7 @@ pub fn owners(data: &[u8], out: &mut Vec) { } 13 => { let handle = &handles[i]; - let part = handle.slice_ref(&handle[at..]); + let part = handle.clone().slice_ref(&handle[at..]); handles.push(part); vec![] } diff --git a/ts/runtime/rs/bytes.ts b/ts/runtime/rs/bytes.ts index ec66683ae..807382608 100644 --- a/ts/runtime/rs/bytes.ts +++ b/ts/runtime/rs/bytes.ts @@ -251,7 +251,7 @@ export function bytesTruncate(p: Place, len: usize): void { /** * `Bytes::slice_ref`: the bytes of `b` that `subset` is, by where they are in memory. Its * panic names no addresses, which JS has none of. Translation refuses a `subset` the body - * doesn't show borrowed from `b`, as Rust may share what JS copied, such as a `&'static + * doesn't show borrowed from `b`'s storage, as Rust may share what JS copied, such as a `&'static * str`'s bytes, which `as_bytes` encodes anew each time. */ export function bytesSliceRef(b: Bytes, subset: ByteSlice): Bytes { diff --git a/ts/src/census.rs b/ts/src/census.rs index 467cb25e3..cf2c003cb 100644 --- a/ts/src/census.rs +++ b/ts/src/census.rs @@ -147,10 +147,13 @@ const VERBATIM: &[&str] = &[ "Box::from_raw", "Buf::copy_to_bytes", "BufMut::put", + "Bytes", + "Bytes::slice_ref", "BytesMut", "Cell::swap", "Copy", "Debug", + "Deref", "Display", "Drop", "Extend", @@ -184,6 +187,7 @@ const VERBATIM: &[&str] = &[ "alloc", "array::from_ref", "as_chunks", + "as_ref", "async", "async fn", "become", @@ -194,6 +198,7 @@ const VERBATIM: &[&str] = &[ "clone_from", "collect", "copy_nonoverlapping", + "dealloc", "dyn Future", "experimental_generic_mono", "f64", @@ -213,6 +218,7 @@ const VERBATIM: &[&str] = &[ "next", "offset_from", "pub", + "realloc", "ref mut", "rs.None", "rs.OuterPending", diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 1e6c6c6c0..ddb9505b4 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -1209,7 +1209,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { ("Bytes", "slice_ref") if bytes && !self.bytes_of(&args[1].node, &args[0].node) => done( self.err( span, - "`Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them", + "`Bytes::slice_ref` of bytes the body doesn't show it borrowed from that `Bytes`'s storage, through `Deref`, `as_ref`, or a slice of one, which JS may keep apart where Rust shares them", ), true, ), diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs index 02b3a3ae8..aa05e876f 100644 --- a/ts/src/lower/mir/origin.rs +++ b/ts/src/lower/mir/origin.rs @@ -539,9 +539,44 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { stays.then_some(place) } - /// Whether the shared slice `subset` is of the bytes of the `Bytes` the shared reference - /// `bytes` is to, as the body shows: through its `Deref` or `as_ref`, or a slice of one. - /// Only then does JS keep both in one buffer where Rust does. + /// The place of the `Bytes` whose storage the `Bytes` at `place` shares, as the body + /// shows: through `slice` or `clone` of one, into a variable assigned once and never + /// changed in place, which keeps that storage. + fn storage(&self, place: mir::Place<'tcx>) -> mir::Place<'tcx> { + let Some(local) = place.as_local() else { + return place; + }; + if self.defs[local] != 1 || self.is_arg(local) || self.mutated[local] { + return place; + } + let Some(bb) = self.def_block(local) else { + return place; + }; + let TerminatorKind::Call { func, args, .. } = &self.mir.basic_blocks[bb].terminator().kind else { + return place; + }; + let Some((def_id, gargs)) = func.const_fn_def() else { + return place; + }; + let bytes = |ty: ty::Ty<'tcx>| matches!(ty.kind(), ty::Adt(adt, _) if Std::of(self.tcx, adt.did()) == Some(Std::Bytes)); + let shares = match library::key(self.tcx, def_id) { + Some((owner, name)) => match (owner.as_str(), name.as_str()) { + ("Bytes", "slice") => library::inherent_std(self.tcx, def_id) == Some(Std::Bytes), + ("Clone", "clone") => gargs.types().next().is_some_and(bytes), + _ => false, + }, + None => false, + }; + match args.first().filter(|_| shares).and_then(|a| self.referent(&a.node)) { + Some(from) => self.storage(from), + None => place, + } + } + + /// Whether the shared slice `subset` is of the storage of the `Bytes` the shared + /// reference `bytes` is to, as the body shows: through the `Deref` or `as_ref`, or a slice + /// of one, of that `Bytes` or one sharing its storage, as [`Self::storage`] has it. Only + /// then does JS keep both in one buffer where Rust does. pub(super) fn bytes_of(&self, subset: &Operand<'tcx>, bytes: &Operand<'tcx>) -> bool { let Some(local) = self.traced(subset) else { return false; @@ -573,7 +608,8 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { ("Deref", "deref") | ("AsRef", "as_ref") if matches!(self_ty.kind(), ty::Adt(adt, _) if Std::of(self.tcx, adt.did()) == Some(Std::Bytes)) => { - self.referent(of).is_some_and(|place| self.referent(bytes) == Some(place)) + let storage = |r| self.referent(r).map(|place| self.storage(place)); + storage(of).is_some_and(|place| storage(bytes) == Some(place)) } _ => false, }, From 4f57d1a703e00749557887c04d74802f08b11a13 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 09:22:24 -0800 Subject: [PATCH 08/10] chore(ts): sort the reject fixture's roots Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/reject/ts.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index ede59d287..3bd1cf244 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -20,8 +20,8 @@ roots = [ "alloc::past_reference", "alloc::peeked_moved", "alloc::pinned_lent", - "alloc::recast_reference", "alloc::realloc_retyped", + "alloc::recast_reference", "alloc::reinterpreted", "alloc::unfixed_reference", "alloc::uninit_array_lent", From fad4a478cb83a2ed0c99d4eb442c2e633c0b43fb Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 10:25:02 -0800 Subject: [PATCH 09/10] chore(ts): format Co-Authored-By: Claude Opus 5.5 --- ts/src/lower/caller.rs | 10 ++++++++-- ts/src/lower/mir/alloc.rs | 8 ++++++-- ts/src/lower/mir/origin.rs | 10 +++++----- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/ts/src/lower/caller.rs b/ts/src/lower/caller.rs index e3afb2d8f..07966a06e 100644 --- a/ts/src/lower/caller.rs +++ b/ts/src/lower/caller.rs @@ -541,7 +541,10 @@ fn bytes_sites(t: &mut Translator<'_, '_>, span: Span) -> Vec<(Key, Expr)> { index: rustc_hir::def_id::DefIndex::from_usize(index), }; if matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { - fns.insert(rustc_middle::ty::print::with_no_trimmed_paths!(tcx.def_path_str(def_id)), def_id); + fns.insert( + rustc_middle::ty::print::with_no_trimmed_paths!(tcx.def_path_str(def_id)), + def_id, + ); } } let mut props = Vec::new(); @@ -591,7 +594,10 @@ fn panics(tcx: TyCtxt<'_>, def_id: DefId, tracked: bool) -> Vec { .any(|item| tcx.is_lang_item(callee, item)) || (tracked && callee.krate == def_id.krate - && tcx.codegen_fn_attrs(callee).flags.contains(CodegenFnAttrFlags::TRACK_CALLER)); + && tcx + .codegen_fn_attrs(callee) + .flags + .contains(CodegenFnAttrFlags::TRACK_CALLER)); let own = (terminator.source_info.scope) .inlined_instance(&mir.source_scopes) .is_none(); diff --git a/ts/src/lower/mir/alloc.rs b/ts/src/lower/mir/alloc.rs index d53fae1cd..ad51163a4 100644 --- a/ts/src/lower/mir/alloc.rs +++ b/ts/src/lower/mir/alloc.rs @@ -25,7 +25,10 @@ fn step(owner: &str, name: &str) -> bool { (owner, name), ("NonNull", "new" | "new_unchecked" | "as_ptr") | ("ptr" | "NonNull", "cast" | "cast_const" | "cast_mut") - | ("Option", "ok_or" | "ok_or_else" | "unwrap" | "expect" | "unwrap_unchecked") + | ( + "Option", + "ok_or" | "ok_or_else" | "unwrap" | "expect" | "unwrap_unchecked" + ) | ("Result", "unwrap" | "expect" | "unwrap_unchecked") | ("Try", "branch") ) @@ -240,7 +243,8 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { }; // Whether `op` reads what `local` holds, or a payload of it, but not through it. let reads = |op: &Operand<'tcx>, local: Local| { - op.place().is_some_and(|p| p.local == local && !p.projection.contains(&ProjectionElem::Deref)) + op.place() + .is_some_and(|p| p.local == local && !p.projection.contains(&ProjectionElem::Deref)) }; let mut types = Vec::new(); let mut seen = vec![root]; diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs index aa05e876f..d663cf3f3 100644 --- a/ts/src/lower/mir/origin.rs +++ b/ts/src/lower/mir/origin.rs @@ -558,11 +558,13 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { let Some((def_id, gargs)) = func.const_fn_def() else { return place; }; - let bytes = |ty: ty::Ty<'tcx>| matches!(ty.kind(), ty::Adt(adt, _) if Std::of(self.tcx, adt.did()) == Some(Std::Bytes)); let shares = match library::key(self.tcx, def_id) { Some((owner, name)) => match (owner.as_str(), name.as_str()) { ("Bytes", "slice") => library::inherent_std(self.tcx, def_id) == Some(Std::Bytes), - ("Clone", "clone") => gargs.types().next().is_some_and(bytes), + ("Clone", "clone") => gargs + .types() + .next() + .is_some_and(|ty| self.b.t.std_of(ty) == Some(Std::Bytes)), _ => false, }, None => false, @@ -605,9 +607,7 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { match library::key(self.tcx, def_id) { Some((owner, name)) => match (owner.as_str(), name.as_str()) { ("Index", "index") if self_ty.is_slice() => self.bytes_of(of, bytes), - ("Deref", "deref") | ("AsRef", "as_ref") - if matches!(self_ty.kind(), ty::Adt(adt, _) if Std::of(self.tcx, adt.did()) == Some(Std::Bytes)) => - { + ("Deref", "deref") | ("AsRef", "as_ref") if self.b.t.std_of(self_ty) == Some(Std::Bytes) => { let storage = |r| self.referent(r).map(|place| self.storage(place)); storage(of).is_some_and(|place| storage(bytes) == Some(place)) } From 547215d06ef7f58ff9f4ef132c4d1c69447aca7f Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 9 Nov 2026 11:01:38 -0800 Subject: [PATCH 10/10] fix(ts): keep slice_ref of one indexed element, and a hook program without debug assertions Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/stock/expected/buf.ts | 55 +++++++++++++++---------------- ts/fixtures/stock/src/buf.rs | 6 ++-- ts/src/lower/caller.rs | 11 +++++-- ts/src/lower/mir/origin.rs | 41 ++++++++++++++++++++++- ts/tests/fixtures.rs | 24 ++++++++++++++ 5 files changed, 102 insertions(+), 35 deletions(-) diff --git a/ts/fixtures/stock/expected/buf.ts b/ts/fixtures/stock/expected/buf.ts index 6396881b4..89f29579c 100644 --- a/ts/fixtures/stock/expected/buf.ts +++ b/ts/fixtures/stock/expected/buf.ts @@ -4719,11 +4719,13 @@ export function edits(dataData: Uint8Array, dataStart: number, dataLen: number, part = rs.bytesNew(); } else if (_17 === 4) { const sub: rs.ByteSlice = rs.byteSliceRange(bytes, rs.min(a, dataLen - 2)); - part = rs.bytesSliceRef(bytes, sub); + const _32: rs.ByteSlice = rs.bytesClone(bytes); + part = rs.bytesSliceRef(_32, sub); + rs.bytesDrop(_32); } else if (_17 === 5) { rs.bytesAdvance(rs.place(() => bytes, (value3) => bytes = value3), a); - const _35: rs.usize = rs.usizeDiv(a, 2); - part = rs.bytesSplitTo(rs.place(() => bytes, (value4) => bytes = value4), _35); + const _37: rs.usize = rs.usizeDiv(a, 2); + part = rs.bytesSplitTo(rs.place(() => bytes, (value4) => bytes = value4), _37); } else if (_17 === 6) { let clone: rs.ByteSlice = rs.bytesClone(bytes); rs.bytesTruncate(rs.place(() => clone, (value2) => clone = value2), a); @@ -4733,15 +4735,15 @@ export function edits(dataData: Uint8Array, dataStart: number, dataLen: number, rs.bytesTruncate(rs.place(() => bytes, (value) => bytes = value), a); part = clone2; } else { - const _46: rs.Place = { v: rs.bytesClone(bytes) }; - rs.bytesDrop(rs.bytesStatic(_46.v)); - rs.bytesDrop(rs.bytesStatic(rs.byteSliceRange(_46.v, 0))); - part = rs.bytesStatic(_46.v); + const _48: rs.Place = { v: rs.bytesClone(bytes) }; + rs.bytesDrop(rs.bytesStatic(_48.v)); + rs.bytesDrop(rs.bytesStatic(rs.byteSliceRange(_48.v, 0))); + part = rs.bytesStatic(_48.v); } - const _65: boolean = rs.bytesIsUnique(part); - const _68: boolean = rs.bytesIsUnique(bytes); - const _70: rs.ByteSlice = part; - const args3 = [(f: rs.Formatter) => rs.bytesDebug(_70, f), (f: rs.Formatter) => rs.bytesDebug(bytes, f), (f: rs.Formatter) => rs.fmtBool(_65, f), (f: rs.Formatter) => rs.fmtBool(_68, f)]; + const _67: boolean = rs.bytesIsUnique(part); + const _70: boolean = rs.bytesIsUnique(bytes); + const _72: rs.ByteSlice = part; + const args3 = [(f: rs.Formatter) => rs.bytesDebug(_72, f), (f: rs.Formatter) => rs.bytesDebug(bytes, f), (f: rs.Formatter) => rs.fmtBool(_67, f), (f: rs.Formatter) => rs.fmtBool(_70, f)]; rs.byteVecExtendFromSlice(out, rs.stringAsBytes(rs.fmtFormat([args3[0], " ", args3[1], " ", args3[2], " ", args3[3]]).v)); rs.bytesDrop(part); rs.bytesDrop(bytes); @@ -4795,22 +4797,22 @@ export function owners(dataData: Uint8Array, dataStart: number, dataLen: number, handles.items.push(part); noted = new rs.ByteVec(); } else if (_74 === 2) { - const at11 = rs.idx(handles.items, i); - const part2: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at11], (value6) => handles.items[at11] = value6), at); + const at12 = rs.idx(handles.items, i); + const part2: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at12], (value6) => handles.items[at12] = value6), at); handles.items.push(part2); noted = new rs.ByteVec(); } else if (_74 === 3) { - const at10 = rs.idx(handles.items, i); - const part3: rs.ByteSlice = rs.bytesSplitOff(rs.place(() => handles.items[at10], (value5) => handles.items[at10] = value5), at); + const at11 = rs.idx(handles.items, i); + const part3: rs.ByteSlice = rs.bytesSplitOff(rs.place(() => handles.items[at11], (value5) => handles.items[at11] = value5), at); handles.items.push(part3); noted = new rs.ByteVec(); } else if (_74 === 4) { - const at9 = rs.idx(handles.items, i); - rs.bytesTruncate(rs.place(() => handles.items[at9], (value4) => handles.items[at9] = value4), at); + const at10 = rs.idx(handles.items, i); + rs.bytesTruncate(rs.place(() => handles.items[at10], (value4) => handles.items[at10] = value4), at); noted = new rs.ByteVec(); } else if (_74 === 5) { - const at8 = rs.idx(handles.items, i); - rs.bytesTruncate(rs.place(() => handles.items[at8], (value3) => handles.items[at8] = value3), 0); + const at9 = rs.idx(handles.items, i); + rs.bytesTruncate(rs.place(() => handles.items[at9], (value3) => handles.items[at9] = value3), 0); noted = new rs.ByteVec(); } else if (_74 === 6) { noted = rs.byteVecFrom(Uint8Array.of(rs.bytesIsUnique(handles.items[rs.idx(handles.items, i)]) ? 1 : 0)); @@ -4834,20 +4836,17 @@ export function owners(dataData: Uint8Array, dataStart: number, dataLen: number, rs.bytesDrop(rs.vecRemove(handles, i)); noted = new rs.ByteVec(); } else if (_74 === 11) { - const at6 = rs.idx(handles.items, i); - rs.bytesAdvance(rs.place(() => handles.items[at6], (value2) => handles.items[at6] = value2), at); + const at7 = rs.idx(handles.items, i); + rs.bytesAdvance(rs.place(() => handles.items[at7], (value2) => handles.items[at7] = value2), at); noted = new rs.ByteVec(); } else if (_74 === 12) { - const at5 = rs.idx(handles.items, i); - const part4: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at5], (value) => handles.items[at5] = value), at); + const at6 = rs.idx(handles.items, i); + const part4: rs.ByteSlice = rs.bytesSplitTo(rs.place(() => handles.items[at6], (value) => handles.items[at6] = value), at); handles.items.push(part4); noted = new rs.ByteVec(); } else if (_74 === 13) { - const at4 = rs.idx(handles.items, i); - const _143: rs.ByteSlice = rs.bytesClone(handles.items[at4]); - const _144: rs.ByteSlice = rs.byteSliceRange(handles.items[at4], at); - const part5: rs.ByteSlice = rs.bytesSliceRef(_143, _144); - rs.bytesDrop(_143); + const sub: rs.ByteSlice = rs.byteSliceRange(handles.items[rs.idx(handles.items, i)], at); + const part5: rs.ByteSlice = rs.bytesSliceRef(handles.items[rs.idx(handles.items, i)], sub); handles.items.push(part5); noted = new rs.ByteVec(); } else if (_74 === 14) { diff --git a/ts/fixtures/stock/src/buf.rs b/ts/fixtures/stock/src/buf.rs index 1068769e3..f7aa49a8e 100644 --- a/ts/fixtures/stock/src/buf.rs +++ b/ts/fixtures/stock/src/buf.rs @@ -974,7 +974,7 @@ pub fn edits(data: &[u8], out: &mut Vec) { 4 => { let all: &[u8] = bytes.as_ref(); let sub = &all[a.min(rest.len())..]; - bytes.slice_ref(sub) + bytes.clone().slice_ref(sub) } 5 => { bytes.advance(a); @@ -1106,8 +1106,8 @@ pub fn owners(data: &[u8], out: &mut Vec) { vec![] } 13 => { - let handle = &handles[i]; - let part = handle.clone().slice_ref(&handle[at..]); + let sub = &handles[i][at..]; + let part = handles[i].slice_ref(sub); handles.push(part); vec![] } diff --git a/ts/src/lower/caller.rs b/ts/src/lower/caller.rs index 07966a06e..5f82daee9 100644 --- a/ts/src/lower/caller.rs +++ b/ts/src/lower/caller.rs @@ -503,6 +503,10 @@ const BYTES: &[(&str, &str, usize)] = &[ ("bytes_mut_buf_advance", "::advance", 0), ]; +/// The [`BYTES`] sites of a `debug_assert!`, which a build without debug assertions leaves +/// out, as Rust leaves a call past it undefined. +const BYTES_DEBUG: &[&str] = &["bytes_mut_set_len"]; + /// The `rs.Sites` of the toolchain's std, which `rs.setHook` takes: where each panic /// [`SITES`] names is, as the native build names it, and each [`BYTES`] names, in the /// program's `bytes`, if it has one. @@ -549,10 +553,11 @@ fn bytes_sites(t: &mut Translator<'_, '_>, span: Span) -> Vec<(Key, Expr)> { } let mut props = Vec::new(); for &(key, path, nth) in BYTES { - let found = fns.get(path).and_then(|&f| panics(tcx, f, true).into_iter().nth(nth)); + let found = fns.get(path).map(|&f| panics(tcx, f, true).into_iter().nth(nth)); match found { - Some(at) => props.push((Key::Name(key.into()), location(tcx, at))), - None => { + Some(Some(at)) => props.push((Key::Name(key.into()), location(tcx, at))), + Some(None) if BYTES_DEBUG.contains(&key) => {} + _ => { t.err( span, format!("the bytes crate's compiled code has no panic {nth} in `{path}`, counting from 0"), diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs index d663cf3f3..1c65599f1 100644 --- a/ts/src/lower/mir/origin.rs +++ b/ts/src/lower/mir/origin.rs @@ -539,6 +539,45 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { stays.then_some(place) } + /// The place the shared reference `r` borrows, as [`Self::referent`] has it, or the + /// element `v[i]` it is where it is `&v[i]` of a `Vec` or slice by an index the body + /// assigns once, which the borrow keeps where it is: two such borrows lend one element. + fn lent(&self, r: &Operand<'tcx>) -> Option> { + let indexed = self.traced(r).filter(|&l| self.def_at[l].is_none()).and_then(|l| { + let (def_id, gargs, args) = self.def_call(l)?; + let by_usize = gargs + .types() + .nth(1) + .is_some_and(|ty| matches!(ty.kind(), ty::Uint(ty::UintTy::Usize))); + if !by_usize || library::key(self.tcx, def_id)? != ("Index".into(), "index".into()) { + return None; + } + let elem = match &args.get(1)?.node { + Operand::Constant(_) => { + let offset = u64::try_from(self.const_int(&args[1].node)?).ok()?; + ProjectionElem::ConstantIndex { + offset, + min_length: offset + 1, + from_end: false, + } + } + op => { + let i = op.place()?.as_local()?; + let fixed = match self.is_arg(i) { + true => self.defs[i] == 0, + false => self.defs[i] == 1, + }; + if !fixed || self.mutated[i] { + return None; + } + ProjectionElem::Index(i) + } + }; + Some(self.lent(&args.first()?.node)?.project_deeper(&[elem], self.tcx)) + }); + indexed.or_else(|| self.referent(r)) + } + /// The place of the `Bytes` whose storage the `Bytes` at `place` shares, as the body /// shows: through `slice` or `clone` of one, into a variable assigned once and never /// changed in place, which keeps that storage. @@ -608,7 +647,7 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { Some((owner, name)) => match (owner.as_str(), name.as_str()) { ("Index", "index") if self_ty.is_slice() => self.bytes_of(of, bytes), ("Deref", "deref") | ("AsRef", "as_ref") if self.b.t.std_of(self_ty) == Some(Std::Bytes) => { - let storage = |r| self.referent(r).map(|place| self.storage(place)); + let storage = |r| self.lent(r).map(|place| self.storage(place)); storage(of).is_some_and(|place| storage(bytes) == Some(place)) } _ => false, diff --git a/ts/tests/fixtures.rs b/ts/tests/fixtures.rs index 3912ce838..f8b3978aa 100644 --- a/ts/tests/fixtures.rs +++ b/ts/tests/fixtures.rs @@ -515,6 +515,30 @@ mod lang { super::lang("panics", Some("hook")); } + /// A program with a hook translates when its crates are built without debug assertions, + /// which leave out the panic of `bytes`' `debug_assert!`. + #[test] + fn panics_without_debug_assertions() { + let name = "lang/panics"; + let out = super::scratch().join(name).join("nodebug"); + let args: [&std::ffi::OsStr; 5] = [ + "--out".as_ref(), + out.as_ref(), + "--".as_ref(), + "--features".as_ref(), + "hook".as_ref(), + ]; + let output = super::driver_command(name, &super::fixture(name).join("ts.toml"), &args, "nodebug-build") + .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false") + .output() + .expect("running rust2-ts"); + assert!( + output.status.success(), + "translating {name} without debug assertions failed:\n{}", + String::from_utf8_lossy(&output.stderr) + ); + } + lang!( args, arrays,