From f9482d14dd376c72ec796ec35917760d7b44d272 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 16:42:15 -0800 Subject: [PATCH 01/17] chore(quest): claim quest/m3/ts-unsafe/unsafecell.md Quest-Claim: 62ddb59853204c5b From b31ccdbb9bb7c240ba943459dfd30e8e5ea138af Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 17:46:50 -0800 Subject: [PATCH 02/17] feat(ts): translate UnsafeCell::raw_get, from_mut, replace, and the unchecked references Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/pointers/caller.ts | 14 +- ts/fixtures/lang/pointers/expected/lib.ts | 13 +- .../lang/pointers/expected/pointers.Slots.ts | 31 +++ .../lang/pointers/expected/pointers.ts | 209 +++++++++++++++++- ts/fixtures/lang/pointers/src/lib.rs | 1 + ts/fixtures/lang/pointers/src/pointers.rs | 162 ++++++++++++++ ts/src/lower/lend.rs | 2 +- ts/src/lower/mir/call.rs | 65 ++++-- ts/src/lower/mir/mod.rs | 60 ++++- ts/src/lower/mir/origin.rs | 4 +- ts/src/lower/pointer.rs | 94 ++++++-- 11 files changed, 608 insertions(+), 47 deletions(-) create mode 100644 ts/fixtures/lang/pointers/expected/pointers.Slots.ts diff --git a/ts/fixtures/lang/pointers/caller.ts b/ts/fixtures/lang/pointers/caller.ts index 3bf736228..75a7b2e9a 100644 --- a/ts/fixtures/lang/pointers/caller.ts +++ b/ts/fixtures/lang/pointers/caller.ts @@ -15,4 +15,16 @@ const unsafeCells = [ [], ].map((bytes) => Uint8Array.of(...op("pointers::cells"), 0, ...bytes)); -export const cases: Uint8Array[] = [...unsafeCells]; +/** + * `pointers::raw_cells` scripts: each operation, then each replace before and after writes + * through pointers to where the cells are kept, and through cells of what a `&mut` borrows. + */ +const rawCells = [ + [0, 9, 2, 3, 4, 5, 6, 7], + [1, 249, 3, 251, 0, 11, 4, 252, 2, 3], + [2, 10, 18, 4, 12, 20, 3, 1, 0], + [5, 13, 21, 0, 3, 5, 253, 4], + [], +].map((bytes) => Uint8Array.of(...op("pointers::raw_cells"), 0, ...bytes)); + +export const cases: Uint8Array[] = [...unsafeCells, ...rawCells]; diff --git a/ts/fixtures/lang/pointers/expected/lib.ts b/ts/fixtures/lang/pointers/expected/lib.ts index e38684560..fa1451647 100644 --- a/ts/fixtures/lang/pointers/expected/lib.ts +++ b/ts/fixtures/lang/pointers/expected/lib.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays } from "./pointers.ts"; +import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays, rawCells } from "./pointers.ts"; import type * as Aimed from "./pointers.Aimed.ts"; import * as Bin from "./pointers.Bin.ts"; import type * as ByCall from "./pointers.ByCall.ts"; @@ -9,6 +9,7 @@ import type * as ByClosure from "./pointers.ByClosure.ts"; import type * as ByObject from "./pointers.ByObject.ts"; import type * as Duo from "./pointers.Duo.ts"; import type * as Halves from "./pointers.Halves.ts"; +import * as Held from "./pointers.Held.ts"; import type * as Housed from "./pointers.Housed.ts"; import type * as Inner from "./pointers.Inner.ts"; import type * as Leaning from "./pointers.Leaning.ts"; @@ -116,6 +117,10 @@ export function copy$pointers_Halves(v: Halves.Halves): Halves.Halves { return ({ at0: copy$mem_MaybeUninit_array_u32(v.at0) } as unknown) as Halves.Halves; } +export function copy$pointers_Held(v: Held.Held): Held.Held { + return new Held.Held(v.n, v.count); +} + export function copy$pointers_Inner(v: Inner.Inner): Inner.Inner { return { a: v.a, b: v.b }; } @@ -184,6 +189,10 @@ export function copy$tuple2_u8_sync_Arc_u8(v: [rs.u8, rs.Arc]): [rs.u8, r return [v[0], v[1]]; } +export function copy$tuple2_u8_u16(v: [rs.u8, rs.u16]): [rs.u8, rs.u16] { + return [v[0], v[1]]; +} + export function copy$tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs.u8] { return [v[0], v[1]]; } @@ -289,6 +298,8 @@ export function cases(): rs.Vec<[string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.By rawPlaces(data26, out26); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::arrays", (_236: rs.u8, data27: rs.ByteSlice, out27: rs.ByteVec): undefined => { arrays(data27, out27); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::raw_cells", (_237: rs.u8, data28: rs.ByteSlice, out28: rs.ByteVec): undefined => { + rawCells(data28, out28); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]]); } diff --git a/ts/fixtures/lang/pointers/expected/pointers.Slots.ts b/ts/fixtures/lang/pointers/expected/pointers.Slots.ts new file mode 100644 index 000000000..98c40ec82 --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.Slots.ts @@ -0,0 +1,31 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; +import type * as Held from "./pointers.Held.ts"; + +/** + * A struct holding cells, as a type that lends pointers to its parts through a + * `*const Self` does. + */ +export class Slots { + num: rs.Cell; + held: rs.Cell; + pair: rs.Cell<[rs.u8, rs.u16]>; + + constructor(num: rs.Cell, held: rs.Cell, pair: rs.Cell<[rs.u8, rs.u16]>) { + this.num = num; + this.held = held; + this.pair = pair; + } + + [Symbol.dispose](): void { + this.held[Symbol.dispose](); + } +} + +/** The pointer to the count, through a pointer to the struct. */ +export function num(this_: Slots): rs.Place { + return this_.num; +} + +//# sourceMappingURL=pointers.Slots.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 27c5d87df..7c07febaa 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_tuple2_u8_u8, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Halves, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u8, drop$pointers_List_String, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; +import { copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_tuple2_u8_u8, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Halves, copy$pointers_Held, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u16, copy$tuple2_u8_u8, drop$pointers_List_String, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; import { then$0bgt67i, then$1a5xgoh, then$0jw3xip } from "./core/bool.ts"; import { isSomeAnd$1s2u5lz, isSomeAnd$1nosv3e, isSomeAnd$07atiew, unwrapOr$0mkoact as option_unwrapOr$0mkoact, unwrapOr$179xo1w as option_unwrapOr$179xo1w, unwrapOr$1fr498u as option_unwrapOr$1fr498u, map$1cfrse5 as option_map$1cfrse5, mapOr$078vnqn as option_mapOr$078vnqn, mapOr$00zno7m as option_mapOr$00zno7m, mapOr$173j2gr as option_mapOr$173j2gr, filter$0wvwd70 } from "./core/option.ts"; import type * as Aimed from "./pointers.Aimed.ts"; @@ -35,6 +35,7 @@ import type * as Refilled from "./pointers.Refilled.ts"; import type * as Run from "./pointers.Run.ts"; import * as Segment from "./pointers.Segment.ts"; import * as Shelf from "./pointers.Shelf.ts"; +import * as Slots from "./pointers.Slots.ts"; import type * as Span from "./pointers.Span.ts"; import type * as Spot from "./pointers.Spot.ts"; import type * as Stable from "./pointers.Stable.ts"; @@ -2980,4 +2981,210 @@ export function lent(n: rs.u8): rs.ByteVec { return o; } +/** + * What `UnsafeCell::raw_get` gives of a reference to a cell of a number, a struct Rust + * copies, one JS keeps as an object, a tuple, and arrays of bytes and of numbers; of a + * pointer to a variable's cell, to a field through a pointer to its struct, and from + * `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, + * an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a + * pointer taken before sees, giving back what it held undropped; and what + * `as_ref_unchecked` and `as_mut_unchecked` lend. + */ +export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { + const count: rs.Arc = new rs.Arc(undefined); + let num: rs.Cell = new rs.Cell(7); + const duo: rs.Cell = new rs.Cell({ _0: 1, _1: 2 }); + const held: rs.Cell = new rs.Cell(new Held.Held(3, rs.arcClone(count))); + const pair: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([4, 5] as [rs.u8, rs.u16]); + const bytes: rs.Cell = new rs.Cell(Uint8Array.of(6, 7)); + const wide: rs.Cell = new rs.Cell([8, 9]); + const slots: Slots.Slots = new Slots.Slots(new rs.Cell(10), new rs.Cell(new Held.Held(11, rs.arcClone(count))), new rs.Cell([12, 13] as [rs.u8, rs.u16])); + const slot: rs.Cell = new rs.Cell(14); + let local: rs.u32 = 15; + const whole: Held.Held = new Held.Held(16, rs.arcClone(count)); + const elems: rs.u16[] = [17, 18, 19]; + for (const op of rs.byteSliceView(data)) { + const n: rs.u8 = rs.u8Div(op, 8); + const _41: rs.u8 = rs.u8Rem(op, 8); + if (_41 === 0) { + const _43: rs.u32 = ((n as rs.u32) << 20) >>> 0; + const _47: rs.Place = num; + _47.v = (_47.v ^ _43) >>> 0; + const _50: Duo.Duo = duo.v; + _50._1 = _50._1 ^ n; + const _53: Held.Held = held.v; + _53.n = _53.n ^ n; + const _56: rs.u16 = ((n as rs.u16) << 8) & 0xffff; + const _60: [rs.u8, rs.u16] = pair.v; + _60[1] = _60[1] ^ _56; + const _63: rs.ByteSlice = rs.byteSliceFrom(bytes.v); + const _66: rs.usize = rs.u8Rem(n, 2) as rs.usize; + if (!(_66 < 2)) { + rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _66); + } + _63.data[_63.start + _66] = _63.data[_63.start + _66] ^ n; + const _70: rs.u16 = ((n as rs.u16) << 4) & 0xffff; + const _74: rs.Slice = rs.sliceFrom(wide.v); + const _77: rs.usize = rs.u8Rem(n, 2) as rs.usize; + if (!(_77 < 2)) { + rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _77); + } + _74.data[_74.start + _77] = _74.data[_74.start + _77] ^ _70; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(duo.v._1, held.v.n)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(pair.v[1]).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceToArray(rs.byteSliceFrom(bytes.v))), undefined, true); + const _106: rs.Slice = rs.sliceFrom(wide.v); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_106.data[_106.start + 1]).reverse()), undefined, true); + } else if (_41 === 1) { + const p: rs.Place> = rs.place(() => num, (value5) => num = value5); + const _111: rs.u32 = rs.u32WrappingMul(p.v.v, ((n as rs.u32) | 1) >>> 0); + p.v.v = _111; + const s: Slots.Slots = slots; + const _118: rs.u32 = n as rs.u32; + const _119: rs.Place = Slots.num(s); + _119.v = rs.u32Add(_119.v, _118); + const _121: Held.Held = s.held.v; + _121.n = _121.n ^ n; + const _123: [rs.u8, rs.u16] = s.pair.v; + _123[0] = _123[0] ^ n; + const _125: rs.u32 = n as rs.u32; + const _126: rs.Place = { v: slot }.v; + _126.v = (_126.v ^ _125) >>> 0; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(slots.held.v.n, slots.pair.v[0])), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slot.v).reverse()), undefined, true); + } else if (_41 === 2) { + const c: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value3) => local = value3)); + c.v = rs.intRotateLeft(c.v, n as rs.u32, 32, false); + const _164: Held.Held = rs.cellOfPlace(rs.partPlace(whole)).v; + _164.n = _164.n ^ n; + const _168: rs.usize = rs.u8Rem(n, 3) as rs.usize; + if (!(_168 < 3)) { + rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _168); + } + const borrowed2 = elems; + const e: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed2[_168], (value4) => borrowed2[_168] = value4)); + const _172: rs.u16 = n as rs.u16; + const _173: rs.Place = e; + _173.v = _173.v ^ _172; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(local).reverse()), undefined, true); + rs.byteVecPush(out, whole.n); + rs.iterExtendBytes(out, rs.iterFlatMap(rs.iterSlice(elems), (e2: rs.u16): Uint8Array => { + return rs.u16ToBeBytes(e2).reverse(); + }, (value: Uint8Array) => rs.iterSlice(value), true, 2), undefined, true); + } else if (_41 === 3) { + const p2: rs.Place = num; + const _189: rs.u32 = ((n as rs.u32) << 3) >>> 0; + const pointer5 = num; + const old14 = pointer5.v; + pointer5.v = _189; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old14).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(p2.v).reverse()), undefined, true); + const p3: Held.Held = held.v; + const _202: Held.Held = new Held.Held(n, rs.arcClone(count)); + const pointer6 = held.v; + const old15 = copy$pointers_Held(pointer6); + rs.ptrWrite(pointer6, _202); + const old2: Held.Held = old15; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old2.n, p3.n, count.strong & 0xff)), undefined, true); + old2[Symbol.dispose](); + rs.byteVecPush(out, count.strong & 0xff); + const p4: [rs.u8, rs.u16] = pair.v; + const _221: [rs.u8, rs.u16] = [n, rs.u16Mul(n as rs.u16, 3)]; + const pointer7 = pair.v; + const old16 = copy$tuple2_u8_u16(pointer7); + rs.ptrWrite(pointer7, _221); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old16[0], p4[0])), undefined, true); + const p5: rs.ByteSlice = rs.byteSliceFrom(bytes.v); + const pointer8 = rs.byteSliceFrom(bytes.v); + const old17 = rs.byteSliceToArray(pointer8); + rs.byteSliceSet(pointer8, Uint8Array.of(n, ~n & 0xff)); + rs.iterExtendBytes(out, rs.iterSlice(old17), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceToArray(p5)), undefined, true); + const pointer9 = duo.v; + const old18 = copy$pointers_Duo(pointer9); + rs.ptrWrite(pointer9, { _0: n, _1: n ^ 1 }); + const old5: Duo.Duo = old18; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old5._0, old5._1, duo.v._0)), undefined, true); + } else if (_41 === 4) { + const _251: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value) => local = value)); + const _253: rs.u32 = n as rs.u32; + const pointer2 = _251; + const old11 = pointer2.v; + pointer2.v = _253; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old11).reverse()), undefined, true); + const h2: rs.Cell = rs.cellOfPlace(rs.partPlace(whole)); + const p6: Held.Held = h2.v; + const _262: Held.Held = new Held.Held(~n & 0xff, rs.arcClone(count)); + const pointer3 = h2.v; + const old12 = copy$pointers_Held(pointer3); + rs.ptrWrite(pointer3, _262); + const old7: Held.Held = old12; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old7.n, p6.n, whole.n, count.strong & 0xff)), undefined, true); + const _278: rs.usize = rs.u8Rem(n, 3) as rs.usize; + if (!(_278 < 3)) { + rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _278); + } + const borrowed = elems; + const _276: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_278], (value2) => borrowed[_278] = value2)); + const _282: rs.u16 = n as rs.u16; + const pointer4 = _276; + const old13 = pointer4.v; + pointer4.v = _282; + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old13).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(local).reverse()), undefined, true); + old7[Symbol.dispose](); + } else if (_41 === 5) { + const _288: rs.u32 = n as rs.u32; + const _289: rs.Place = num; + _289.v = (_289.v ^ _288) >>> 0; + const r: rs.Place = num; + r.v = rs.intRotateLeft(r.v, 1, 32, false); + const _295: Held.Held = held.v; + _295.n = _295.n ^ n; + const _297: rs.u16 = n as rs.u16; + const _298: [rs.u8, rs.u16] = pair.v; + _298[1] = _298[1] ^ _297; + const _300: rs.ByteSlice = rs.byteSliceFrom(bytes.v); + _300.data[_300.start + 1] = _300.data[_300.start + 1] ^ n; + rs.sliceSet(rs.sliceFrom(wide.v), [n as rs.u16, 1]); + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(held.v.n, duo.v._0)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(pair.v[1]).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceView(rs.byteSliceFrom(bytes.v))), undefined, true); + const _331: rs.Slice = rs.sliceFrom(wide.v); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_331.data[_331.start]).reverse()), undefined, true); + const arr: Uint8Array = Uint8Array.of(n, 1, 2); + const a: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(arr)))); + const _338: rs.ByteSlice = rs.byteSliceFrom(a.v); + const _340: rs.usize = rs.u8Rem(n, 3) as rs.usize; + if (!(_340 < 3)) { + rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _340); + } + _338.data[_338.start + _340] = _338.data[_338.start + _340] ^ 64; + const pointer = rs.byteSliceFrom(a.v); + const old10 = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, Uint8Array.of(3, n, 4)); + rs.iterExtendBytes(out, rs.iterSlice(old10), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(copy$array_u8(arr)), undefined, true); + } else { + rs.byteVecPush(out, n); + } + } + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); + const _357: Held.Held = held.v; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_357.n, count.strong & 0xff)), undefined, true); + _357[Symbol.dispose](); + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); + slots.held[Symbol.dispose](); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(whole.n, count.strong & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterFlatMap(rs.iterSlice(elems), (e3: rs.u16): Uint8Array => { + return rs.u16ToBeBytes(e3).reverse(); + }, (value: Uint8Array) => rs.iterSlice(value), true, 2), undefined, true); + whole[Symbol.dispose](); + count[Symbol.dispose](); +} + //# sourceMappingURL=pointers.ts.map diff --git a/ts/fixtures/lang/pointers/src/lib.rs b/ts/fixtures/lang/pointers/src/lib.rs index 28baff2b1..af7b1979d 100644 --- a/ts/fixtures/lang/pointers/src/lib.rs +++ b/ts/fixtures/lang/pointers/src/lib.rs @@ -49,5 +49,6 @@ fn cases() -> Vec<(&'static str, Case)> { ("pointers::raw_arcs", |_, data, out| pointers::raw_arcs(data, out)), ("pointers::raw_places", |_, data, out| pointers::raw_places(data, out)), ("pointers::arrays", |_, data, out| pointers::arrays(data, out)), + ("pointers::raw_cells", |_, data, out| pointers::raw_cells(data, out)), ] } diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index 10bfc8ffd..f4a50f2d8 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2409,3 +2409,165 @@ fn lent(n: u8) -> Vec { struct Holder { arr: [u32; 2], } + +/// A struct holding cells, as a type that lends pointers to its parts through a +/// `*const Self` does. +struct Slots { + num: UnsafeCell, + held: UnsafeCell, + pair: UnsafeCell<(u8, u16)>, +} + +impl Slots { + /// The pointer to the count, through a pointer to the struct. + unsafe fn num(this: *const Self) -> *mut u32 { + unsafe { UnsafeCell::raw_get(&raw const (*this).num) } + } +} + +/// What `UnsafeCell::raw_get` gives of a reference to a cell of a number, a struct Rust +/// copies, one JS keeps as an object, a tuple, and arrays of bytes and of numbers; of a +/// pointer to a variable's cell, to a field through a pointer to its struct, and from +/// `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, +/// an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a +/// pointer taken before sees, giving back what it held undropped; and what +/// `as_ref_unchecked` and `as_mut_unchecked` lend. +pub fn raw_cells(data: &[u8], out: &mut Vec) { + let count = Rc::new(()); + let num = UnsafeCell::new(7u32); + let duo = UnsafeCell::new(Duo(1, 2)); + let held = UnsafeCell::new(Held { + n: 3, + count: Rc::clone(&count), + }); + let pair = UnsafeCell::new((4u8, 5u16)); + let bytes = UnsafeCell::new([6u8, 7]); + let wide = UnsafeCell::new([8u16, 9]); + let slots = Slots { + num: UnsafeCell::new(10), + held: UnsafeCell::new(Held { + n: 11, + count: Rc::clone(&count), + }), + pair: UnsafeCell::new((12, 13)), + }; + let slot = MaybeUninit::new(UnsafeCell::new(14u32)); + let mut local = 15u32; + let mut whole = Held { + n: 16, + count: Rc::clone(&count), + }; + let mut elems = [17u16, 18, 19]; + for &op in data { + let n = op / 8; + match op % 8 { + // Through references, each read back through `get`. + 0 => unsafe { + *UnsafeCell::raw_get(&num) ^= u32::from(n) << 20; + (*UnsafeCell::raw_get(&duo)).1 ^= n; + (*UnsafeCell::raw_get(&held)).n ^= n; + (*UnsafeCell::raw_get(&pair)).1 ^= u16::from(n) << 8; + (*UnsafeCell::raw_get(&bytes))[usize::from(n % 2)] ^= n; + (*UnsafeCell::raw_get(&wide))[usize::from(n % 2)] ^= u16::from(n) << 4; + out.extend((*num.get()).to_le_bytes()); + out.extend([(*duo.get()).1, (*held.get()).n]); + out.extend((*pair.get()).1.to_le_bytes()); + out.extend(*bytes.get()); + out.extend((*wide.get())[1].to_le_bytes()); + }, + // Through raw pointers to where each cell is kept. + 1 => unsafe { + let p = &raw const num; + *UnsafeCell::raw_get(p) = (*UnsafeCell::raw_get(p)).wrapping_mul(u32::from(n) | 1); + let s = &raw const slots; + *Slots::num(s) += u32::from(n); + (*UnsafeCell::raw_get(&raw const (*s).held)).n ^= n; + (*UnsafeCell::raw_get(&raw const (*s).pair)).0 ^= n; + *UnsafeCell::raw_get(slot.as_ptr()) ^= u32::from(n); + out.extend((*num.get()).to_le_bytes()); + out.extend((*slots.num.get()).to_le_bytes()); + out.extend([(*slots.held.get()).n, (*slots.pair.get()).0]); + out.extend((*slot.assume_init_ref().get()).to_le_bytes()); + }, + // Cells of what a `&mut` borrows, written through `get` and seen by the borrowed. + 2 => unsafe { + let c = UnsafeCell::from_mut(&mut local); + *c.get() = (*c.get()).rotate_left(u32::from(n)); + let h = UnsafeCell::from_mut(&mut whole); + (*h.get()).n ^= n; + let e = UnsafeCell::from_mut(&mut elems[usize::from(n % 3)]); + *e.get() ^= u16::from(n); + out.extend(local.to_le_bytes()); + out.push(whole.n); + out.extend(elems.iter().flat_map(|e| e.to_le_bytes())); + }, + // Each replaced, what it held given back, a pointer taken before seeing the new. + 3 => unsafe { + let p = num.get(); + let old = num.replace(u32::from(n) << 3); + out.extend(old.to_le_bytes()); + out.extend((*p).to_le_bytes()); + let p = held.get(); + let old = held.replace(Held { + n, + count: Rc::clone(&count), + }); + out.extend([old.n, (*p).n, Rc::strong_count(&count) as u8]); + drop(old); + out.push(Rc::strong_count(&count) as u8); + let p = pair.get(); + let old = pair.replace((n, u16::from(n) * 3)); + out.extend([old.0, (*p).0]); + let p = bytes.get(); + let old = bytes.replace([n, !n]); + out.extend(old); + out.extend(*p); + let old = duo.replace(Duo(n, n ^ 1)); + out.extend([old.0, old.1, (*duo.get()).0]); + }, + // Through cells `from_mut` makes, each replaced. + 4 => unsafe { + let old = UnsafeCell::from_mut(&mut local).replace(u32::from(n)); + out.extend(old.to_le_bytes()); + let h = UnsafeCell::from_mut(&mut whole); + let p = h.get(); + let old = h.replace(Held { + n: !n, + count: Rc::clone(&count), + }); + out.extend([old.n, (*p).n, whole.n, Rc::strong_count(&count) as u8]); + let old = UnsafeCell::from_mut(&mut elems[usize::from(n % 3)]).replace(u16::from(n)); + out.extend(old.to_le_bytes()); + out.extend(local.to_le_bytes()); + }, + // What the unchecked references lend, and a cell of an array a `&mut` borrows. + 5 => unsafe { + *num.as_mut_unchecked() ^= u32::from(n); + let r = num.as_mut_unchecked(); + *r = r.rotate_left(1); + held.as_mut_unchecked().n ^= n; + pair.as_mut_unchecked().1 ^= u16::from(n); + bytes.as_mut_unchecked()[1] ^= n; + *wide.as_mut_unchecked() = [u16::from(n), 1]; + out.extend(num.as_ref_unchecked().to_le_bytes()); + out.extend([held.as_ref_unchecked().n, duo.as_ref_unchecked().0]); + out.extend(pair.as_ref_unchecked().1.to_le_bytes()); + out.extend(bytes.as_ref_unchecked()); + out.extend(wide.as_ref_unchecked()[0].to_le_bytes()); + let mut arr = [n, 1, 2]; + let a = UnsafeCell::from_mut(&mut arr); + (*a.get())[usize::from(n % 3)] ^= 0x40; + let old = a.replace([3, n, 4]); + out.extend(old); + out.extend(arr); + }, + _ => out.push(n), + } + } + out.extend(num.into_inner().to_le_bytes()); + out.extend([held.into_inner().n, Rc::strong_count(&count) as u8]); + out.extend(slots.num.into_inner().to_le_bytes()); + drop(slots.held); + out.extend([whole.n, Rc::strong_count(&count) as u8]); + out.extend(elems.iter().flat_map(|e| e.to_le_bytes())); +} diff --git a/ts/src/lower/lend.rs b/ts/src/lower/lend.rs index fb9f68fe8..697bcedc8 100644 --- a/ts/src/lower/lend.rs +++ b/ts/src/lower/lend.rs @@ -75,7 +75,7 @@ pub fn plan<'tcx>(t: &Translator<'_, 'tcx>, def_id: DefId, args: GenericArgsRef< ("mem", "swap" | "replace" | "take") | ("Clone", "clone_from") | ("ToOwned", "clone_into") - | ("raw" | "array" | "Cell" | "ptr", "from_mut") + | ("raw" | "array" | "Cell" | "UnsafeCell" | "ptr", "from_mut") | ("Option", "get_or_insert" | "get_or_insert_with" | "get_or_insert_default" | "insert") => true, // `Clone::clone`, which takes the slice, and an array as a slice, which it is. ("ToOwned", "to_owned") => true, diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index b32ff4e28..57dbbe1f9 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -533,6 +533,22 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { { done(p, false) } + // `get` of the cell a reference points at, which the pointer it was made into + // would only be where that cell is kept. + ("UnsafeCell", "raw_get") + if let Some(cell) = args[0] + .node + .place() + .and_then(|p| self.raw_cells.get(&p.as_local()?).copied()) => + { + let cell = self.place_expr(cell, span, out); + let get = tcx + .associated_items(tcx.parent(def_id)) + .filter_by_name_unhygienic(rustc_span::Symbol::intern("get")) + .next()? + .def_id; + done(self.b.call_def(get, gargs, vec![cell], span, out), false) + } // A place of no bytes has nothing to read or write, and every value of its type // is the one it holds. What made the `&mut` still runs, as its unread temporary. ("mem", "take" | "replace" | "swap") if self.b.t.zero_sized(targ(0)?) => match name.as_str() { @@ -703,10 +719,10 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } // A slice or an array of the one value, or a cell of it, read and written where the // `&mut` points. - ("raw" | "array", "from_mut") | ("Cell", "from_mut") + ("raw" | "array" | "Cell" | "UnsafeCell", "from_mut") if owner != "raw" || library::std_path(tcx, def_id) == ["slice", "raw", "from_mut"] => { - let made = self.one_of(owner == "Cell", targ(0)?, &args[0].node, span, out)?; + let made = self.one_of(&owner, targ(0)?, &args[0].node, span, out)?; done(made, true) } // A range of numbers steps in place, with nothing between it and the loop. @@ -1387,15 +1403,15 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { Some(borrowed) } - /// `slice::from_mut`, `array::from_mut`, or with `cell`, `Cell::from_mut` of `op`, a - /// `&mut` to a `ty`: of the `rs.Place` it is or of the place it names, or else in place - /// of the bytes or the object it is, which a cell reads a copy of, as a `Cell` only - /// moves or copies a value out, and a slice writes its element into. A slice of one - /// byte no array holds is refused, as is either of an object JS can't write a whole - /// value into. + /// `slice::from_mut`, `array::from_mut`, or `from_mut` of `owner`, `Cell` or + /// `UnsafeCell`, of `op`, a `&mut` to a `ty`: of the `rs.Place` it is or of the place it + /// names, or else in place of the bytes or the object it is, which a `Cell` reads a copy + /// of, as it only moves or copies a value out, an `UnsafeCell` lends as itself, as its + /// `get` points at it, and a slice writes its element into. A slice of one byte no array + /// holds is refused, as is any of an object JS can't write a whole value into. fn one_of( &mut self, - cell: bool, + owner: &str, ty: Ty<'tcx>, op: &Operand<'tcx>, span: Span, @@ -1405,11 +1421,19 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { let place = op.place().filter(|p| p.projection.is_empty())?; let deref = tcx.mk_place_deref(place); // `array::from_mut` makes a `&mut [T; 1]`, which is a slice as a `&mut [T]` is. - let (of_place, of_object) = match cell { - true => ("cellOfPlace", "cellIn"), - false => ("sliceOfPlace", "sliceIn"), + let of_place = match owner { + "Cell" | "UnsafeCell" => "cellOfPlace", + _ => "sliceOfPlace", }; let of = |made: Expr| Expr::rs(of_place, vec![made]); + // A `Cell` of an object reads a copy, to the depth a copy of it goes. + let depth = (owner == "Cell").then(|| self.copy_depth(ty)).flatten(); + let of_object = |object: Expr| match owner { + "Cell" => Expr::rs("cellIn", [object].into_iter().chain(depth.clone()).collect()), + "UnsafeCell" => of(Expr::rs("partPlace", vec![object])), + _ => Expr::rs("sliceIn", vec![object]), + }; + let cell = owner != "raw" && owner != "array"; if !cell && crate::lower::byte(ty) { return Some(self.err( span, @@ -1418,18 +1442,13 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } if self.in_array(deref) { let slice = self.place_expr(deref, span, out); - let mut made = vec![self.b.t.array_rs(ty, "View").call(vec![slice])]; - if cell { - made.extend(self.copy_depth(ty)); - } - return Some(Expr::rs(of_object, made)); + return Some(of_object(self.b.t.array_rs(ty, "View").call(vec![slice]))); } - // A cell of an object reads a copy wherever it is, as its place may be one a write - // lands in, such as an element of a slice of one. + // A cell of an object is of it wherever it is, as its place may be one a write lands + // in, such as an element of a slice of one. if cell && self.b.t.write_in(ty).is_some() { - let mut made = vec![self.operand(op, span, out)]; - made.extend(self.copy_depth(ty)); - return Some(Expr::rs("cellIn", made)); + let object = self.operand(op, span, out); + return Some(of_object(object)); } if let Slot::Alias(Some(target)) = &self.slots[place.local] && assignable(target) @@ -1445,7 +1464,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } if self.b.t.write_in(ty).is_some() { let object = self.operand(op, span, out); - return Some(Expr::rs(of_object, vec![object])); + return Some(of_object(object)); } Some(self.err( span, diff --git a/ts/src/lower/mir/mod.rs b/ts/src/lower/mir/mod.rs index 45dc195cb..31204c5fa 100644 --- a/ts/src/lower/mir/mod.rs +++ b/ts/src/lower/mir/mod.rs @@ -434,8 +434,13 @@ pub struct Fx<'m, 'b, 'a, 'tcx> { idx_of: FxHashMap>, /// The halves a `|` joins into a `u64`, by the local of the shift that is its left side. halves: FxHashMap, &'m Operand<'tcx>)>, - /// Temporaries of those joins, whose assignments go unlowered. + /// Temporaries of those joins, and the pointers of `raw_cells`, whose assignments go + /// unlowered. joined: FxHashSet, + /// Pointers made of a reference to an `UnsafeCell` that only `UnsafeCell::raw_get` + /// reads, right after, by the place the reference points at, which it reads in their + /// place: the cell itself, where a raw pointer to it would be where it is kept. + raw_cells: FxHashMap>, /// The paths a switch lowered so far tests the variant of, with the enum it tests, /// which `tsc` narrows in its arms. An `Option`'s `Some` is the path itself, so it /// may be tested as both. @@ -534,6 +539,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { idx_of: FxHashMap::default(), halves: FxHashMap::default(), joined: FxHashSet::default(), + raw_cells: FxHashMap::default(), switched: FxHashSet::default(), tested: IndexVec::from_elem_n(false, n), widened: FxHashSet::default(), @@ -715,6 +721,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { self.find_stepped(); self.unborrow_passed(); self.find_halves(); + self.find_raw_cells(); self.find_slotted_calls(); self.find_scalars(); self.find_returning(); @@ -1413,6 +1420,57 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } } + /// Find the pointers [`Self::raw_cells`] holds: `&raw const *r` of a reference `r`, + /// read once, as the argument of the `UnsafeCell::raw_get` its block ends in, with + /// nothing but storage markers between. + fn find_raw_cells(&mut self) { + let mir: &'m mir::Body<'tcx> = self.mir; + let tcx = self.tcx; + for (bb, data) in mir.basic_blocks.iter_enumerated() { + let TerminatorKind::Call { func, args, .. } = &data.terminator().kind else { + continue; + }; + let ty::FnDef(def_id, _) = *func.ty(&mir.local_decls, tcx).kind() else { + continue; + }; + if !crate::lower::library::is_std(tcx, def_id) + || crate::lower::library::key(tcx, def_id) + .is_none_or(|(owner, name)| (owner.as_str(), name.as_str()) != ("UnsafeCell", "raw_get")) + { + continue; + } + let Some(Operand::Move(p)) = args.first().map(|a| &a.node) else { + continue; + }; + let Some(local) = p.as_local().filter(|&l| self.defs[l] == 1 && self.uses[l] == 1) else { + continue; + }; + let Some((at, i)) = self.def_at[local] else { + continue; + }; + let Some(Rvalue::RawPtr(mir::RawPtrKind::Const | mir::RawPtrKind::Mut, cell)) = self.def_rvalue(local) + else { + continue; + }; + let behind_ref = match cell.projection.as_slice() { + [rest @ .., mir::ProjectionElem::Deref] => { + Place::ty_from(cell.local, rest, &mir.local_decls, tcx).ty.is_ref() + } + _ => false, + }; + let next = data.statements[i + 1..].iter().all(|s| { + matches!( + s.kind, + StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop + ) + }); + if behind_ref && at == bb && next { + self.raw_cells.insert(local, *cell); + self.joined.insert(local); + } + } + } + /// Mark the locals whose variant a switch or a library call's test tests, that a /// closure or async block captures and could test, or that std takes, whose runtime /// function infers its type parameters from them, and what references to them borrow. diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs index 33fd92e18..464f61aa5 100644 --- a/ts/src/lower/mir/origin.rs +++ b/ts/src/lower/mir/origin.rs @@ -413,7 +413,9 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { ("ptr" | "NonNull", "cast_const" | "cast_mut") | ("NonNull", "as_ptr" | "new_unchecked") => { self.arc_pointer(&args.first()?.node) } - ("UnsafeCell", "get") | ("Cell", "as_ptr") | ("MaybeUninit", "as_ptr" | "as_mut_ptr") => Some(false), + ("UnsafeCell", "get" | "raw_get") | ("Cell", "as_ptr") | ("MaybeUninit", "as_ptr" | "as_mut_ptr") => { + Some(false) + } _ => None, } } diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index 087e48216..77f086fec 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -344,7 +344,10 @@ pub fn derives(tcx: ty::TyCtxt<'_>, def_id: rustc_hir::def_id::DefId) -> Option< | ("NonNull", "dangling" | "add" | "sub" | "offset" | "write") | ("MaybeUninit", "new" | "uninit" | "assume_init") | ("ManuallyDrop", "new" | "into_inner") - | ("UnsafeCell", "new" | "get" | "raw_get" | "get_mut" | "into_inner") + | ( + "UnsafeCell", + "new" | "get" | "raw_get" | "get_mut" | "as_ref_unchecked" | "as_mut_unchecked" | "into_inner", + ) | ("Cell", "as_ptr" | "get_mut") | ("Box", "new") => Some(Derives::Other), ("ptr", "read" | "read_unaligned" | "read_volatile") @@ -380,12 +383,15 @@ pub fn allocates(tcx: ty::TyCtxt<'_>, def_id: rustc_hir::def_id::DefId) -> bool /// Which of std's `def_id`'s arguments are `&mut`s whose lowering may give what they /// borrow another value, rather than writing into it, where one is an alias of a borrowed /// place: the place is assigned again, as `mem::replace`, `clone_from`, and `clone_into` -/// assign it, and a whole write of what `slice::from_mut`, `array::from_mut`, or -/// `Cell::from_mut` makes does. `mem::replace`'s value is no such one. +/// assign it, and a whole write of what `slice::from_mut`, `array::from_mut`, +/// `Cell::from_mut`, or `UnsafeCell::from_mut` makes does. `mem::replace`'s value is no +/// such one. pub fn rebinds(tcx: ty::TyCtxt<'_>, def_id: rustc_hir::def_id::DefId) -> &'static [usize] { match super::library::key(tcx, def_id) { Some((owner, name)) => match (owner.as_str(), name.as_str()) { - ("mem", "replace" | "take") | ("Clone", "clone_from") | ("raw" | "array" | "Cell", "from_mut") => &[0], + ("mem", "replace" | "take") + | ("Clone", "clone_from") + | ("raw" | "array" | "Cell" | "UnsafeCell", "from_mut") => &[0], ("ToOwned", "clone_into") => &[1], ("mem", "swap") => &[0, 1], _ => &[], @@ -404,6 +410,7 @@ pub fn lends(tcx: ty::TyCtxt<'_>, def_id: rustc_hir::def_id::DefId) -> bool { ("MaybeUninit", "as_mut_ptr" | "write" | "assume_init_mut" | "assume_init_drop") | ("ManuallyDrop", "drop" | "take") | ("UnsafeCell" | "Cell", "get_mut") + | ("UnsafeCell", "as_mut_unchecked") | ("NonNull" | "ptr", "as_mut") | ("Box", "leak") ) @@ -431,8 +438,10 @@ pub fn keeps<'tcx>(tcx: ty::TyCtxt<'tcx>, def_id: rustc_hir::def_id::DefId, args | "cast_mut" | "eq" | "read" | "read_unaligned" | "read_volatile" | "drop_in_place" ) | ("Box", "into_raw" | "leak" | "from_raw") | ("ManuallyDrop", "new" | "into_inner" | "drop" | "take") - | ("UnsafeCell", "new" | "get" | "get_mut" | "into_inner") - | ("Cell", "as_ptr" | "get_mut") + | ( + "UnsafeCell", + "new" | "get" | "raw_get" | "get_mut" | "as_ref_unchecked" | "as_mut_unchecked" | "into_inner" + ) | ("Cell", "as_ptr" | "get_mut") ) || owner == "Atomic" && super::library::is_std(tcx, def_id) ); keeps || from_reference(tcx, def_id, args) @@ -505,6 +514,13 @@ pub fn call<'tcx>( true => Expr::rs("byteSliceFrom", vec![e]), false => super::library::slice_from(e), }; + // The pointer an `rs.Cell` gives to what it holds: the cell, the place of a value JS + // copies, or else the object or array it holds. + let cell_pointer = |b: &Body<'_, '_, 'tcx>, cell: Expr| match b.t.pointee(pointee()) { + Pointee::Place => cell, + Pointee::Array => to_pointer(b, cell.member("v")), + _ => cell.member("v"), + }; let to_reference = |b: &Body<'_, '_, 'tcx>, e: Expr| match super::byte_array(b.t.uninit_inner(pointee())) { true => Expr::rs("byteSliceView", vec![e]), false => super::library::slice_view(e), @@ -525,7 +541,7 @@ pub fn call<'tcx>( ("ptr", "null" | "null_mut") => Expr::Rs("NULL".into()), // A pointer to `()` is an identity, which the `rs.Place` of a place or a reference // is not. A box of one holds nothing, so its pointer is dangling. - ("MaybeUninit", "as_ptr" | "as_mut_ptr") | ("UnsafeCell", "get") | ("Cell", "as_ptr") + ("MaybeUninit", "as_ptr" | "as_mut_ptr") | ("UnsafeCell", "get" | "raw_get") | ("Cell", "as_ptr") if b.t.identity_pointee(pointee()) => { b.err( @@ -743,13 +759,8 @@ pub fn call<'tcx>( } ("ptr", "swap") if b.t.zero_sized(pointee()) => Expr::undefined(), ("ptr", "replace") => { - let p = b.spill(arg(), "pointer", out); - let value = arg(); - let old = read(b, p.clone(), pointee(), span); - let old = b.spill(old, "old", out); - let write = write(b, p, value, pointee(), span); - out.push(StmtKind::Expr(write).at(None)); - old + let (p, value) = (arg(), arg()); + replace(b, p, value, pointee(), span, out) } ("ptr", "swap") => { let (x, y) = (b.spill(arg(), "x", out), b.spill(arg(), "y", out)); @@ -833,10 +844,39 @@ pub fn call<'tcx>( // An `rs.Cell`, a `Cell`'s too, which is the place a pointer to a value JS copies is, // and holds the object a pointer to any other is. ("UnsafeCell", "new") => Expr::New(Box::new(Expr::Rs("Cell".into())), vec![arg()]), - ("UnsafeCell", "get") | ("Cell", "as_ptr") if kind(b, pointee()) == Some(Pointee::Place) => arg(), - ("UnsafeCell", "get") | ("Cell", "as_ptr") if array(b, pointee()) => to_pointer(b, arg().member("v")), - ("UnsafeCell" | "Cell", "get_mut") if b.t.needs_place(pointee()) => arg(), - ("UnsafeCell", "get" | "get_mut" | "into_inner") | ("Cell", "as_ptr" | "get_mut") => arg().member("v"), + ("UnsafeCell", "get") | ("Cell", "as_ptr") => { + let cell = arg(); + cell_pointer(b, cell) + } + // `get` of the cell where the pointer points, an `rs.Place` of where it is kept. + ("UnsafeCell", "raw_get") => { + let tcx = b.t.tcx; + let cell = tcx + .type_of(tcx.parent(call.def_id)) + .instantiate(tcx, gargs) + .skip_norm_wip(); + let cell = match b.t.pointee(cell) { + Pointee::Object => arg(), + _ => arg().member("v"), + }; + cell_pointer(b, cell) + } + // `ptr::replace` of the pointer `get` gives. + ("UnsafeCell", "replace") if b.t.zero_sized(pointee()) => { + let _ = arg(); + arg() + } + ("UnsafeCell", "replace") => { + let cell = arg(); + let p = cell_pointer(b, cell); + let value = arg(); + replace(b, p, value, pointee(), span, out) + } + // What `get` points at, lent as `get_mut` lends it. + ("UnsafeCell" | "Cell", "get_mut") | ("UnsafeCell", "as_mut_unchecked") if b.t.needs_place(pointee()) => arg(), + ("UnsafeCell", "get_mut" | "as_ref_unchecked" | "as_mut_unchecked" | "into_inner") | ("Cell", "get_mut") => { + arg().member("v") + } // Pointers into an array: `rs.AtIndex`es, which a `Vec`'s and a slice's give. ("slice" | "Vec", "as_ptr" | "as_mut_ptr") => { let elem = targ(gargs, 0); @@ -1116,6 +1156,24 @@ fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> b.t.snapshot(value, ty, span) } +/// `ptr::replace` of `p`, a pointer to `ty`, with `value`: what it held, as a value of its +/// own. +fn replace<'tcx>( + b: &mut Body<'_, '_, 'tcx>, + p: Expr, + value: Expr, + ty: Ty<'tcx>, + span: Span, + out: &mut Vec, +) -> Expr { + let p = b.spill(p, "pointer", out); + let old = read(b, p.clone(), ty, span); + let old = b.spill(old, "old", out); + let write = write(b, p, value, ty, span); + out.push(StmtKind::Expr(write).at(None)); + old +} + /// The write of `value`, a `ty`, where `p` points. fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { if b.t.zero_sized(ty) { From bdcb53d0bab2b462ca6bfff5c8de39b71a525d7c Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 17:56:05 -0800 Subject: [PATCH 03/17] quest: finish UnsafeCell's raw accessors Co-Authored-By: Claude Opus 5.5 --- quest/m3/ts-unsafe/README.md | 1 - quest/m3/ts-unsafe/unsafecell.md | 15 --------------- 2 files changed, 16 deletions(-) delete mode 100644 quest/m3/ts-unsafe/unsafecell.md diff --git a/quest/m3/ts-unsafe/README.md b/quest/m3/ts-unsafe/README.md index fbe362cfd..0055d64da 100644 --- a/quest/m3/ts-unsafe/README.md +++ b/quest/m3/ts-unsafe/README.md @@ -77,4 +77,3 @@ Public API: the translator's output. - [BinaryHeap from std's MIR](/quest/m3/ts-unsafe/binary-heap-mir.md) - std's `BinaryHeap` and `PeekMut` translate, replacing the runtime's port - [A slice's pointer cast to an array's](/quest/m3/ts-unsafe/array-casts.md) - `*(s as *const [u8] as *const [u8; N])` in a crate's own code, and `IoSlice` - [Unchecked integer operations](/quest/m3/ts-unsafe/unchecked-ops.md) - `unchecked_*` and `NonZero::new_unchecked` translate, aborting on a violated precondition -- [UnsafeCell's raw accessors](/quest/m3/ts-unsafe/unsafecell.md) - `UnsafeCell::raw_get`, `from_mut`, and `replace` translate diff --git a/quest/m3/ts-unsafe/unsafecell.md b/quest/m3/ts-unsafe/unsafecell.md deleted file mode 100644 index 33c4225cd..000000000 --- a/quest/m3/ts-unsafe/unsafecell.md +++ /dev/null @@ -1,15 +0,0 @@ -# [XS] UnsafeCell's raw accessors - -## Goal - -[rust2-ts](/ts/README.md) translates `UnsafeCell::raw_get`, `UnsafeCell::from_mut`, and -`UnsafeCell::replace`, proven by differential tests against native Rust. Today each is -refused as an unsupported call, and `raw_get(&c)` also as a raw pointer behind a -reference. - -## Plan - -`UnsafeCell::get` already lends the cell where it is an `rs.Place`. No consumer at the moq -pin. - -Public API: the translator's output. From 9f0ad6b4b8029bf96ae4e302f21d88849fd9e7da Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 18:53:49 -0800 Subject: [PATCH 04/17] fix(ts): take a raw_get pointer only from the call's own block, read by copy too Co-Authored-By: Claude Opus 5.5 --- .../lang/pointers/expected/pointers.ts | 163 +++++++++--------- ts/fixtures/lang/pointers/src/pointers.rs | 1 + ts/fixtures/reject/census.txt | 1 + ts/fixtures/reject/expected.txt | 1 + ts/fixtures/reject/src/alloc.rs | 9 + ts/fixtures/reject/ts.toml | 1 + ts/src/lower/mir/mod.rs | 8 +- 7 files changed, 100 insertions(+), 84 deletions(-) diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 7c07febaa..7fc1d1151 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -3010,47 +3010,50 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _43: rs.u32 = ((n as rs.u32) << 20) >>> 0; const _47: rs.Place = num; _47.v = (_47.v ^ _43) >>> 0; - const _50: Duo.Duo = duo.v; - _50._1 = _50._1 ^ n; - const _53: Held.Held = held.v; - _53.n = _53.n ^ n; - const _56: rs.u16 = ((n as rs.u16) << 8) & 0xffff; - const _60: [rs.u8, rs.u16] = pair.v; - _60[1] = _60[1] ^ _56; - const _63: rs.ByteSlice = rs.byteSliceFrom(bytes.v); - const _66: rs.usize = rs.u8Rem(n, 2) as rs.usize; - if (!(_66 < 2)) { - rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _66); + const _50: rs.u32 = n as rs.u32; + const _51: rs.Place = num; + _51.v = (_51.v ^ _50) >>> 0; + const _54: Duo.Duo = duo.v; + _54._1 = _54._1 ^ n; + const _57: Held.Held = held.v; + _57.n = _57.n ^ n; + const _60: rs.u16 = ((n as rs.u16) << 8) & 0xffff; + const _64: [rs.u8, rs.u16] = pair.v; + _64[1] = _64[1] ^ _60; + const _67: rs.ByteSlice = rs.byteSliceFrom(bytes.v); + const _70: rs.usize = rs.u8Rem(n, 2) as rs.usize; + if (!(_70 < 2)) { + rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _70); } - _63.data[_63.start + _66] = _63.data[_63.start + _66] ^ n; - const _70: rs.u16 = ((n as rs.u16) << 4) & 0xffff; - const _74: rs.Slice = rs.sliceFrom(wide.v); - const _77: rs.usize = rs.u8Rem(n, 2) as rs.usize; - if (!(_77 < 2)) { - rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _77); + _67.data[_67.start + _70] = _67.data[_67.start + _70] ^ n; + const _74: rs.u16 = ((n as rs.u16) << 4) & 0xffff; + const _78: rs.Slice = rs.sliceFrom(wide.v); + const _81: rs.usize = rs.u8Rem(n, 2) as rs.usize; + if (!(_81 < 2)) { + rs.panic("index out of bounds: the len is " + 2 + " but the index is " + _81); } - _74.data[_74.start + _77] = _74.data[_74.start + _77] ^ _70; + _78.data[_78.start + _81] = _78.data[_78.start + _81] ^ _74; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(duo.v._1, held.v.n)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(pair.v[1]).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceToArray(rs.byteSliceFrom(bytes.v))), undefined, true); - const _106: rs.Slice = rs.sliceFrom(wide.v); - rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_106.data[_106.start + 1]).reverse()), undefined, true); + const _110: rs.Slice = rs.sliceFrom(wide.v); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_110.data[_110.start + 1]).reverse()), undefined, true); } else if (_41 === 1) { const p: rs.Place> = rs.place(() => num, (value5) => num = value5); - const _111: rs.u32 = rs.u32WrappingMul(p.v.v, ((n as rs.u32) | 1) >>> 0); - p.v.v = _111; + const _115: rs.u32 = rs.u32WrappingMul(p.v.v, ((n as rs.u32) | 1) >>> 0); + p.v.v = _115; const s: Slots.Slots = slots; - const _118: rs.u32 = n as rs.u32; - const _119: rs.Place = Slots.num(s); - _119.v = rs.u32Add(_119.v, _118); - const _121: Held.Held = s.held.v; - _121.n = _121.n ^ n; - const _123: [rs.u8, rs.u16] = s.pair.v; - _123[0] = _123[0] ^ n; - const _125: rs.u32 = n as rs.u32; - const _126: rs.Place = { v: slot }.v; - _126.v = (_126.v ^ _125) >>> 0; + const _122: rs.u32 = n as rs.u32; + const _123: rs.Place = Slots.num(s); + _123.v = rs.u32Add(_123.v, _122); + const _125: Held.Held = s.held.v; + _125.n = _125.n ^ n; + const _127: [rs.u8, rs.u16] = s.pair.v; + _127[0] = _127[0] ^ n; + const _129: rs.u32 = n as rs.u32; + const _130: rs.Place = { v: slot }.v; + _130.v = (_130.v ^ _129) >>> 0; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(slots.held.v.n, slots.pair.v[0])), undefined, true); @@ -3058,17 +3061,17 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { } else if (_41 === 2) { const c: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value3) => local = value3)); c.v = rs.intRotateLeft(c.v, n as rs.u32, 32, false); - const _164: Held.Held = rs.cellOfPlace(rs.partPlace(whole)).v; - _164.n = _164.n ^ n; - const _168: rs.usize = rs.u8Rem(n, 3) as rs.usize; - if (!(_168 < 3)) { - rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _168); + const _168: Held.Held = rs.cellOfPlace(rs.partPlace(whole)).v; + _168.n = _168.n ^ n; + const _172: rs.usize = rs.u8Rem(n, 3) as rs.usize; + if (!(_172 < 3)) { + rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _172); } const borrowed2 = elems; - const e: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed2[_168], (value4) => borrowed2[_168] = value4)); - const _172: rs.u16 = n as rs.u16; - const _173: rs.Place = e; - _173.v = _173.v ^ _172; + const e: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed2[_172], (value4) => borrowed2[_172] = value4)); + const _176: rs.u16 = n as rs.u16; + const _177: rs.Place = e; + _177.v = _177.v ^ _176; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(local).reverse()), undefined, true); rs.byteVecPush(out, whole.n); rs.iterExtendBytes(out, rs.iterFlatMap(rs.iterSlice(elems), (e2: rs.u16): Uint8Array => { @@ -3076,26 +3079,26 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { }, (value: Uint8Array) => rs.iterSlice(value), true, 2), undefined, true); } else if (_41 === 3) { const p2: rs.Place = num; - const _189: rs.u32 = ((n as rs.u32) << 3) >>> 0; + const _193: rs.u32 = ((n as rs.u32) << 3) >>> 0; const pointer5 = num; const old14 = pointer5.v; - pointer5.v = _189; + pointer5.v = _193; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old14).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(p2.v).reverse()), undefined, true); const p3: Held.Held = held.v; - const _202: Held.Held = new Held.Held(n, rs.arcClone(count)); + const _206: Held.Held = new Held.Held(n, rs.arcClone(count)); const pointer6 = held.v; const old15 = copy$pointers_Held(pointer6); - rs.ptrWrite(pointer6, _202); + rs.ptrWrite(pointer6, _206); const old2: Held.Held = old15; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old2.n, p3.n, count.strong & 0xff)), undefined, true); old2[Symbol.dispose](); rs.byteVecPush(out, count.strong & 0xff); const p4: [rs.u8, rs.u16] = pair.v; - const _221: [rs.u8, rs.u16] = [n, rs.u16Mul(n as rs.u16, 3)]; + const _225: [rs.u8, rs.u16] = [n, rs.u16Mul(n as rs.u16, 3)]; const pointer7 = pair.v; const old16 = copy$tuple2_u8_u16(pointer7); - rs.ptrWrite(pointer7, _221); + rs.ptrWrite(pointer7, _225); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old16[0], p4[0])), undefined, true); const p5: rs.ByteSlice = rs.byteSliceFrom(bytes.v); const pointer8 = rs.byteSliceFrom(bytes.v); @@ -3109,61 +3112,61 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const old5: Duo.Duo = old18; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old5._0, old5._1, duo.v._0)), undefined, true); } else if (_41 === 4) { - const _251: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value) => local = value)); - const _253: rs.u32 = n as rs.u32; - const pointer2 = _251; + const _255: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value) => local = value)); + const _257: rs.u32 = n as rs.u32; + const pointer2 = _255; const old11 = pointer2.v; - pointer2.v = _253; + pointer2.v = _257; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old11).reverse()), undefined, true); const h2: rs.Cell = rs.cellOfPlace(rs.partPlace(whole)); const p6: Held.Held = h2.v; - const _262: Held.Held = new Held.Held(~n & 0xff, rs.arcClone(count)); + const _266: Held.Held = new Held.Held(~n & 0xff, rs.arcClone(count)); const pointer3 = h2.v; const old12 = copy$pointers_Held(pointer3); - rs.ptrWrite(pointer3, _262); + rs.ptrWrite(pointer3, _266); const old7: Held.Held = old12; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old7.n, p6.n, whole.n, count.strong & 0xff)), undefined, true); - const _278: rs.usize = rs.u8Rem(n, 3) as rs.usize; - if (!(_278 < 3)) { - rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _278); + const _282: rs.usize = rs.u8Rem(n, 3) as rs.usize; + if (!(_282 < 3)) { + rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _282); } const borrowed = elems; - const _276: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_278], (value2) => borrowed[_278] = value2)); - const _282: rs.u16 = n as rs.u16; - const pointer4 = _276; + const _280: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_282], (value2) => borrowed[_282] = value2)); + const _286: rs.u16 = n as rs.u16; + const pointer4 = _280; const old13 = pointer4.v; - pointer4.v = _282; + pointer4.v = _286; rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old13).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(local).reverse()), undefined, true); old7[Symbol.dispose](); } else if (_41 === 5) { - const _288: rs.u32 = n as rs.u32; - const _289: rs.Place = num; - _289.v = (_289.v ^ _288) >>> 0; + const _292: rs.u32 = n as rs.u32; + const _293: rs.Place = num; + _293.v = (_293.v ^ _292) >>> 0; const r: rs.Place = num; r.v = rs.intRotateLeft(r.v, 1, 32, false); - const _295: Held.Held = held.v; - _295.n = _295.n ^ n; - const _297: rs.u16 = n as rs.u16; - const _298: [rs.u8, rs.u16] = pair.v; - _298[1] = _298[1] ^ _297; - const _300: rs.ByteSlice = rs.byteSliceFrom(bytes.v); - _300.data[_300.start + 1] = _300.data[_300.start + 1] ^ n; + const _299: Held.Held = held.v; + _299.n = _299.n ^ n; + const _301: rs.u16 = n as rs.u16; + const _302: [rs.u8, rs.u16] = pair.v; + _302[1] = _302[1] ^ _301; + const _304: rs.ByteSlice = rs.byteSliceFrom(bytes.v); + _304.data[_304.start + 1] = _304.data[_304.start + 1] ^ n; rs.sliceSet(rs.sliceFrom(wide.v), [n as rs.u16, 1]); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(held.v.n, duo.v._0)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(pair.v[1]).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceView(rs.byteSliceFrom(bytes.v))), undefined, true); - const _331: rs.Slice = rs.sliceFrom(wide.v); - rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_331.data[_331.start]).reverse()), undefined, true); + const _335: rs.Slice = rs.sliceFrom(wide.v); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_335.data[_335.start]).reverse()), undefined, true); const arr: Uint8Array = Uint8Array.of(n, 1, 2); const a: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(arr)))); - const _338: rs.ByteSlice = rs.byteSliceFrom(a.v); - const _340: rs.usize = rs.u8Rem(n, 3) as rs.usize; - if (!(_340 < 3)) { - rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _340); + const _342: rs.ByteSlice = rs.byteSliceFrom(a.v); + const _344: rs.usize = rs.u8Rem(n, 3) as rs.usize; + if (!(_344 < 3)) { + rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _344); } - _338.data[_338.start + _340] = _338.data[_338.start + _340] ^ 64; + _342.data[_342.start + _344] = _342.data[_342.start + _344] ^ 64; const pointer = rs.byteSliceFrom(a.v); const old10 = rs.byteSliceToArray(pointer); rs.byteSliceSet(pointer, Uint8Array.of(3, n, 4)); @@ -3174,9 +3177,9 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { } } rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); - const _357: Held.Held = held.v; - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_357.n, count.strong & 0xff)), undefined, true); - _357[Symbol.dispose](); + const _361: Held.Held = held.v; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_361.n, count.strong & 0xff)), undefined, true); + _361[Symbol.dispose](); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); slots.held[Symbol.dispose](); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(whole.n, count.strong & 0xff)), undefined, true); diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index f4a50f2d8..b1ba472b5 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2464,6 +2464,7 @@ pub fn raw_cells(data: &[u8], out: &mut Vec) { // Through references, each read back through `get`. 0 => unsafe { *UnsafeCell::raw_get(&num) ^= u32::from(n) << 20; + *UnsafeCell::raw_get(&num as *const _) ^= u32::from(n); (*UnsafeCell::raw_get(&duo)).1 ^= n; (*UnsafeCell::raw_get(&held)).n ^= n; (*UnsafeCell::raw_get(&pair)).1 ^= u16::from(n) << 8; diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index d7acc7b88..603394d1a 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -10,6 +10,7 @@ fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another +fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index fb9f70ed7..26dd8a4b5 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -14,6 +14,7 @@ fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another +fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index dd4e03d8b..40b9c29a6 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -30,6 +30,15 @@ pub fn vec_pointer(v: &mut Vec) -> usize { unsafe { (*p).len() } } +/// A pointer to a cell behind a reference, made before a call that `UnsafeCell::raw_get` +/// comes after, which reads it in its place only right after it is made. +pub fn cell_pointer_kept(c: &std::cell::UnsafeCell, n: u32) -> u32 { + let x = n.wrapping_mul(3); + let p: *const std::cell::UnsafeCell = c; + let y = vec_pointer(&mut vec![0; x as usize]); + unsafe { *std::cell::UnsafeCell::raw_get(p) + y as u32 } +} + /// Casts between pointers to bytes and to values outside memory from `alloc`, which read /// the bytes of one type as another: of an array's bytes to `u32`s, of memory from /// `alloc` to the crate's struct, which JS keeps as an object, and of a pointer to values diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index b5754cba1..7d4ef2bd7 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -4,6 +4,7 @@ roots = [ "alloc::alloc_retyped", "alloc::allocated", "alloc::bytes_as_values", + "alloc::cell_pointer_kept", "alloc::reinterpreted", "alloc::vec_pointer", "alloc::zeroed", diff --git a/ts/src/lower/mir/mod.rs b/ts/src/lower/mir/mod.rs index 31204c5fa..b0a9bc98a 100644 --- a/ts/src/lower/mir/mod.rs +++ b/ts/src/lower/mir/mod.rs @@ -1421,7 +1421,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } /// Find the pointers [`Self::raw_cells`] holds: `&raw const *r` of a reference `r`, - /// read once, as the argument of the `UnsafeCell::raw_get` its block ends in, with + /// read once, as the argument of the `UnsafeCell::raw_get` its own block ends in, with /// nothing but storage markers between. fn find_raw_cells(&mut self) { let mir: &'m mir::Body<'tcx> = self.mir; @@ -1439,13 +1439,13 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { { continue; } - let Some(Operand::Move(p)) = args.first().map(|a| &a.node) else { + let Some(Operand::Copy(p) | Operand::Move(p)) = args.first().map(|a| &a.node) else { continue; }; let Some(local) = p.as_local().filter(|&l| self.defs[l] == 1 && self.uses[l] == 1) else { continue; }; - let Some((at, i)) = self.def_at[local] else { + let Some((_, i)) = self.def_at[local].filter(|&(at, _)| at == bb) else { continue; }; let Some(Rvalue::RawPtr(mir::RawPtrKind::Const | mir::RawPtrKind::Mut, cell)) = self.def_rvalue(local) @@ -1464,7 +1464,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop ) }); - if behind_ref && at == bb && next { + if behind_ref && next { self.raw_cells.insert(local, *cell); self.joined.insert(local); } From 7111cf49615aaeaae2024708757b87e83cc90797 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 21:34:47 -0800 Subject: [PATCH 05/17] fix(ts): write a Cell's object in place, and settle raw_get's null, slice, and reassigned cases Cell::replace, set, swap, take, and update of an object or array now write into what Cell::as_ptr points at, sharing ptr::replace's and ptr::swap's helpers, which copy the old value out as objectCopy does. Cell::swap translates. UnsafeCell::raw_get keeps a pointer to no memory as it is, gives a raw slice of a cell of a slice, and through a reference is refused where the cell is assigned again, as get is. Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/borrows/expected/borrows.ts | 10 +- ts/fixtures/lang/borrows/expected/lib.ts | 4 + .../lang/conversions/expected/conversions.ts | 348 ++++++++++++++---- ts/fixtures/lang/locals/expected/lib.ts | 4 + ts/fixtures/lang/locals/expected/locals.ts | 7 +- ts/fixtures/lang/manual/expected/lib.ts | 4 + .../lang/manual/expected/uninit.Counted.ts | 15 +- ts/fixtures/lang/manual/expected/uninit.ts | 9 +- ts/fixtures/lang/pointers/caller.ts | 19 +- ts/fixtures/lang/pointers/expected/lib.ts | 24 +- .../lang/pointers/expected/pointers.Slots.ts | 2 +- .../lang/pointers/expected/pointers.ts | 338 ++++++++++++++--- ts/fixtures/lang/pointers/src/lib.rs | 7 + ts/fixtures/lang/pointers/src/pointers.rs | 117 +++++- ts/fixtures/lang/pointers/ts.toml | 2 +- ts/fixtures/lang/sorts/expected/lib.ts | 4 + ts/fixtures/lang/sorts/expected/sorts.ts | 43 ++- ts/fixtures/reject/census.txt | 1 + ts/fixtures/reject/expected.txt | 1 + ts/fixtures/reject/src/alloc.rs | 10 + ts/fixtures/reject/ts.toml | 1 + ts/fixtures/task/expected/drops/called.ts | 6 +- ts/runtime/rs.ts | 4 +- ts/runtime/rs/task.ts | 9 +- ts/src/lower/copy.rs | 21 +- ts/src/lower/mir/call.rs | 45 +-- ts/src/lower/mir/place.rs | 11 +- ts/src/lower/mod.rs | 15 + ts/src/lower/pointer.rs | 101 +++-- ts/src/lower/task.rs | 47 ++- 30 files changed, 1000 insertions(+), 229 deletions(-) diff --git a/ts/fixtures/lang/borrows/expected/borrows.ts b/ts/fixtures/lang/borrows/expected/borrows.ts index cf5bfa9ec..18e6b73f0 100644 --- a/ts/fixtures/lang/borrows/expected/borrows.ts +++ b/ts/fixtures/lang/borrows/expected/borrows.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$Option_tuple2_u64_u64, copy$tuple2_u64_u64, drop$Result_cell_RefMut_Vec_u8_cell_BorrowMutError, drop$Result_cell_RefMut_array4_u8_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Parts_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Tagged_cell_BorrowMutError, drop$Result_cell_RefMut_tuple2_u16_Vec_u8_cell_BorrowMutError, drop$Result_cell_RefMut_u32_cell_BorrowMutError, drop$Result_cell_RefMut_u32_tuple2_cell_RefMut_borrows_Parts_unit, drop$Result_cell_Ref_Vec_u8_cell_BorrowError, drop$Result_cell_Ref_array4_u8_cell_BorrowError, drop$Result_cell_Ref_borrows_Parts_cell_BorrowError, drop$Result_cell_Ref_ref_str_tuple2_cell_Ref_tuple2_u8_ref_str_u8, drop$Result_cell_Ref_tuple2_u16_Vec_u8_cell_BorrowError, drop$Result_cell_Ref_u32_cell_BorrowError, drop$Result_cell_Ref_u32_cell_Ref_borrows_Parts, drop$Result_cell_Ref_u32_tuple2_cell_Ref_borrows_Parts_unit, drop$tuple2_Option_cell_Ref_borrows_Tagged_Option_cell_RefMut_borrows_Tagged, drop$tuple2_Result_cell_Ref_Vec_u8_cell_BorrowError_cell_Ref_Vec_u8, drop$tuple2_cell_RefMut_slice_u8_cell_RefMut_slice_u8, drop$tuple2_cell_Ref_Vec_u8_cell_Ref_u8, drop$tuple2_cell_Ref_str_cell_RefMut_u32, drop$tuple3_cell_Ref_String_cell_Ref_array4_u8_cell_Ref_u32 } from "./lib.ts"; +import { copy$Option_tuple2_u64_u64, copy$tuple2_u64_u64, copy$tuple2_u8_char, drop$Result_cell_RefMut_Vec_u8_cell_BorrowMutError, drop$Result_cell_RefMut_array4_u8_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Parts_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Tagged_cell_BorrowMutError, drop$Result_cell_RefMut_tuple2_u16_Vec_u8_cell_BorrowMutError, drop$Result_cell_RefMut_u32_cell_BorrowMutError, drop$Result_cell_RefMut_u32_tuple2_cell_RefMut_borrows_Parts_unit, drop$Result_cell_Ref_Vec_u8_cell_BorrowError, drop$Result_cell_Ref_array4_u8_cell_BorrowError, drop$Result_cell_Ref_borrows_Parts_cell_BorrowError, drop$Result_cell_Ref_ref_str_tuple2_cell_Ref_tuple2_u8_ref_str_u8, drop$Result_cell_Ref_tuple2_u16_Vec_u8_cell_BorrowError, drop$Result_cell_Ref_u32_cell_BorrowError, drop$Result_cell_Ref_u32_cell_Ref_borrows_Parts, drop$Result_cell_Ref_u32_tuple2_cell_Ref_borrows_Parts_unit, drop$tuple2_Option_cell_Ref_borrows_Tagged_Option_cell_RefMut_borrows_Tagged, drop$tuple2_Result_cell_Ref_Vec_u8_cell_BorrowError_cell_Ref_Vec_u8, drop$tuple2_cell_RefMut_slice_u8_cell_RefMut_slice_u8, drop$tuple2_cell_Ref_Vec_u8_cell_Ref_u8, drop$tuple2_cell_Ref_str_cell_RefMut_u32, drop$tuple3_cell_Ref_String_cell_Ref_array4_u8_cell_Ref_u32 } from "./lib.ts"; import * as Noisy from "./borrows.Noisy.ts"; import * as Parts from "./borrows.Parts.ts"; import * as Polling from "./borrows.Polling.ts"; @@ -33,7 +33,7 @@ export function borrows(data: rs.ByteSlice, out: rs.ByteVec): void { const copy: [rs.u64, rs.u64] = copy$tuple2_u64_u64(copy$tuple2_u64_u64(pair.v)); copy[0] = rs.u64Add(copy[0], op as rs.u64); rs.byteVecPush(out, rs.u64Low(copy$tuple2_u64_u64(pair.v)[0]) & 0xff); - rs.cellSet(pair, [copy[1], copy[0]] as [rs.u64, rs.u64]); + rs.ptrWrite(pair.v, [copy[1], copy[0]] as [rs.u64, rs.u64]); rs.byteVecPush(out, rs.u64Low(copy$tuple2_u64_u64(pair.v)[1]) & 0xff); } else if (_25 === 2) { rs.byteVecPush(out, rs.cellReplace(count, op as rs.u32) & 0xff); @@ -1134,12 +1134,12 @@ export function printed(data: rs.ByteSlice, out: rs.ByteVec): void { rs.refRelease(last); rs.refMutRelease(exclusive); } else if (_25 === 3) { - const _164: [rs.u8, rs.char] = [rs.u8WrappingAdd(pair.v[0], v), rs.u8Add(97, rs.u8Rem(v, 26)) as rs.char]; - rs.cellSet(pair, _164); + const _164: [rs.u8, rs.char] = [rs.u8WrappingAdd(copy$tuple2_u8_char(pair.v)[0], v), rs.u8Add(97, rs.u8Rem(v, 26)) as rs.char]; + rs.ptrWrite(pair.v, _164); const wide: rs.Cell = new rs.Cell(rs.u64Shl(v as rs.u64, 56)); const maybe: rs.Cell = new rs.Cell(thenSome$0quywni(rs.u8Rem(v, 2) === 0, v)); const _184: rs.Cell = new rs.Cell(undefined); - const args34 = [(f: rs.Formatter) => rs.cellDebug(() => pair.v, f, (value: [rs.u8, rs.char], f: rs.Formatter) => rs.fmtTuple([(f: rs.Formatter) => rs.u8Debug(value[0], f), (f: rs.Formatter) => rs.fmtCharDebug(value[1], f)], f)), (f: rs.Formatter) => rs.cellDebug(() => wide.v, f, rs.u64Debug), (f: rs.Formatter) => rs.cellDebug(() => maybe.v, f, (value: rs.u8 | undefined, f: rs.Formatter) => rs.fmtOption(value, f, rs.u8Debug)), (f: rs.Formatter) => rs.cellDebug(() => _184.v, f, rs.fmtUnit)]; + const args34 = [(f: rs.Formatter) => rs.cellDebug(() => copy$tuple2_u8_char(pair.v), f, (value: [rs.u8, rs.char], f: rs.Formatter) => rs.fmtTuple([(f: rs.Formatter) => rs.u8Debug(value[0], f), (f: rs.Formatter) => rs.fmtCharDebug(value[1], f)], f)), (f: rs.Formatter) => rs.cellDebug(() => wide.v, f, rs.u64Debug), (f: rs.Formatter) => rs.cellDebug(() => maybe.v, f, (value: rs.u8 | undefined, f: rs.Formatter) => rs.fmtOption(value, f, rs.u8Debug)), (f: rs.Formatter) => rs.cellDebug(() => _184.v, f, rs.fmtUnit)]; text = rs.fmtFormat([args34[0], " ", rs.fmtSpec({ alternate: true }, args34[0]), " ", args34[1], " ", args34[2], " ", rs.fmtSpec({ alternate: true }, args34[2]), " ", args34[3], " ", rs.fmtSpec({ align: ">", width: 3 }, args34[0])]); } else if (_25 === 4) { rs.cellSet(shown.cell, v); diff --git a/ts/fixtures/lang/borrows/expected/lib.ts b/ts/fixtures/lang/borrows/expected/lib.ts index 44b4c6b14..d00d97391 100644 --- a/ts/fixtures/lang/borrows/expected/lib.ts +++ b/ts/fixtures/lang/borrows/expected/lib.ts @@ -14,6 +14,10 @@ export function copy$tuple2_u64_u64(v: [rs.u64, rs.u64]): [rs.u64, rs.u64] { return [v[0], v[1]]; } +export function copy$tuple2_u8_char(v: [rs.u8, rs.char]): [rs.u8, rs.char] { + return [v[0], v[1]]; +} + export function drop$Option_cell_RefMut_borrows_Tagged(_1: rs.RefCell | undefined): void { if (_1 !== undefined) { rs.refMutRelease(_1); diff --git a/ts/fixtures/lang/conversions/expected/conversions.ts b/ts/fixtures/lang/conversions/expected/conversions.ts index 0988e8a82..7372db684 100644 --- a/ts/fixtures/lang/conversions/expected/conversions.ts +++ b/ts/fixtures/lang/conversions/expected/conversions.ts @@ -772,7 +772,12 @@ export function lentParam$Option_u16(to: rs.Place, data: rs. rs.idx(s, 0); const borrowed6 = s.data; const borrowed7 = s.start; - put$conversions_Value_Option_u16(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed6[borrowed7], (value6) => borrowed6[borrowed7] = value6)))), [make$conversions_Value_Option_u16(data, 26)])[0], out); + const _114: rs.Cell> = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed6[borrowed7], (value6) => borrowed6[borrowed7] = value6))))); + const _121: Array = [make$conversions_Value_Option_u16(data, 26)]; + const pointer = rs.sliceFrom(_114.v); + const old6 = rs.objectCopy(rs.sliceView(pointer)); + rs.sliceSet(pointer, _121); + put$conversions_Value_Option_u16(old6[0], out); rs.idx(s, 0); const borrowed8 = s.data; const borrowed9 = s.start; @@ -780,7 +785,12 @@ export function lentParam$Option_u16(to: rs.Place, data: rs. rs.idx(s, 0); const borrowed10 = s.data; const borrowed11 = s.start; - put$conversions_Value_Option_u16(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed10[borrowed11], (value8) => borrowed10[borrowed11] = value8))))), 1), [[make$conversions_Value_Option_u16(data, 28)]])[0][0], out); + const _147: rs.Cell[]> = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed10[borrowed11], (value8) => borrowed10[borrowed11] = value8))))))); + const _148: Array[] = [[make$conversions_Value_Option_u16(data, 28)]]; + const pointer2 = rs.sliceFrom(_147.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _148); + put$conversions_Value_Option_u16(old7[0][0], out); rs.idx(s, 0); s.data[s.start] = other; rs.idx(s, 0); @@ -834,7 +844,12 @@ export function lentParam$String(to: rs.String, data: rs.ByteSlice, out: rs.Byte rs.idx(s, 0); const borrowed3 = s.data; const borrowed4 = s.start; - put$conversions_Value_String(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))), 1), [make$conversions_Value_String(data, 26)])[0], out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))))); + const _121: rs.String[] = [make$conversions_Value_String(data, 26)]; + const pointer = rs.sliceFrom(_114.v); + const old6 = rs.objectCopy(rs.sliceView(pointer), 1); + rs.sliceSet(pointer, _121); + put$conversions_Value_String(old6[0], out); rs.idx(s, 0); const borrowed5 = s.data; const borrowed6 = s.start; @@ -842,7 +857,12 @@ export function lentParam$String(to: rs.String, data: rs.ByteSlice, out: rs.Byte rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - put$conversions_Value_String(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))), 2), [[make$conversions_Value_String(data, 28)]])[0][0], out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))))); + const _148: rs.String[][] = [[make$conversions_Value_String(data, 28)]]; + const pointer2 = rs.sliceFrom(_147.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 2); + rs.sliceSet(pointer2, _148); + put$conversions_Value_String(old7[0][0], out); rs.idx(s, 0); s.data[s.start] = rs.stringCloneFrom(s.data[s.start], other); rs.idx(s, 0); @@ -896,7 +916,12 @@ export function lentParam$Vec_u32(to: rs.Vec, data: rs.ByteSlice, out: r rs.idx(s, 0); const borrowed3 = s.data; const borrowed4 = s.start; - put$conversions_Value_Vec_u32(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))), 1), [make$conversions_Value_Vec_u32(data, 26)])[0], out); + const _114: rs.Cell[]> = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))))); + const _121: rs.Vec[] = [make$conversions_Value_Vec_u32(data, 26)]; + const pointer = rs.sliceFrom(_114.v); + const old6 = rs.objectCopy(rs.sliceView(pointer), 1); + rs.sliceSet(pointer, _121); + put$conversions_Value_Vec_u32(old6[0], out); rs.idx(s, 0); const borrowed5 = s.data; const borrowed6 = s.start; @@ -904,7 +929,12 @@ export function lentParam$Vec_u32(to: rs.Vec, data: rs.ByteSlice, out: r rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - put$conversions_Value_Vec_u32(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))), 2), [[make$conversions_Value_Vec_u32(data, 28)]])[0][0], out); + const _147: rs.Cell[][]> = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))))); + const _148: rs.Vec[][] = [[make$conversions_Value_Vec_u32(data, 28)]]; + const pointer2 = rs.sliceFrom(_147.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 2); + rs.sliceSet(pointer2, _148); + put$conversions_Value_Vec_u32(old7[0][0], out); rs.idx(s, 0); rs.vecCloneFrom(s.data[s.start], other, 4); rs.idx(s, 0); @@ -958,7 +988,12 @@ export function lentParam$Vec_u8(to: rs.ByteVec, data: rs.ByteSlice, out: rs.Byt rs.idx(s, 0); const borrowed3 = s.data; const borrowed4 = s.start; - put$conversions_Value_Vec_u8(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))), 1), [make$conversions_Value_Vec_u8(data, 26)])[0], out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))))); + const _121: rs.ByteVec[] = [make$conversions_Value_Vec_u8(data, 26)]; + const pointer = rs.sliceFrom(_114.v); + const old6 = rs.objectCopy(rs.sliceView(pointer), 1); + rs.sliceSet(pointer, _121); + put$conversions_Value_Vec_u8(old6[0], out); rs.idx(s, 0); const borrowed5 = s.data; const borrowed6 = s.start; @@ -966,7 +1001,12 @@ export function lentParam$Vec_u8(to: rs.ByteVec, data: rs.ByteSlice, out: rs.Byt rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - put$conversions_Value_Vec_u8(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))), 2), [[make$conversions_Value_Vec_u8(data, 28)]])[0][0], out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))))); + const _148: rs.ByteVec[][] = [[make$conversions_Value_Vec_u8(data, 28)]]; + const pointer2 = rs.sliceFrom(_147.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 2); + rs.sliceSet(pointer2, _148); + put$conversions_Value_Vec_u8(old7[0][0], out); rs.idx(s, 0); rs.byteVecCloneFrom(s.data[s.start], other); rs.idx(s, 0); @@ -1020,13 +1060,13 @@ export function lentParam$array0_u8(to: rs.ByteSlice, data: rs.ByteSlice, out: r rs.idx(s, 0); put$conversions_Value_array0_u8(rs.byteSliceFrom(s.data[s.start]), out); rs.idx(s, 0); - put$conversions_Value_array0_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))), copy$array_u8(make$conversions_Value_array0_u8(data, 25)))), out); + put$conversions_Value_array0_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))), copy$array_u8(make$conversions_Value_array0_u8(data, 25)))), out); rs.idx(s, 0); - put$conversions_Value_array0_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))), 1), [copy$array_u8(make$conversions_Value_array0_u8(data, 26))])[0]), out); + put$conversions_Value_array0_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))), [copy$array_u8(make$conversions_Value_array0_u8(data, 26))])[0]), out); rs.idx(s, 0); put$conversions_Value_array0_u8(rs.byteSliceFrom(replaceFirst$array0_u8(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))))), [make$conversions_Value_array0_u8(data, 27)])[0]), out); rs.idx(s, 0); - put$conversions_Value_array0_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))), 2), [[copy$array_u8(make$conversions_Value_array0_u8(data, 28))]])[0][0]), out); + put$conversions_Value_array0_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))))), [[copy$array_u8(make$conversions_Value_array0_u8(data, 28))]])[0][0]), out); rs.idx(s, 0); const _154: rs.ByteSlice = rs.byteSliceFrom(s.data[s.start]); const _159: rs.ByteSlice = rs.byteSliceFrom(other); @@ -1094,13 +1134,28 @@ export function lentParam$array1_u8(to: rs.ByteSlice, data: rs.ByteSlice, out: r rs.idx(s, 0); put$conversions_Value_array1_u8(rs.byteSliceFrom(s.data[s.start]), out); rs.idx(s, 0); - put$conversions_Value_array1_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))), copy$array_u8(make$conversions_Value_array1_u8(data, 25)))), out); + const _105: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))); + const _111: Uint8Array = copy$array_u8(make$conversions_Value_array1_u8(data, 25)); + const pointer = rs.byteSliceFrom(_105.v); + const old6 = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, _111); + put$conversions_Value_array1_u8(rs.byteSliceFrom(old6), out); rs.idx(s, 0); - put$conversions_Value_array1_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))), 1), [copy$array_u8(make$conversions_Value_array1_u8(data, 26))])[0]), out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))); + const _121: Uint8Array[] = [copy$array_u8(make$conversions_Value_array1_u8(data, 26))]; + const pointer2 = rs.sliceFrom(_114.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _121); + put$conversions_Value_array1_u8(rs.byteSliceFrom(old7[0]), out); rs.idx(s, 0); put$conversions_Value_array1_u8(rs.byteSliceFrom(replaceFirst$array1_u8(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))))), [make$conversions_Value_array1_u8(data, 27)])[0]), out); rs.idx(s, 0); - put$conversions_Value_array1_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))), 2), [[copy$array_u8(make$conversions_Value_array1_u8(data, 28))]])[0][0]), out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))))); + const _148: Uint8Array[][] = [[copy$array_u8(make$conversions_Value_array1_u8(data, 28))]]; + const pointer3 = rs.sliceFrom(_147.v); + const old8 = rs.objectCopy(rs.sliceView(pointer3), 2); + rs.sliceSet(pointer3, _148); + put$conversions_Value_array1_u8(rs.byteSliceFrom(old8[0][0]), out); rs.idx(s, 0); const _154: rs.ByteSlice = rs.byteSliceFrom(s.data[s.start]); const _159: rs.ByteSlice = rs.byteSliceFrom(other); @@ -1160,13 +1215,28 @@ export function lentParam$array2_u32(to: rs.Slice, data: rs.ByteSlice, o rs.idx(s, 0); put$conversions_Value_array2_u32(rs.sliceFrom(s.data[s.start]), out); rs.idx(s, 0); - put$conversions_Value_array2_u32(rs.sliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceFrom(s.data[s.start]))), copy$array_u32(make$conversions_Value_array2_u32(data, 25)))), out); + const _105: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceFrom(s.data[s.start])))); + const _111: rs.u32[] = copy$array_u32(make$conversions_Value_array2_u32(data, 25)); + const pointer = rs.sliceFrom(_105.v); + const old6 = rs.objectCopy(rs.sliceView(pointer)); + rs.sliceSet(pointer, _111); + put$conversions_Value_array2_u32(rs.sliceFrom(old6), out); rs.idx(s, 0); - put$conversions_Value_array2_u32(rs.sliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceFrom(s.data[s.start])))), 1), [copy$array_u32(make$conversions_Value_array2_u32(data, 26))])[0]), out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceFrom(s.data[s.start])))))); + const _121: rs.u32[][] = [copy$array_u32(make$conversions_Value_array2_u32(data, 26))]; + const pointer2 = rs.sliceFrom(_114.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _121); + put$conversions_Value_array2_u32(rs.sliceFrom(old7[0]), out); rs.idx(s, 0); put$conversions_Value_array2_u32(rs.sliceFrom(replaceFirst$array2_u32(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceFrom(s.data[s.start]))))), [make$conversions_Value_array2_u32(data, 27)])[0]), out); rs.idx(s, 0); - put$conversions_Value_array2_u32(rs.sliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceFrom(s.data[s.start])))))), 2), [[copy$array_u32(make$conversions_Value_array2_u32(data, 28))]])[0][0]), out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceFrom(s.data[s.start])))))))); + const _148: rs.u32[][][] = [[copy$array_u32(make$conversions_Value_array2_u32(data, 28))]]; + const pointer3 = rs.sliceFrom(_147.v); + const old8 = rs.objectCopy(rs.sliceView(pointer3), 2); + rs.sliceSet(pointer3, _148); + put$conversions_Value_array2_u32(rs.sliceFrom(old8[0][0]), out); rs.idx(s, 0); const _154: rs.Slice = rs.sliceFrom(s.data[s.start]); const _159: rs.Slice = rs.sliceFrom(other); @@ -1234,13 +1304,28 @@ export function lentParam$array3_u8(to: rs.ByteSlice, data: rs.ByteSlice, out: r rs.idx(s, 0); put$conversions_Value_array3_u8(rs.byteSliceFrom(s.data[s.start]), out); rs.idx(s, 0); - put$conversions_Value_array3_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))), copy$array_u8(make$conversions_Value_array3_u8(data, 25)))), out); + const _105: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))); + const _111: Uint8Array = copy$array_u8(make$conversions_Value_array3_u8(data, 25)); + const pointer = rs.byteSliceFrom(_105.v); + const old6 = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, _111); + put$conversions_Value_array3_u8(rs.byteSliceFrom(old6), out); rs.idx(s, 0); - put$conversions_Value_array3_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))), 1), [copy$array_u8(make$conversions_Value_array3_u8(data, 26))])[0]), out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))); + const _121: Uint8Array[] = [copy$array_u8(make$conversions_Value_array3_u8(data, 26))]; + const pointer2 = rs.sliceFrom(_114.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _121); + put$conversions_Value_array3_u8(rs.byteSliceFrom(old7[0]), out); rs.idx(s, 0); put$conversions_Value_array3_u8(rs.byteSliceFrom(replaceFirst$array3_u8(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))))), [make$conversions_Value_array3_u8(data, 27)])[0]), out); rs.idx(s, 0); - put$conversions_Value_array3_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))), 2), [[copy$array_u8(make$conversions_Value_array3_u8(data, 28))]])[0][0]), out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))))); + const _148: Uint8Array[][] = [[copy$array_u8(make$conversions_Value_array3_u8(data, 28))]]; + const pointer3 = rs.sliceFrom(_147.v); + const old8 = rs.objectCopy(rs.sliceView(pointer3), 2); + rs.sliceSet(pointer3, _148); + put$conversions_Value_array3_u8(rs.byteSliceFrom(old8[0][0]), out); rs.idx(s, 0); const _154: rs.ByteSlice = rs.byteSliceFrom(s.data[s.start]); const _159: rs.ByteSlice = rs.byteSliceFrom(other); @@ -1308,13 +1393,28 @@ export function lentParam$array40_u8(to: rs.ByteSlice, data: rs.ByteSlice, out: rs.idx(s, 0); put$conversions_Value_array40_u8(rs.byteSliceFrom(s.data[s.start]), out); rs.idx(s, 0); - put$conversions_Value_array40_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))), copy$array_u8(make$conversions_Value_array40_u8(data, 25)))), out); + const _105: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))); + const _111: Uint8Array = copy$array_u8(make$conversions_Value_array40_u8(data, 25)); + const pointer = rs.byteSliceFrom(_105.v); + const old6 = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, _111); + put$conversions_Value_array40_u8(rs.byteSliceFrom(old6), out); rs.idx(s, 0); - put$conversions_Value_array40_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))), 1), [copy$array_u8(make$conversions_Value_array40_u8(data, 26))])[0]), out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))); + const _121: Uint8Array[] = [copy$array_u8(make$conversions_Value_array40_u8(data, 26))]; + const pointer2 = rs.sliceFrom(_114.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _121); + put$conversions_Value_array40_u8(rs.byteSliceFrom(old7[0]), out); rs.idx(s, 0); put$conversions_Value_array40_u8(rs.byteSliceFrom(replaceFirst$array40_u8(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start]))))), [make$conversions_Value_array40_u8(data, 27)])[0]), out); rs.idx(s, 0); - put$conversions_Value_array40_u8(rs.byteSliceFrom(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))), 2), [[copy$array_u8(make$conversions_Value_array40_u8(data, 28))]])[0][0]), out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceIn(rs.byteSliceView(rs.byteSliceFrom(s.data[s.start])))))))); + const _148: Uint8Array[][] = [[copy$array_u8(make$conversions_Value_array40_u8(data, 28))]]; + const pointer3 = rs.sliceFrom(_147.v); + const old8 = rs.objectCopy(rs.sliceView(pointer3), 2); + rs.sliceSet(pointer3, _148); + put$conversions_Value_array40_u8(rs.byteSliceFrom(old8[0][0]), out); rs.idx(s, 0); const _154: rs.ByteSlice = rs.byteSliceFrom(s.data[s.start]); const _159: rs.ByteSlice = rs.byteSliceFrom(other); @@ -1374,13 +1474,23 @@ export function lentParam$conversions_Logged(to: Logged.Logged, data: rs.ByteSli rs.idx(s, 0); Logged.put$conversions_Value(s.data[s.start], out); rs.idx(s, 0); - const _104: Logged.Logged = rs.cellReplace(rs.cellIn(s.data[s.start]), Logged.make$conversions_Value(data, 25)); + const _105: rs.Cell = rs.cellOfPlace(rs.partPlace(s.data[s.start])); + const _111: Logged.Logged = Logged.make$conversions_Value(data, 25); + const pointer = _105.v; + const old6 = rs.objectCopy(pointer); + rs.ptrWrite(pointer, _111); + const _104: Logged.Logged = old6; Logged.put$conversions_Value(_104, out); _104[Symbol.dispose](); rs.idx(s, 0); const borrowed3 = s.data; const borrowed4 = s.start; - const old: Logged.Logged = rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))), 1), [Logged.make$conversions_Value(data, 26)])[0]; + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))))); + const _121: Logged.Logged[] = [Logged.make$conversions_Value(data, 26)]; + const pointer2 = rs.sliceFrom(_114.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _121); + const old: Logged.Logged = old7[0]; Logged.put$conversions_Value(old, out); rs.idx(s, 0); const borrowed5 = s.data; @@ -1390,7 +1500,12 @@ export function lentParam$conversions_Logged(to: Logged.Logged, data: rs.ByteSli rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - const old3: Logged.Logged = rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))), 2), [[Logged.make$conversions_Value(data, 28)]])[0][0]; + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))))); + const _148: Logged.Logged[][] = [[Logged.make$conversions_Value(data, 28)]]; + const pointer3 = rs.sliceFrom(_147.v); + const old8 = rs.objectCopy(rs.sliceView(pointer3), 2); + rs.sliceSet(pointer3, _148); + const old3: Logged.Logged = old8[0][0]; Logged.put$conversions_Value(old3, out); rs.idx(s, 0); rs.sliceReplace(s.data, s.start, Logged.clone$Clone(other))[Symbol.dispose](); @@ -1447,11 +1562,21 @@ export function lentParam$conversions_Named(to: Named.Named, data: rs.ByteSlice, rs.idx(s, 0); Named.put$conversions_Value(s.data[s.start], out); rs.idx(s, 0); - Named.put$conversions_Value(rs.cellReplace(rs.cellIn(s.data[s.start]), Named.make$conversions_Value(data, 25)), out); + const _105: rs.Cell = rs.cellOfPlace(rs.partPlace(s.data[s.start])); + const _111: Named.Named = Named.make$conversions_Value(data, 25); + const pointer = _105.v; + const old6 = rs.objectCopy(pointer); + rs.ptrWrite(pointer, _111); + Named.put$conversions_Value(old6, out); rs.idx(s, 0); const borrowed3 = s.data; const borrowed4 = s.start; - Named.put$conversions_Value(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))), 1), [Named.make$conversions_Value(data, 26)])[0], out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed3[borrowed4], (value3) => borrowed3[borrowed4] = value3))))); + const _121: Named.Named[] = [Named.make$conversions_Value(data, 26)]; + const pointer2 = rs.sliceFrom(_114.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _121); + Named.put$conversions_Value(old7[0], out); rs.idx(s, 0); const borrowed5 = s.data; const borrowed6 = s.start; @@ -1459,7 +1584,12 @@ export function lentParam$conversions_Named(to: Named.Named, data: rs.ByteSlice, rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - Named.put$conversions_Value(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))), 2), [[Named.make$conversions_Value(data, 28)]])[0][0], out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5))))))); + const _148: Named.Named[][] = [[Named.make$conversions_Value(data, 28)]]; + const pointer3 = rs.sliceFrom(_147.v); + const old8 = rs.objectCopy(rs.sliceView(pointer3), 2); + rs.sliceSet(pointer3, _148); + Named.put$conversions_Value(old8[0][0], out); rs.idx(s, 0); s.data[s.start] = Named.clone$Clone(other); rs.idx(s, 0); @@ -1524,7 +1654,12 @@ export function lentParam$u32(to: rs.Place, data: rs.ByteSlice, out: rs. rs.idx(s, 0); const borrowed6 = s.data; const borrowed7 = s.start; - put$conversions_Value_u32(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed6[borrowed7], (value6) => borrowed6[borrowed7] = value6)))), [make$conversions_Value_u32(data, 26)])[0], out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed6[borrowed7], (value6) => borrowed6[borrowed7] = value6))))); + const _121: rs.u32[] = [make$conversions_Value_u32(data, 26)]; + const pointer = rs.sliceFrom(_114.v); + const old6 = rs.objectCopy(rs.sliceView(pointer)); + rs.sliceSet(pointer, _121); + put$conversions_Value_u32(old6[0], out); rs.idx(s, 0); const borrowed8 = s.data; const borrowed9 = s.start; @@ -1532,7 +1667,12 @@ export function lentParam$u32(to: rs.Place, data: rs.ByteSlice, out: rs. rs.idx(s, 0); const borrowed10 = s.data; const borrowed11 = s.start; - put$conversions_Value_u32(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed10[borrowed11], (value8) => borrowed10[borrowed11] = value8))))), 1), [[make$conversions_Value_u32(data, 28)]])[0][0], out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed10[borrowed11], (value8) => borrowed10[borrowed11] = value8))))))); + const _148: rs.u32[][] = [[make$conversions_Value_u32(data, 28)]]; + const pointer2 = rs.sliceFrom(_147.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _148); + put$conversions_Value_u32(old7[0][0], out); rs.idx(s, 0); s.data[s.start] = other; rs.idx(s, 0); @@ -1597,7 +1737,12 @@ export function lentParam$u64(to: rs.Place, data: rs.ByteSlice, out: rs. rs.idx(s, 0); const borrowed6 = s.data; const borrowed7 = s.start; - put$conversions_Value_u64(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed6[borrowed7], (value6) => borrowed6[borrowed7] = value6)))), [make$conversions_Value_u64(data, 26)])[0], out); + const _114: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed6[borrowed7], (value6) => borrowed6[borrowed7] = value6))))); + const _121: rs.u64[] = [make$conversions_Value_u64(data, 26)]; + const pointer = rs.sliceFrom(_114.v); + const old6 = rs.objectCopy(rs.sliceView(pointer)); + rs.sliceSet(pointer, _121); + put$conversions_Value_u64(old6[0], out); rs.idx(s, 0); const borrowed8 = s.data; const borrowed9 = s.start; @@ -1605,7 +1750,12 @@ export function lentParam$u64(to: rs.Place, data: rs.ByteSlice, out: rs. rs.idx(s, 0); const borrowed10 = s.data; const borrowed11 = s.start; - put$conversions_Value_u64(rs.cellReplace(rs.cellIn(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed10[borrowed11], (value8) => borrowed10[borrowed11] = value8))))), 1), [[make$conversions_Value_u64(data, 28)]])[0][0], out); + const _147: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceIn(rs.sliceView(rs.sliceOfPlace(rs.place(() => borrowed10[borrowed11], (value8) => borrowed10[borrowed11] = value8))))))); + const _148: rs.u64[][] = [[make$conversions_Value_u64(data, 28)]]; + const pointer2 = rs.sliceFrom(_147.v); + const old7 = rs.objectCopy(rs.sliceView(pointer2), 1); + rs.sliceSet(pointer2, _148); + put$conversions_Value_u64(old7[0][0], out); rs.idx(s, 0); s.data[s.start] = other; rs.idx(s, 0); @@ -1955,37 +2105,55 @@ export function setThrough$Vec_u8(to: rs.ByteVec, value: rs.ByteVec): rs.ByteVec /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ export function setThrough$array0_u8(to: rs.ByteSlice, value: Uint8Array): Uint8Array { - return rs.cellReplace(rs.cellIn(rs.byteSliceView(to)), copy$array_u8(value)); + return rs.cellReplace(rs.cellOfPlace(rs.partPlace(rs.byteSliceView(to))), copy$array_u8(value)); } /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ export function setThrough$array1_u8(to: rs.ByteSlice, value: Uint8Array): Uint8Array { - return rs.cellReplace(rs.cellIn(rs.byteSliceView(to)), copy$array_u8(value)); + const pointer = rs.byteSliceFrom(rs.cellOfPlace(rs.partPlace(rs.byteSliceView(to))).v); + const old = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, copy$array_u8(value)); + return old; } /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ export function setThrough$array2_u32(to: rs.Slice, value: rs.u32[]): rs.u32[] { - return rs.cellReplace(rs.cellIn(rs.sliceView(to)), copy$array_u32(value)); + const pointer = rs.sliceFrom(rs.cellOfPlace(rs.partPlace(rs.sliceView(to))).v); + const old = rs.objectCopy(rs.sliceView(pointer)); + rs.sliceSet(pointer, copy$array_u32(value)); + return old; } /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ export function setThrough$array3_u8(to: rs.ByteSlice, value: Uint8Array): Uint8Array { - return rs.cellReplace(rs.cellIn(rs.byteSliceView(to)), copy$array_u8(value)); + const pointer = rs.byteSliceFrom(rs.cellOfPlace(rs.partPlace(rs.byteSliceView(to))).v); + const old = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, copy$array_u8(value)); + return old; } /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ export function setThrough$array40_u8(to: rs.ByteSlice, value: Uint8Array): Uint8Array { - return rs.cellReplace(rs.cellIn(rs.byteSliceView(to)), copy$array_u8(value)); + const pointer = rs.byteSliceFrom(rs.cellOfPlace(rs.partPlace(rs.byteSliceView(to))).v); + const old = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, copy$array_u8(value)); + return old; } /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ export function setThrough$conversions_Logged(to: Logged.Logged, value: Logged.Logged): Logged.Logged { - return rs.cellReplace(rs.cellIn(to), value); + const pointer = rs.cellOfPlace(rs.partPlace(to)).v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, value); + return old; } /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ export function setThrough$conversions_Named(to: Named.Named, value: Named.Named): Named.Named { - return rs.cellReplace(rs.cellIn(to), value); + const pointer = rs.cellOfPlace(rs.partPlace(to)).v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, value); + return old; } /** A cell of what a `&mut` whose place only the caller knows points at, written through. */ @@ -2398,14 +2566,14 @@ export function converted$array0_u8(data: rs.ByteSlice, out: rs.ByteVec): void { _70.data.set(rs.byteSliceToArray(_71), _70.start); put$conversions_Value_array0_u8(rs.byteSliceFrom(copied), out); put$conversions_Value_array0_u8(rs.byteSliceFrom(a), out); - const cell: rs.Cell = rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(c))); + const cell: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(c)))); const old: Uint8Array = rs.cellReplace(cell, copy$array_u8(make$conversions_Value_array0_u8(data, 10))); rs.cellSet(cell, copy$array_u8(make$conversions_Value_array0_u8(data, 11))); put$conversions_Value_array0_u8(rs.byteSliceFrom(old), out); put$conversions_Value_array0_u8(rs.byteSliceFrom(c), out); put$conversions_Value_array0_u8(rs.byteSliceFrom(setThrough$array0_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), make$conversions_Value_array0_u8(data, 12))), out); put$conversions_Value_array0_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), out); - rs.cellSet(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(held.value))), copy$array_u8(make$conversions_Value_array0_u8(data, 17))); + rs.cellSet(rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(held.value)))), copy$array_u8(make$conversions_Value_array0_u8(data, 17))); put$conversions_Value_array0_u8(rs.byteSliceFrom(held.value), out); const array: Uint8Array[] = [copy$array_u8(make$conversions_Value_array0_u8(data, 13)), copy$array_u8(make$conversions_Value_array0_u8(data, 14)), copy$array_u8(make$conversions_Value_array0_u8(data, 15))]; rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(array).length, rs.sliceFrom(array).length], (n: rs.usize): rs.u8 => { @@ -2506,14 +2674,21 @@ export function converted$array1_u8(data: rs.ByteSlice, out: rs.ByteVec): void { _70.data.set(rs.byteSliceToArray(_71), _70.start); put$conversions_Value_array1_u8(rs.byteSliceFrom(copied), out); put$conversions_Value_array1_u8(rs.byteSliceFrom(a), out); - const cell: rs.Cell = rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(c))); - const old: Uint8Array = rs.cellReplace(cell, copy$array_u8(make$conversions_Value_array1_u8(data, 10))); - rs.cellSet(cell, copy$array_u8(make$conversions_Value_array1_u8(data, 11))); + const cell: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(c)))); + const _79: Uint8Array = copy$array_u8(make$conversions_Value_array1_u8(data, 10)); + const pointer = rs.byteSliceFrom(cell.v); + const old2 = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, _79); + const old: Uint8Array = old2; + const _81: Uint8Array = copy$array_u8(make$conversions_Value_array1_u8(data, 11)); + rs.byteSliceSet(rs.byteSliceFrom(cell.v), _81); put$conversions_Value_array1_u8(rs.byteSliceFrom(old), out); put$conversions_Value_array1_u8(rs.byteSliceFrom(c), out); put$conversions_Value_array1_u8(rs.byteSliceFrom(setThrough$array1_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), make$conversions_Value_array1_u8(data, 12))), out); put$conversions_Value_array1_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), out); - rs.cellSet(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(held.value))), copy$array_u8(make$conversions_Value_array1_u8(data, 17))); + const _96: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(held.value)))); + const _98: Uint8Array = copy$array_u8(make$conversions_Value_array1_u8(data, 17)); + rs.byteSliceSet(rs.byteSliceFrom(_96.v), _98); put$conversions_Value_array1_u8(rs.byteSliceFrom(held.value), out); const array: Uint8Array[] = [copy$array_u8(make$conversions_Value_array1_u8(data, 13)), copy$array_u8(make$conversions_Value_array1_u8(data, 14)), copy$array_u8(make$conversions_Value_array1_u8(data, 15))]; rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(array).length, rs.sliceFrom(array).length], (n: rs.usize): rs.u8 => { @@ -2613,14 +2788,21 @@ export function converted$array2_u32(data: rs.ByteSlice, out: rs.ByteVec): void rs.replaceIn(rs.sliceView(_70), rs.sliceToArray(_71)); put$conversions_Value_array2_u32(rs.sliceFrom(copied), out); put$conversions_Value_array2_u32(rs.sliceFrom(a), out); - const cell: rs.Cell = rs.cellIn(rs.sliceView(rs.sliceFrom(c))); - const old: rs.u32[] = rs.cellReplace(cell, copy$array_u32(make$conversions_Value_array2_u32(data, 10))); - rs.cellSet(cell, copy$array_u32(make$conversions_Value_array2_u32(data, 11))); + const cell: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceFrom(c)))); + const _79: rs.u32[] = copy$array_u32(make$conversions_Value_array2_u32(data, 10)); + const pointer = rs.sliceFrom(cell.v); + const old2 = rs.objectCopy(rs.sliceView(pointer)); + rs.sliceSet(pointer, _79); + const old: rs.u32[] = old2; + const _81: rs.u32[] = copy$array_u32(make$conversions_Value_array2_u32(data, 11)); + rs.sliceSet(rs.sliceFrom(cell.v), _81); put$conversions_Value_array2_u32(rs.sliceFrom(old), out); put$conversions_Value_array2_u32(rs.sliceFrom(c), out); put$conversions_Value_array2_u32(rs.sliceFrom(setThrough$array2_u32(rs.sliceFrom(v.items[rs.idx(v.items, 0)]), make$conversions_Value_array2_u32(data, 12))), out); put$conversions_Value_array2_u32(rs.sliceFrom(v.items[rs.idx(v.items, 0)]), out); - rs.cellSet(rs.cellIn(rs.sliceView(rs.sliceFrom(held.value))), copy$array_u32(make$conversions_Value_array2_u32(data, 17))); + const _96: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.sliceView(rs.sliceFrom(held.value)))); + const _98: rs.u32[] = copy$array_u32(make$conversions_Value_array2_u32(data, 17)); + rs.sliceSet(rs.sliceFrom(_96.v), _98); put$conversions_Value_array2_u32(rs.sliceFrom(held.value), out); const array: rs.u32[][] = [copy$array_u32(make$conversions_Value_array2_u32(data, 13)), copy$array_u32(make$conversions_Value_array2_u32(data, 14)), copy$array_u32(make$conversions_Value_array2_u32(data, 15))]; rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(array).length, rs.sliceFrom(array).length], (n: rs.usize): rs.u8 => { @@ -2720,14 +2902,21 @@ export function converted$array3_u8(data: rs.ByteSlice, out: rs.ByteVec): void { _70.data.set(rs.byteSliceToArray(_71), _70.start); put$conversions_Value_array3_u8(rs.byteSliceFrom(copied), out); put$conversions_Value_array3_u8(rs.byteSliceFrom(a), out); - const cell: rs.Cell = rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(c))); - const old: Uint8Array = rs.cellReplace(cell, copy$array_u8(make$conversions_Value_array3_u8(data, 10))); - rs.cellSet(cell, copy$array_u8(make$conversions_Value_array3_u8(data, 11))); + const cell: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(c)))); + const _79: Uint8Array = copy$array_u8(make$conversions_Value_array3_u8(data, 10)); + const pointer = rs.byteSliceFrom(cell.v); + const old2 = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, _79); + const old: Uint8Array = old2; + const _81: Uint8Array = copy$array_u8(make$conversions_Value_array3_u8(data, 11)); + rs.byteSliceSet(rs.byteSliceFrom(cell.v), _81); put$conversions_Value_array3_u8(rs.byteSliceFrom(old), out); put$conversions_Value_array3_u8(rs.byteSliceFrom(c), out); put$conversions_Value_array3_u8(rs.byteSliceFrom(setThrough$array3_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), make$conversions_Value_array3_u8(data, 12))), out); put$conversions_Value_array3_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), out); - rs.cellSet(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(held.value))), copy$array_u8(make$conversions_Value_array3_u8(data, 17))); + const _96: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(held.value)))); + const _98: Uint8Array = copy$array_u8(make$conversions_Value_array3_u8(data, 17)); + rs.byteSliceSet(rs.byteSliceFrom(_96.v), _98); put$conversions_Value_array3_u8(rs.byteSliceFrom(held.value), out); const array: Uint8Array[] = [copy$array_u8(make$conversions_Value_array3_u8(data, 13)), copy$array_u8(make$conversions_Value_array3_u8(data, 14)), copy$array_u8(make$conversions_Value_array3_u8(data, 15))]; rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(array).length, rs.sliceFrom(array).length], (n: rs.usize): rs.u8 => { @@ -2829,14 +3018,21 @@ export function converted$array40_u8(data: rs.ByteSlice, out: rs.ByteVec): void _70.data.set(rs.byteSliceToArray(_71), _70.start); put$conversions_Value_array40_u8(rs.byteSliceFrom(copied), out); put$conversions_Value_array40_u8(rs.byteSliceFrom(a), out); - const cell: rs.Cell = rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(c))); - const old: Uint8Array = rs.cellReplace(cell, copy$array_u8(make$conversions_Value_array40_u8(data, 10))); - rs.cellSet(cell, copy$array_u8(make$conversions_Value_array40_u8(data, 11))); + const cell: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(c)))); + const _79: Uint8Array = copy$array_u8(make$conversions_Value_array40_u8(data, 10)); + const pointer = rs.byteSliceFrom(cell.v); + const old2 = rs.byteSliceToArray(pointer); + rs.byteSliceSet(pointer, _79); + const old: Uint8Array = old2; + const _81: Uint8Array = copy$array_u8(make$conversions_Value_array40_u8(data, 11)); + rs.byteSliceSet(rs.byteSliceFrom(cell.v), _81); put$conversions_Value_array40_u8(rs.byteSliceFrom(old), out); put$conversions_Value_array40_u8(rs.byteSliceFrom(c), out); put$conversions_Value_array40_u8(rs.byteSliceFrom(setThrough$array40_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), make$conversions_Value_array40_u8(data, 12))), out); put$conversions_Value_array40_u8(rs.byteSliceFrom(v.items[rs.idx(v.items, 0)]), out); - rs.cellSet(rs.cellIn(rs.byteSliceView(rs.byteSliceFrom(held.value))), copy$array_u8(make$conversions_Value_array40_u8(data, 17))); + const _96: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(held.value)))); + const _98: Uint8Array = copy$array_u8(make$conversions_Value_array40_u8(data, 17)); + rs.byteSliceSet(rs.byteSliceFrom(_96.v), _98); put$conversions_Value_array40_u8(rs.byteSliceFrom(held.value), out); const array: Uint8Array[] = [copy$array_u8(make$conversions_Value_array40_u8(data, 13)), copy$array_u8(make$conversions_Value_array40_u8(data, 14)), copy$array_u8(make$conversions_Value_array40_u8(data, 15))]; rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(array).length, rs.sliceFrom(array).length], (n: rs.usize): rs.u8 => { @@ -2940,9 +3136,17 @@ export function converted$conversions_Logged(data: rs.ByteSlice, out: rs.ByteVec old7[Symbol.dispose](); Logged.put$conversions_Value(copied, out); Logged.put$conversions_Value(a, out); - const cell: rs.Cell = rs.cellIn(c); - const old: Logged.Logged = rs.cellReplace(cell, Logged.make$conversions_Value(data, 10)); - rs.cellSet(cell, Logged.make$conversions_Value(data, 11), rs.drop); + const cell: rs.Cell = rs.cellOfPlace(rs.partPlace(c)); + const _79: Logged.Logged = Logged.make$conversions_Value(data, 10); + const pointer = cell.v; + const old8 = rs.objectCopy(pointer); + rs.ptrWrite(pointer, _79); + const old: Logged.Logged = old8; + const _81: Logged.Logged = Logged.make$conversions_Value(data, 11); + const pointer2 = cell.v; + const old9 = rs.objectCopy(pointer2); + rs.ptrWrite(pointer2, _81); + old9[Symbol.dispose](); Logged.put$conversions_Value(old, out); Logged.put$conversions_Value(c, out); const at5 = rs.idx(v.items, 0); @@ -2951,7 +3155,12 @@ export function converted$conversions_Logged(data: rs.ByteSlice, out: rs.ByteVec Logged.put$conversions_Value(_88, out); _88[Symbol.dispose](); Logged.put$conversions_Value(v.items[rs.idx(v.items, 0)], out); - rs.cellSet(rs.cellIn(held.value), Logged.make$conversions_Value(data, 17), rs.drop); + const _96: rs.Cell = rs.cellOfPlace(rs.partPlace(held.value)); + const _98: Logged.Logged = Logged.make$conversions_Value(data, 17); + const pointer3 = _96.v; + const old10 = rs.objectCopy(pointer3); + rs.ptrWrite(pointer3, _98); + old10[Symbol.dispose](); Logged.put$conversions_Value(held.value, out); const array: Logged.Logged[] = [Logged.make$conversions_Value(data, 13), Logged.make$conversions_Value(data, 14), Logged.make$conversions_Value(data, 15)]; rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(array).length, rs.sliceFrom(array).length], (n: rs.usize): rs.u8 => { @@ -3046,16 +3255,23 @@ export function converted$conversions_Named(data: rs.ByteSlice, out: rs.ByteVec) a = Named.clone$Clone(copied); Named.put$conversions_Value(copied, out); Named.put$conversions_Value(a, out); - const cell: rs.Cell = rs.cellIn(c); - const old: Named.Named = rs.cellReplace(cell, Named.make$conversions_Value(data, 10)); - rs.cellSet(cell, Named.make$conversions_Value(data, 11)); + const cell: rs.Cell = rs.cellOfPlace(rs.partPlace(c)); + const _79: Named.Named = Named.make$conversions_Value(data, 10); + const pointer = cell.v; + const old2 = rs.objectCopy(pointer); + rs.ptrWrite(pointer, _79); + const old: Named.Named = old2; + const _81: Named.Named = Named.make$conversions_Value(data, 11); + rs.ptrWrite(cell.v, _81); Named.put$conversions_Value(old, out); Named.put$conversions_Value(c, out); const at5 = rs.idx(v.items, 0); const _91: Named.Named = Named.make$conversions_Value(data, 12); Named.put$conversions_Value(setThrough$conversions_Named(v.items[at5], _91), out); Named.put$conversions_Value(v.items[rs.idx(v.items, 0)], out); - rs.cellSet(rs.cellIn(held.value), Named.make$conversions_Value(data, 17)); + const _96: rs.Cell = rs.cellOfPlace(rs.partPlace(held.value)); + const _98: Named.Named = Named.make$conversions_Value(data, 17); + rs.ptrWrite(_96.v, _98); Named.put$conversions_Value(held.value, out); const array: Named.Named[] = [Named.make$conversions_Value(data, 13), Named.make$conversions_Value(data, 14), Named.make$conversions_Value(data, 15)]; rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(array).length, rs.sliceFrom(array).length], (n: rs.usize): rs.u8 => { diff --git a/ts/fixtures/lang/locals/expected/lib.ts b/ts/fixtures/lang/locals/expected/lib.ts index 4d402e1fd..99773d02a 100644 --- a/ts/fixtures/lang/locals/expected/lib.ts +++ b/ts/fixtures/lang/locals/expected/lib.ts @@ -12,6 +12,10 @@ export function copy$tuple2_locals_Twin_locals_Twin(v: [Twin.Twin, Twin.Twin]): return [copy$locals_Twin(v[0]), copy$locals_Twin(v[1])]; } +export function copy$tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs.u8] { + return [v[0], v[1]]; +} + /** * Each case's name and what it runs. The first two bytes, big-endian, pick one modulo * their count, so a case added at the end moves no other. diff --git a/ts/fixtures/lang/locals/expected/locals.ts b/ts/fixtures/lang/locals/expected/locals.ts index 0e82c8bd5..6dcc73831 100644 --- a/ts/fixtures/lang/locals/expected/locals.ts +++ b/ts/fixtures/lang/locals/expected/locals.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$locals_Twin } from "./lib.ts"; +import { copy$locals_Twin, copy$tuple2_u8_u8 } from "./lib.ts"; import { thenSome$1qfl7tz } from "./core/bool.ts"; import { mapOr$14nyq77 as option_mapOr$14nyq77, mapOr$0543jy8 as option_mapOr$0543jy8, mapOr$1idpb1d as option_mapOr$1idpb1d } from "./core/option.ts"; import { mapOr$12d9lqr as result_mapOr$12d9lqr } from "./core/result.ts"; @@ -258,13 +258,14 @@ export function serial(reset: boolean): rs.u8 { return ((next: rs.Cell<[rs.u8, rs.u8]>): rs.u8 => { let _4!: [rs.u8, rs.u8]; if (!reset) { - _4 = next.v; + _4 = copy$tuple2_u8_u8(next.v); } else { _4 = [0, 4] as [rs.u8, rs.u8]; } const n: rs.u8 = _4[0]; const end: rs.u8 = _4[1]; - rs.cellSet(next, [rs.u8WrappingAdd(n, !reset ? 1 : 0), end] as [rs.u8, rs.u8]); + const _6: [rs.u8, rs.u8] = [rs.u8WrappingAdd(n, !reset ? 1 : 0), end]; + rs.ptrWrite(next.v, _6); return n; })(NEXT$serial.get()); } diff --git a/ts/fixtures/lang/manual/expected/lib.ts b/ts/fixtures/lang/manual/expected/lib.ts index 02a257938..785afff75 100644 --- a/ts/fixtures/lang/manual/expected/lib.ts +++ b/ts/fixtures/lang/manual/expected/lib.ts @@ -19,6 +19,10 @@ export function copy$tuple2_u8_u16(v: [rs.u8, rs.u16]): [rs.u8, rs.u16] { return [v[0], v[1]]; } +export function copy$tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs.u8] { + return [v[0], v[1]]; +} + /** * Each case's name and what it runs. The first two bytes, big-endian, pick one modulo * their count, so a case added at the end moves no other. diff --git a/ts/fixtures/lang/manual/expected/uninit.Counted.ts b/ts/fixtures/lang/manual/expected/uninit.Counted.ts index af84b7397..58f6989b4 100644 --- a/ts/fixtures/lang/manual/expected/uninit.Counted.ts +++ b/ts/fixtures/lang/manual/expected/uninit.Counted.ts @@ -1,6 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; +import { copy$tuple2_u8_u8 } from "./lib.ts"; /** Counts its clones and drops, so a write that clones in shows it drops nothing. */ export class Counted { @@ -18,16 +19,22 @@ export class Counted { } export function clone$Clone(self: Counted): Counted { - const _4: [rs.u8, rs.u8] = self._1.v; + const _4: [rs.u8, rs.u8] = copy$tuple2_u8_u8(self._1.v); const c: rs.u8 = _4[0]; const d: rs.u8 = _4[1]; - rs.cellSet(self._1, [rs.u8WrappingAdd(c, 1), d] as [rs.u8, rs.u8]); + const _11: rs.Cell<[rs.u8, rs.u8]> = self._1; + const _6: [rs.u8, rs.u8] = [rs.u8WrappingAdd(c, 1), d]; + rs.ptrWrite(_11.v, _6); return new Counted(rs.u8WrappingAdd(self._0, 1), self._1); } export function drop$ops_Drop(self: Counted): void { - const _4: [rs.u8, rs.u8] = self._1.v; - rs.cellSet(self._1, [_4[0], rs.u8WrappingAdd(_4[1], 1)] as [rs.u8, rs.u8]); + const _4: [rs.u8, rs.u8] = copy$tuple2_u8_u8(self._1.v); + const c: rs.u8 = _4[0]; + const d: rs.u8 = _4[1]; + const _9: rs.Cell<[rs.u8, rs.u8]> = self._1; + const _6: [rs.u8, rs.u8] = [c, rs.u8WrappingAdd(d, 1)]; + rs.ptrWrite(_9.v, _6); } //# sourceMappingURL=uninit.Counted.ts.map diff --git a/ts/fixtures/lang/manual/expected/uninit.ts b/ts/fixtures/lang/manual/expected/uninit.ts index 9bade7812..0f9bd58e0 100644 --- a/ts/fixtures/lang/manual/expected/uninit.ts +++ b/ts/fixtures/lang/manual/expected/uninit.ts @@ -1,6 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; +import { copy$tuple2_u8_u8 } from "./lib.ts"; import { map$1yjmt5m as option_map$1yjmt5m } from "./core/option.ts"; import * as Counted from "./uninit.Counted.ts"; @@ -164,13 +165,13 @@ export function boxedUninits(data: rs.ByteSlice, out: rs.ByteVec): void { }); rs.sliceWriteCloneOfSlice(counted, rs.sliceFrom(src2.items), Counted.clone$Clone); rs.sliceWriteCloneOfSlice(counted, rs.sliceFrom(src2.items), Counted.clone$Clone); - rs.byteVecPush(out, counts.v[0]); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[0]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); const counted2: rs.Slice = counted; rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(rs.sliceItems(counted2)), (c: Counted.Counted): rs.u8 => { return c._0; }), undefined, true); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); rs.byteVecExtendFromSlice(out, boxedClone$u8(data)); const _134: rs.Slice = boxedClone$uninit_Counted(counted2); rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(rs.sliceItems(_134)), (c2: Counted.Counted): rs.u8 => { @@ -178,7 +179,7 @@ export function boxedUninits(data: rs.ByteSlice, out: rs.ByteVec): void { }), undefined, true); rs.dropAll(_134); rs.dropAll(counted2); - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(counts.v[0], counts.v[1])), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy$tuple2_u8_u8(counts.v)[0], copy$tuple2_u8_u8(counts.v)[1])), undefined, true); const len: rs.usize = data.length; const p: rs.Place = rs.rawByteSliceOf(rs.byteSliceNewUninit(len)).ptr; rs.ptrCopy(rs.byteSliceAsPtr(data), p, len); diff --git a/ts/fixtures/lang/pointers/caller.ts b/ts/fixtures/lang/pointers/caller.ts index 75a7b2e9a..a325c6541 100644 --- a/ts/fixtures/lang/pointers/caller.ts +++ b/ts/fixtures/lang/pointers/caller.ts @@ -17,14 +17,27 @@ const unsafeCells = [ /** * `pointers::raw_cells` scripts: each operation, then each replace before and after writes - * through pointers to where the cells are kept, and through cells of what a `&mut` borrows. + * through pointers to where the cells are kept, through cells of what a `&mut` borrows, and + * of pointers to no cell beside cells of a `Vec` and a `String`. */ const rawCells = [ - [0, 9, 2, 3, 4, 5, 6, 7], + [0, 8, 9, 2, 3, 4, 5, 6, 7], [1, 249, 3, 251, 0, 11, 4, 252, 2, 3], [2, 10, 18, 4, 12, 20, 3, 1, 0], [5, 13, 21, 0, 3, 5, 253, 4], + [6, 14, 22, 30, 254], [], ].map((bytes) => Uint8Array.of(...op("pointers::raw_cells"), 0, ...bytes)); -export const cases: Uint8Array[] = [...unsafeCells, ...rawCells]; +/** + * `pointers::cell_writes` scripts: each write, then each again with a wrapping operand, and + * cells of what a `&mut` borrows between writes to the others. + */ +const cellWrites = [ + [0, 1, 2, 3, 4, 5, 6, 7], + [8, 249, 250, 3, 252, 13, 1, 0], + [5, 2, 4, 253, 0, 5, 3], + [], +].map((bytes) => Uint8Array.of(...op("pointers::cell_writes"), 0, ...bytes)); + +export const cases: Uint8Array[] = [...unsafeCells, ...rawCells, ...cellWrites]; diff --git a/ts/fixtures/lang/pointers/expected/lib.ts b/ts/fixtures/lang/pointers/expected/lib.ts index 244833de2..5f3085610 100644 --- a/ts/fixtures/lang/pointers/expected/lib.ts +++ b/ts/fixtures/lang/pointers/expected/lib.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays, rawCells } from "./pointers.ts"; +import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays, rawCells, cellWrites } from "./pointers.ts"; import type * as Aimed from "./pointers.Aimed.ts"; import * as Bin from "./pointers.Bin.ts"; import type * as ByCall from "./pointers.ByCall.ts"; @@ -9,7 +9,6 @@ import type * as ByClosure from "./pointers.ByClosure.ts"; import type * as ByObject from "./pointers.ByObject.ts"; import type * as Duo from "./pointers.Duo.ts"; import type * as Halves from "./pointers.Halves.ts"; -import * as Held from "./pointers.Held.ts"; import type * as Housed from "./pointers.Housed.ts"; import type * as Inner from "./pointers.Inner.ts"; import type * as Leaning from "./pointers.Leaning.ts"; @@ -117,10 +116,6 @@ export function copy$pointers_Halves(v: Halves.Halves): Halves.Halves { return ({ at0: copy$mem_MaybeUninit_array_u32(v.at0) } as unknown) as Halves.Halves; } -export function copy$pointers_Held(v: Held.Held): Held.Held { - return new Held.Held(v.n, v.count); -} - export function copy$pointers_Inner(v: Inner.Inner): Inner.Inner { return { a: v.a, b: v.b }; } @@ -300,7 +295,24 @@ export function cases(): rs.Vec<[string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.By arrays(data27, out27); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::raw_cells", (_237: rs.u8, data28: rs.ByteSlice, out28: rs.ByteVec): undefined => { rawCells(data28, out28); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::cell_writes", (_238: rs.u8, data29: rs.ByteSlice, out29: rs.ByteVec): undefined => { + cellWrites(data29, out29); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]]); } +/** + * `UnsafeCell::get` and `raw_get` of a cell of a slice, which only a caller can give, as + * translated code can't unsize a cell. + */ +export function cellSlice(c: rs.Cell>, at: rs.usize): rs.u16 { + const _4: rs.RawSlice = rs.rawSliceOf(c.v); + rs.idx(_4, at); + const slice = _4.deref(); + const _3: rs.u16 = slice.data[slice.start + at]; + const _9: rs.RawSlice = rs.rawSliceOf(c.v); + rs.idx(_9, 0); + const slice2 = _9.deref(); + return _3 ^ slice2.data[slice2.start]; +} + //# sourceMappingURL=lib.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.Slots.ts b/ts/fixtures/lang/pointers/expected/pointers.Slots.ts index 98c40ec82..4b0db4c2d 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.Slots.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.Slots.ts @@ -25,7 +25,7 @@ export class Slots { /** The pointer to the count, through a pointer to the struct. */ export function num(this_: Slots): rs.Place { - return this_.num; + return rs.rawUnsize(rs.place(() => this_.num, (value) => this_.num = value), (p: rs.Place>) => p.v); } //# sourceMappingURL=pointers.Slots.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 8b13f9fc9..5f3b0cf2a 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_tuple2_u8_u8, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Halves, copy$pointers_Held, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u16, copy$tuple2_u8_u8, drop$pointers_List_String, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; +import { copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_tuple2_u8_u8, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Halves, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u16, copy$tuple2_u8_u8, drop$pointers_List_String, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; import { then$0bgt67i, then$1a5xgoh, then$0jw3xip } from "./core/bool.ts"; import { isSomeAnd$1s2u5lz, isSomeAnd$1nosv3e, isSomeAnd$07atiew, unwrapOr$0mkoact as option_unwrapOr$0mkoact, unwrapOr$179xo1w as option_unwrapOr$179xo1w, unwrapOr$1fr498u as option_unwrapOr$1fr498u, map$1cfrse5 as option_map$1cfrse5, mapOr$078vnqn as option_mapOr$078vnqn, mapOr$00zno7m as option_mapOr$00zno7m, mapOr$173j2gr as option_mapOr$173j2gr, filter$0wvwd70 } from "./core/option.ts"; import type * as Aimed from "./pointers.Aimed.ts"; @@ -234,7 +234,7 @@ export function places(data: rs.ByteSlice, out: rs.ByteVec): void { } else if (_21 === 10) { const _46: Pair.Pair = { a: v, b: rs.u32Add(v, 1) }; const pointer2 = pp; - const old3 = copy$pointers_Pair(pointer2); + const old3 = rs.objectCopy(pointer2); rs.ptrWrite(pointer2, _46); old3; } else if (_21 === 11) { @@ -2986,8 +2986,9 @@ export function lent(n: rs.u8): rs.ByteVec { * pointer to a variable's cell, to a field through a pointer to its struct, and from * `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, * an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a - * pointer taken before sees, giving back what it held undropped; and what - * `as_ref_unchecked` and `as_mut_unchecked` lend. + * pointer taken before sees, giving back what it held undropped; what `as_ref_unchecked` + * and `as_mut_unchecked` lend; `raw_get` of pointers to no cell; and cells of a `Vec` and + * a `String` a `&mut` borrows, replaced and swapped through `get`. */ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const count: rs.Arc = new rs.Arc(undefined); @@ -3040,18 +3041,18 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_110.data[_110.start + 1]).reverse()), undefined, true); } else if (_41 === 1) { const p: rs.Place> = rs.place(() => num, (value5) => num = value5); - const _115: rs.u32 = rs.u32WrappingMul(p.v.v, ((n as rs.u32) | 1) >>> 0); - p.v.v = _115; + const _115: rs.u32 = rs.u32WrappingMul(rs.rawUnsize(p, (p10: rs.Place>) => p10.v).v, ((n as rs.u32) | 1) >>> 0); + rs.rawUnsize(p, (p11: rs.Place>) => p11.v).v = _115; const s: Slots.Slots = slots; const _122: rs.u32 = n as rs.u32; const _123: rs.Place = Slots.num(s); _123.v = rs.u32Add(_123.v, _122); - const _125: Held.Held = s.held.v; + const _125: Held.Held = rs.rawUnsize(rs.place(() => s.held, (value6) => s.held = value6), (p12: rs.Place>) => p12.v.v); _125.n = _125.n ^ n; - const _127: [rs.u8, rs.u16] = s.pair.v; + const _127: [rs.u8, rs.u16] = rs.rawUnsize(rs.place(() => s.pair, (value7) => s.pair = value7), (p13: rs.Place>) => p13.v.v); _127[0] = _127[0] ^ n; const _129: rs.u32 = n as rs.u32; - const _130: rs.Place = { v: slot }.v; + const _130: rs.Place = rs.rawUnsize({ v: slot }, (p14: rs.Place>) => p14.v); _130.v = (_130.v ^ _129) >>> 0; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); @@ -3079,51 +3080,54 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { } else if (_41 === 3) { const p2: rs.Place = num; const _193: rs.u32 = ((n as rs.u32) << 3) >>> 0; - const pointer5 = num; - const old14 = pointer5.v; - pointer5.v = _193; - rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old14).reverse()), undefined, true); + const pointer8 = num; + const old19 = pointer8.v; + pointer8.v = _193; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old19).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(p2.v).reverse()), undefined, true); const p3: Held.Held = held.v; - const _206: Held.Held = new Held.Held(n, rs.arcClone(count)); - const pointer6 = held.v; - const old15 = copy$pointers_Held(pointer6); - rs.ptrWrite(pointer6, _206); - const old2: Held.Held = old15; + const _207: rs.Arc = rs.arcClone(count); + const _206: Held.Held = new Held.Held(n, _207); + const pointer9 = held.v; + const old20 = rs.objectCopy(pointer9); + rs.ptrWrite(pointer9, _206); + const old2: Held.Held = old20; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old2.n, p3.n, count.strong & 0xff)), undefined, true); old2[Symbol.dispose](); rs.byteVecPush(out, count.strong & 0xff); const p4: [rs.u8, rs.u16] = pair.v; const _225: [rs.u8, rs.u16] = [n, rs.u16Mul(n as rs.u16, 3)]; - const pointer7 = pair.v; - const old16 = copy$tuple2_u8_u16(pointer7); - rs.ptrWrite(pointer7, _225); - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old16[0], p4[0])), undefined, true); + const pointer10 = pair.v; + const old21 = rs.objectCopy(pointer10); + rs.ptrWrite(pointer10, _225); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old21[0], p4[0])), undefined, true); const p5: rs.ByteSlice = rs.byteSliceFrom(bytes.v); - const pointer8 = rs.byteSliceFrom(bytes.v); - const old17 = rs.byteSliceToArray(pointer8); - rs.byteSliceSet(pointer8, Uint8Array.of(n, ~n & 0xff)); - rs.iterExtendBytes(out, rs.iterSlice(old17), undefined, true); + const _237: Uint8Array = Uint8Array.of(n, ~n & 0xff); + const pointer11 = rs.byteSliceFrom(bytes.v); + const old22 = rs.byteSliceToArray(pointer11); + rs.byteSliceSet(pointer11, _237); + rs.iterExtendBytes(out, rs.iterSlice(old22), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceToArray(p5)), undefined, true); - const pointer9 = duo.v; - const old18 = copy$pointers_Duo(pointer9); - rs.ptrWrite(pointer9, { _0: n, _1: n ^ 1 }); - const old5: Duo.Duo = old18; + const _244: Duo.Duo = { _0: n, _1: n ^ 1 }; + const pointer12 = duo.v; + const old23 = rs.objectCopy(pointer12); + rs.ptrWrite(pointer12, _244); + const old5: Duo.Duo = old23; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old5._0, old5._1, duo.v._0)), undefined, true); } else if (_41 === 4) { const _255: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value) => local = value)); const _257: rs.u32 = n as rs.u32; - const pointer2 = _255; - const old11 = pointer2.v; - pointer2.v = _257; - rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old11).reverse()), undefined, true); + const pointer5 = _255; + const old16 = pointer5.v; + pointer5.v = _257; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old16).reverse()), undefined, true); const h2: rs.Cell = rs.cellOfPlace(rs.partPlace(whole)); const p6: Held.Held = h2.v; const _266: Held.Held = new Held.Held(~n & 0xff, rs.arcClone(count)); - const pointer3 = h2.v; - const old12 = copy$pointers_Held(pointer3); - rs.ptrWrite(pointer3, _266); - const old7: Held.Held = old12; + const pointer6 = h2.v; + const old17 = rs.objectCopy(pointer6); + rs.ptrWrite(pointer6, _266); + const old7: Held.Held = old17; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old7.n, p6.n, whole.n, count.strong & 0xff)), undefined, true); const _282: rs.usize = rs.u8Rem(n, 3) as rs.usize; if (!(_282 < 3)) { @@ -3132,10 +3136,10 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const borrowed = elems; const _280: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_282], (value2) => borrowed[_282] = value2)); const _286: rs.u16 = n as rs.u16; - const pointer4 = _280; - const old13 = pointer4.v; - pointer4.v = _286; - rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old13).reverse()), undefined, true); + const pointer7 = _280; + const old18 = pointer7.v; + pointer7.v = _286; + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old18).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(local).reverse()), undefined, true); old7[Symbol.dispose](); } else if (_41 === 5) { @@ -3151,7 +3155,8 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { _302[1] = _302[1] ^ _301; const _304: rs.ByteSlice = rs.byteSliceFrom(bytes.v); _304.data[_304.start + 1] = _304.data[_304.start + 1] ^ n; - rs.sliceSet(rs.sliceFrom(wide.v), [n as rs.u16, 1]); + const _308: rs.u16 = n as rs.u16; + rs.sliceSet(rs.sliceFrom(wide.v), [_308, 1]); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(held.v.n, duo.v._0)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(pair.v[1]).reverse()), undefined, true); @@ -3166,19 +3171,54 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _344); } _342.data[_342.start + _344] = _342.data[_342.start + _344] ^ 64; - const pointer = rs.byteSliceFrom(a.v); - const old10 = rs.byteSliceToArray(pointer); - rs.byteSliceSet(pointer, Uint8Array.of(3, n, 4)); - rs.iterExtendBytes(out, rs.iterSlice(old10), undefined, true); + const _350: Uint8Array = Uint8Array.of(3, n, 4); + const pointer4 = rs.byteSliceFrom(a.v); + const old15 = rs.byteSliceToArray(pointer4); + rs.byteSliceSet(pointer4, _350); + rs.iterExtendBytes(out, rs.iterSlice(old15), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(copy$array_u8(arr)), undefined, true); + } else if (_41 === 6) { + const null_: rs.Place> = rs.NULL; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(rs.rawUnsize(null_, (p7: rs.Place>) => p7.v) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.NULL, (p8: rs.Place>) => p8.v.v) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.ptrWrappingAdd(null_, rs.u8Rem(n, 4) as rs.usize, 4, 4), (p9: rs.Place>) => p9.v) === rs.NULL ? 1 : 0)), undefined, true); + const list: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(n, 1)); + const other: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(2)); + const text: rs.String = rs.stringFrom("ab"); + const c2: rs.Cell = rs.cellOfPlace(rs.partPlace(list)); + const _381: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(n ^ 1, 3, 4)); + const pointer = c2; + const old11 = rs.objectCopy(pointer.v); + pointer.v = _381; + const old10: rs.ByteVec = old11; + const x = c2; + const y = rs.cellOfPlace(rs.partPlace(other)); + const old12 = rs.objectCopy(x.v); + x.v = rs.objectCopy(y.v); + y.v = old12; + const _392: rs.Place = c2; + const _394: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(5)); + const pointer2 = _392; + const old13 = rs.objectCopy(pointer2.v); + pointer2.v = _394; + const swapped: rs.ByteVec = old13; + const _398: rs.Cell = rs.cellOfPlace(rs.partPlace(text)); + const _400: rs.String = rs.fmtToString((f: rs.Formatter) => rs.u8Display(n, f), 0); + const pointer3 = _398; + const old14 = rs.objectCopy(pointer3.v); + pointer3.v = _400; + const was: rs.String = old14; + rs.iterExtendBytes(out, rs.byteVecIntoIter(old10), undefined, true); + rs.iterExtendBytes(out, rs.byteVecIntoIter(swapped), undefined, true); + rs.iterExtendBytes(out, rs.byteVecIntoIter(other), undefined, true); + rs.iterExtendBytes(out, rs.byteVecIntoIter(list), undefined, true); + rs.iterExtendBytes(out, rs.iterChain(rs.strBytes(was.v), rs.strBytes(text.v)), undefined, true); } else { rs.byteVecPush(out, n); } } rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); - const _361: Held.Held = held.v; - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_361.n, count.strong & 0xff)), undefined, true); - _361[Symbol.dispose](); + const _423: Held.Held = held.v; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_423.n, count.strong & 0xff)), undefined, true); + _423[Symbol.dispose](); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); slots.held[Symbol.dispose](); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(whole.n, count.strong & 0xff)), undefined, true); @@ -3189,4 +3229,202 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { count[Symbol.dispose](); } +/** + * What `Cell`s of a tuple, a struct Rust copies, one that owns a count, and arrays of bytes + * and of numbers, and cells `Cell::from_mut` makes of a tuple and an array, hold after + * `replace`, `set`, `swap`, `take`, and `update`, each read back through a pointer + * `as_ptr` gave before, and what each gives back and drops. + */ +export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { + const count: rs.Arc = new rs.Arc(undefined); + const held: (a0: rs.u8) => Held.Held = (n2: rs.u8): Held.Held => { + return new Held.Held(n2, rs.arcClone(count)); + }; + const pair: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([1, 2] as [rs.u8, rs.u16]); + const duo: rs.Cell = new rs.Cell({ _0: 3, _1: 4 }); + const owner: rs.Cell = new rs.Cell(held(5)); + const bytes: rs.Cell = new rs.Cell(Uint8Array.of(6, 7)); + const wide: rs.Cell = new rs.Cell([8, 9]); + const _21: [[rs.u8, rs.u16], Duo.Duo, Held.Held] = [pair.v, duo.v, owner.v]; + const toPair: [rs.u8, rs.u16] = _21[0]; + const toDuo: Duo.Duo = _21[1]; + const toOwner: Held.Held = _21[2]; + const _30: [rs.ByteSlice, rs.Slice] = [rs.byteSliceFrom(bytes.v), rs.sliceFrom(wide.v)]; + const toBytes: rs.ByteSlice = _30[0]; + const toWide: rs.Slice = _30[1]; + const lent: [rs.u8, rs.u16] = [10, 11]; + const lentBytes: Uint8Array = Uint8Array.of(12, 13); + for (const op of rs.byteSliceView(data)) { + const n: rs.u8 = rs.u8Div(op, 8); + const _46: rs.u8 = rs.u8Rem(op, 8); + if (_46 === 0) { + const _50: [rs.u8, rs.u16] = [n, ((n as rs.u16) << 8) & 0xffff]; + const pointer14 = pair.v; + const old17 = rs.objectCopy(pointer14); + rs.ptrWrite(pointer14, _50); + const _57: rs.u8 = old17[0]; + const _61: Duo.Duo = { _0: n, _1: ~n & 0xff }; + const pointer15 = duo.v; + const old18 = rs.objectCopy(pointer15); + rs.ptrWrite(pointer15, _61); + const _58: rs.u8 = old18._1; + const _66: Held.Held = held(n); + const pointer16 = owner.v; + const old19 = rs.objectCopy(pointer16); + rs.ptrWrite(pointer16, _66); + const _64: Held.Held = old19; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_57, _58, _64.n)), undefined, true); + _64[Symbol.dispose](); + const _72: Uint8Array = Uint8Array.of(n, 1); + const pointer17 = rs.byteSliceFrom(bytes.v); + const old20 = rs.byteSliceToArray(pointer17); + rs.byteSliceSet(pointer17, _72); + rs.iterExtendBytes(out, rs.iterSlice(old20), undefined, true); + const _78: rs.u16[] = [n as rs.u16, 2]; + const pointer18 = rs.sliceFrom(wide.v); + const old21 = rs.objectCopy(rs.sliceView(pointer18)); + rs.sliceSet(pointer18, _78); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old21[1]).reverse()), undefined, true); + } else if (_46 === 1) { + rs.ptrWrite(pair.v, [n ^ 1, 3] as [rs.u8, rs.u16]); + rs.ptrWrite(duo.v, { _0: n ^ 2, _1: 4 }); + const _92: Held.Held = held(n ^ 3); + const pointer13 = owner.v; + const old16 = rs.objectCopy(pointer13); + rs.ptrWrite(pointer13, _92); + old16[Symbol.dispose](); + const _98: Uint8Array = Uint8Array.of(n ^ 4, 5); + rs.byteSliceSet(rs.byteSliceFrom(bytes.v), _98); + const _102: rs.u16[] = [6, ((n as rs.u16) << 4) & 0xffff]; + rs.sliceSet(rs.sliceFrom(wide.v), _102); + } else if (_46 === 2) { + const _110: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([n, 7] as [rs.u8, rs.u16]); + const x2 = pair.v; + const y2 = _110.v; + const old11 = rs.objectCopy(x2); + rs.ptrWrite(x2, rs.objectCopy(y2)); + rs.ptrWrite(y2, old11); + const other: rs.Cell = new rs.Cell({ _0: 8, _1: n }); + const x3 = duo.v; + const y3 = other.v; + const old12 = rs.objectCopy(x3); + rs.ptrWrite(x3, rs.objectCopy(y3)); + rs.ptrWrite(y3, old12); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy$pointers_Duo(other.v)._0, copy$pointers_Duo(other.v)._1)), undefined, true); + const other2: rs.Cell = new rs.Cell(held(n ^ 5)); + const x4 = owner.v; + const y4 = other2.v; + const old13 = rs.objectCopy(x4); + rs.ptrWrite(x4, rs.objectCopy(y4)); + rs.ptrWrite(y4, old13); + const _135: Held.Held = other2.v; + rs.byteVecPush(out, _135.n); + _135[Symbol.dispose](); + const x5 = rs.byteSliceFrom(bytes.v); + const y5 = rs.byteSliceFrom(bytes.v); + const old14 = rs.byteSliceToArray(x5); + rs.byteSliceSet(x5, rs.byteSliceToArray(y5)); + rs.byteSliceSet(y5, old14); + const _143: rs.Cell = new rs.Cell([n as rs.u16, 9]); + const x6 = rs.sliceFrom(wide.v); + const y6 = rs.sliceFrom(_143.v); + const old15 = rs.objectCopy(rs.sliceView(x6)); + rs.sliceSet(x6, rs.objectCopy(rs.sliceView(y6))); + rs.sliceSet(y6, old15); + } else if (_46 === 3) { + const pointer10 = pair.v; + const old8 = rs.objectCopy(pointer10); + rs.ptrWrite(pointer10, [0, 0]); + rs.byteVecPush(out, old8[0]); + const pointer11 = rs.byteSliceFrom(bytes.v); + const old9 = rs.byteSliceToArray(pointer11); + rs.byteSliceSet(pointer11, rs.bytes(2)); + rs.iterExtendBytes(out, rs.iterSlice(old9), undefined, true); + const pointer12 = rs.sliceFrom(wide.v); + const old10 = rs.objectCopy(rs.sliceView(pointer12)); + rs.sliceSet(pointer12, Array.from({ length: 2 }, (): rs.u16 => { + return 0; + })); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old10[0]).reverse()), undefined, true); + } else if (_46 === 4) { + const _162: (a0: [rs.u8, rs.u16]) => [rs.u8, rs.u16] = (_25: [rs.u8, rs.u16]): [rs.u8, rs.u16] => { + const a3: rs.u8 = _25[0]; + const b3: rs.u16 = _25[1]; + return [rs.u8WrappingAdd(a3, n), b3 ^ 1] as [rs.u8, rs.u16]; + }; + const pointer6 = pair.v; + rs.ptrWrite(pointer6, _162(copy$tuple2_u8_u16(pointer6))); + const _166: (a0: Duo.Duo) => Duo.Duo = (_27: Duo.Duo): Duo.Duo => { + return { _0: _27._1, _1: _27._0 ^ n }; + }; + const pointer7 = duo.v; + rs.ptrWrite(pointer7, _166(copy$pointers_Duo(pointer7))); + const _170: (a0: Uint8Array) => Uint8Array = (_28: Uint8Array): Uint8Array => { + return Uint8Array.of(_28[1], rs.u8WrappingMul(_28[0], n)); + }; + const pointer8 = rs.byteSliceFrom(bytes.v); + rs.byteSliceSet(pointer8, _170(rs.byteSliceToArray(pointer8))); + const _174: (a0: rs.u16[]) => rs.u16[] = (_29: rs.u16[]): rs.u16[] => { + const a6: rs.u16 = _29[0]; + return [rs.intRotateLeft(_29[1], n as rs.u32, 16, false), a6]; + }; + const pointer9 = rs.sliceFrom(wide.v); + rs.sliceSet(pointer9, _174(rs.sliceToArray(pointer9))); + } else if (_46 === 5) { + const c: rs.Cell<[rs.u8, rs.u16]> = rs.cellOfPlace(rs.partPlace(lent)); + const p: [rs.u8, rs.u16] = c.v; + rs.ptrWrite(c.v, [n, 1] as [rs.u8, rs.u16]); + const _182: [rs.u8, rs.u16] = [n ^ 1, 2]; + const pointer = c.v; + const old4 = rs.objectCopy(pointer); + rs.ptrWrite(pointer, _182); + const old2: [rs.u8, rs.u16] = old4; + const _186: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([n ^ 2, 3] as [rs.u8, rs.u16]); + const x = c.v; + const y = _186.v; + const old5 = rs.objectCopy(x); + rs.ptrWrite(x, rs.objectCopy(y)); + rs.ptrWrite(y, old5); + const pointer2 = c.v; + rs.ptrWrite(pointer2, ((_2: [rs.u8, rs.u16]): [rs.u8, rs.u16] => { + return [_2[0] ^ 4, _2[1]] as [rs.u8, rs.u16]; + })(copy$tuple2_u8_u16(pointer2))); + const _192: rs.u8 = old2[0]; + const _193: rs.u8 = p[0]; + const pointer3 = c.v; + const old6 = rs.objectCopy(pointer3); + rs.ptrWrite(pointer3, [0, 0]); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_192, _193, old6[0])), undefined, true); + const c2: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(lentBytes)))); + const p2: rs.ByteSlice = rs.byteSliceFrom(c2.v); + const _200: Uint8Array = Uint8Array.of(n, 5); + const pointer4 = rs.byteSliceFrom(c2.v); + const old7 = rs.byteSliceToArray(pointer4); + rs.byteSliceSet(pointer4, _200); + const old3: Uint8Array = old7; + const pointer5 = rs.byteSliceFrom(c2.v); + rs.byteSliceSet(pointer5, ((_23: Uint8Array): Uint8Array => { + return Uint8Array.of(_23[1], _23[0]); + })(rs.byteSliceToArray(pointer5))); + rs.iterExtendBytes(out, rs.iterSlice(old3), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceToArray(p2)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(lent[0], lentBytes[0])), undefined, true); + } else { + rs.byteVecPush(out, n); + } + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toPair[0], toDuo._0, toDuo._1, toOwner.n)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(toPair[1]).reverse()), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceToArray(toBytes)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(toWide.data[toWide.start + 1]).reverse()), undefined, true); + rs.byteVecPush(out, count.strong & 0xff); + } + const _233: rs.u8 = copy$tuple2_u8_u16(pair.v)[0]; + const _236: rs.u8 = copy$pointers_Duo(duo.v)._1; + const _240: Held.Held = owner.v; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_233, _236, _240.n, copy$array_u8(bytes.v)[1])), undefined, true); + _240[Symbol.dispose](); + rs.byteVecPush(out, count.strong & 0xff); + count[Symbol.dispose](); +} + //# sourceMappingURL=pointers.ts.map diff --git a/ts/fixtures/lang/pointers/src/lib.rs b/ts/fixtures/lang/pointers/src/lib.rs index af7b1979d..216b6eb31 100644 --- a/ts/fixtures/lang/pointers/src/lib.rs +++ b/ts/fixtures/lang/pointers/src/lib.rs @@ -50,5 +50,12 @@ fn cases() -> Vec<(&'static str, Case)> { ("pointers::raw_places", |_, data, out| pointers::raw_places(data, out)), ("pointers::arrays", |_, data, out| pointers::arrays(data, out)), ("pointers::raw_cells", |_, data, out| pointers::raw_cells(data, out)), + ("pointers::cell_writes", |_, data, out| pointers::cell_writes(data, out)), ] } + +/// `UnsafeCell::get` and `raw_get` of a cell of a slice, which only a caller can give, as +/// translated code can't unsize a cell. +pub fn cell_slice(c: &std::cell::UnsafeCell<[u16]>, at: usize) -> u16 { + unsafe { (*std::cell::UnsafeCell::raw_get(c))[at] ^ (*c.get())[0] } +} diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index b1ba472b5..c8c31aa1d 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2430,8 +2430,9 @@ impl Slots { /// pointer to a variable's cell, to a field through a pointer to its struct, and from /// `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, /// an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a -/// pointer taken before sees, giving back what it held undropped; and what -/// `as_ref_unchecked` and `as_mut_unchecked` lend. +/// pointer taken before sees, giving back what it held undropped; what `as_ref_unchecked` +/// and `as_mut_unchecked` lend; `raw_get` of pointers to no cell; and cells of a `Vec` and +/// a `String` a `&mut` borrows, replaced and swapped through `get`. pub fn raw_cells(data: &[u8], out: &mut Vec) { let count = Rc::new(()); let num = UnsafeCell::new(7u32); @@ -2562,6 +2563,31 @@ pub fn raw_cells(data: &[u8], out: &mut Vec) { out.extend(old); out.extend(arr); }, + // Pointers to no cell, which `raw_get` gives back as they are, and cells of what + // a `&mut` borrows that is an object JS writes whole values into, replaced and + // swapped while the value given back is kept. + 6 => unsafe { + let null = ptr::null::>(); + let pair = ptr::null_mut::>(); + out.extend([ + u8::from(UnsafeCell::raw_get(null).is_null()), + u8::from(UnsafeCell::raw_get(pair).is_null()), + u8::from(UnsafeCell::raw_get(null.wrapping_add(usize::from(n % 4))).is_null()), + ]); + let mut list = vec![n, 1]; + let mut other = vec![2]; + let mut text = String::from("ab"); + let c = UnsafeCell::from_mut(&mut list); + let old = c.replace(vec![n ^ 1, 3, 4]); + ptr::swap(c.get(), UnsafeCell::from_mut(&mut other).get()); + let swapped = ptr::replace(c.get(), vec![5]); + let was = UnsafeCell::from_mut(&mut text).replace(n.to_string()); + out.extend(old); + out.extend(swapped); + out.extend(other); + out.extend(list); + out.extend(was.bytes().chain(text.bytes())); + }, _ => out.push(n), } } @@ -2572,3 +2598,90 @@ pub fn raw_cells(data: &[u8], out: &mut Vec) { out.extend([whole.n, Rc::strong_count(&count) as u8]); out.extend(elems.iter().flat_map(|e| e.to_le_bytes())); } + +/// What `Cell`s of a tuple, a struct Rust copies, one that owns a count, and arrays of bytes +/// and of numbers, and cells `Cell::from_mut` makes of a tuple and an array, hold after +/// `replace`, `set`, `swap`, `take`, and `update`, each read back through a pointer +/// `as_ptr` gave before, and what each gives back and drops. +pub fn cell_writes(data: &[u8], out: &mut Vec) { + let count = Rc::new(()); + let held = |n: u8| Held { + n, + count: Rc::clone(&count), + }; + let pair = Cell::new((1u8, 2u16)); + let duo = Cell::new(Duo(3, 4)); + let owner = Cell::new(held(5)); + let bytes = Cell::new([6u8, 7]); + let wide = Cell::new([8u16, 9]); + let (to_pair, to_duo, to_owner) = (pair.as_ptr(), duo.as_ptr(), owner.as_ptr()); + let (to_bytes, to_wide) = (bytes.as_ptr(), wide.as_ptr()); + let mut lent = (10u8, 11u16); + let mut lent_bytes = [12u8, 13]; + for &op in data { + let n = op / 8; + match op % 8 { + 0 => { + let old = pair.replace((n, u16::from(n) << 8)); + out.extend([old.0, duo.replace(Duo(n, !n)).1, owner.replace(held(n)).n]); + out.extend(bytes.replace([n, 1])); + out.extend(wide.replace([u16::from(n), 2])[1].to_le_bytes()); + } + 1 => { + pair.set((n ^ 1, 3)); + duo.set(Duo(n ^ 2, 4)); + owner.set(held(n ^ 3)); + bytes.set([n ^ 4, 5]); + wide.set([6, u16::from(n) << 4]); + } + 2 => { + pair.swap(&Cell::new((n, 7))); + let other = Cell::new(Duo(8, n)); + duo.swap(&other); + out.extend([other.get().0, other.get().1]); + let other = Cell::new(held(n ^ 5)); + owner.swap(&other); + out.push(other.into_inner().n); + bytes.swap(&bytes); + wide.swap(&Cell::new([u16::from(n), 9])); + } + 3 => { + out.push(pair.take().0); + out.extend(bytes.take()); + out.extend(wide.take()[0].to_le_bytes()); + } + 4 => { + pair.update(|(a, b)| (a.wrapping_add(n), b ^ 1)); + duo.update(|Duo(a, b)| Duo(b, a ^ n)); + bytes.update(|[a, b]| [b, a.wrapping_mul(n)]); + wide.update(|[a, b]| [b.rotate_left(u32::from(n)), a]); + } + 5 => unsafe { + let c = Cell::from_mut(&mut lent); + let p = c.as_ptr(); + c.set((n, 1)); + let old = c.replace((n ^ 1, 2)); + c.swap(&Cell::new((n ^ 2, 3))); + c.update(|(a, b)| (a ^ 4, b)); + out.extend([old.0, (*p).0, c.take().0]); + let c = Cell::from_mut(&mut lent_bytes); + let p = c.as_ptr(); + let old = c.replace([n, 5]); + c.update(|[a, b]| [b, a]); + out.extend(old); + out.extend(*p); + out.extend([lent.0, lent_bytes[0]]); + }, + _ => out.push(n), + } + unsafe { + out.extend([(*to_pair).0, (*to_duo).0, (*to_duo).1, (*to_owner).n]); + out.extend((*to_pair).1.to_le_bytes()); + out.extend(*to_bytes); + out.extend((*to_wide)[1].to_le_bytes()); + } + out.push(Rc::strong_count(&count) as u8); + } + out.extend([pair.get().0, duo.get().1, owner.into_inner().n, bytes.get()[1]]); + out.push(Rc::strong_count(&count) as u8); +} diff --git a/ts/fixtures/lang/pointers/ts.toml b/ts/fixtures/lang/pointers/ts.toml index 5cb50e220..cc97ae162 100644 --- a/ts/fixtures/lang/pointers/ts.toml +++ b/ts/fixtures/lang/pointers/ts.toml @@ -1,2 +1,2 @@ package = "lang-pointers-fixture" -roots = ["harness::run", "harness::op"] +roots = ["harness::run", "harness::op", "cell_slice"] diff --git a/ts/fixtures/lang/sorts/expected/lib.ts b/ts/fixtures/lang/sorts/expected/lib.ts index 36b0d81f2..557052212 100644 --- a/ts/fixtures/lang/sorts/expected/lib.ts +++ b/ts/fixtures/lang/sorts/expected/lib.ts @@ -3,6 +3,10 @@ import * as rs from "./rs.ts"; import { sorts, disorders, btrees } from "./sorts.ts"; +export function copy$tuple2_u32_u32(v: [rs.u32, rs.u32]): [rs.u32, rs.u32] { + return [v[0], v[1]]; +} + /** * Each case's name and what it runs. The first two bytes, big-endian, pick one modulo * their count, so a case added at the end moves no other. diff --git a/ts/fixtures/lang/sorts/expected/sorts.ts b/ts/fixtures/lang/sorts/expected/sorts.ts index 5f885871e..a0f877e27 100644 --- a/ts/fixtures/lang/sorts/expected/sorts.ts +++ b/ts/fixtures/lang/sorts/expected/sorts.ts @@ -1,6 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; +import { copy$tuple2_u32_u32 } from "./lib.ts"; import { unwrapOr$1fr498u as option_unwrapOr$1fr498u, mapOr$0z4nmg2 as option_mapOr$0z4nmg2 } from "./core/option.ts"; import type * as Held from "./sorts.Held.ts"; import * as Item from "./sorts.Item.ts"; @@ -14,13 +15,13 @@ export function LOG$init(): rs.Cell<[rs.u32, rs.u32]> { /** Forgets what an earlier case logged, which its panic may have left. */ export function begin(): void { ((log: rs.Cell<[rs.u32, rs.u32]>): undefined => { - rs.cellSet(log, [2166136261, 0]); + rs.ptrWrite(log.v, [2166136261, 0]); })(LOG.get()); } export function note(what: rs.u8, a: rs.u16, b: rs.u16): void { ((log: rs.Cell<[rs.u32, rs.u32]>): undefined => { - const _52: [rs.u32, rs.u32] = log.v; + const _52: [rs.u32, rs.u32] = copy$tuple2_u32_u32(log.v); let hash: rs.u32 = _52[0]; const count: rs.u32 = _52[1]; const iter: rs.Iter = rs.iterSlice(Uint8Array.of(what, a & 0xff, (a >> 8) & 0xff, b & 0xff, (b >> 8) & 0xff)); @@ -31,14 +32,18 @@ export function note(what: rs.u8, a: rs.u16, b: rs.u16): void { } hash = rs.u32WrappingMul((hash ^ _24 as rs.u32) >>> 0, 16777619); } - rs.cellSet(log, [hash, rs.u32Add(count, 1)] as [rs.u32, rs.u32]); + const _33: [rs.u32, rs.u32] = [hash, rs.u32Add(count, 1)]; + rs.ptrWrite(log.v, _33); })(LOG.get()); } /** Puts the log's hash and count, then each tag in order, or a hash of them past 64. */ export function put$Cell_u32(v: rs.Slice>>, out: rs.ByteVec): void { const _5: [rs.u32, rs.u32] = ((log: rs.Cell<[rs.u32, rs.u32]>): [rs.u32, rs.u32] => { - return rs.cellReplace(log, [2166136261, 0]); + const pointer = log.v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, [2166136261, 0]); + return old; })(LOG.get()); const hash: rs.u32 = _5[0]; const count: rs.u32 = _5[1]; @@ -58,7 +63,10 @@ export function put$Cell_u32(v: rs.Slice>>, out: rs.By /** Puts the log's hash and count, then each tag in order, or a hash of them past 64. */ export function put$array10_u32(v: rs.Slice>, out: rs.ByteVec): void { const _5: [rs.u32, rs.u32] = ((log: rs.Cell<[rs.u32, rs.u32]>): [rs.u32, rs.u32] => { - return rs.cellReplace(log, [2166136261, 0]); + const pointer = log.v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, [2166136261, 0]); + return old; })(LOG.get()); const hash: rs.u32 = _5[0]; const count: rs.u32 = _5[1]; @@ -78,7 +86,10 @@ export function put$array10_u32(v: rs.Slice>, out: rs.ByteVe /** Puts the log's hash and count, then each tag in order, or a hash of them past 64. */ export function put$array1100_u32(v: rs.Slice>, out: rs.ByteVec): void { const _5: [rs.u32, rs.u32] = ((log: rs.Cell<[rs.u32, rs.u32]>): [rs.u32, rs.u32] => { - return rs.cellReplace(log, [2166136261, 0]); + const pointer = log.v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, [2166136261, 0]); + return old; })(LOG.get()); const hash: rs.u32 = _5[0]; const count: rs.u32 = _5[1]; @@ -98,7 +109,10 @@ export function put$array1100_u32(v: rs.Slice>, out: rs.Byte /** Puts the log's hash and count, then each tag in order, or a hash of them past 64. */ export function put$array22_u32(v: rs.Slice>, out: rs.ByteVec): void { const _5: [rs.u32, rs.u32] = ((log: rs.Cell<[rs.u32, rs.u32]>): [rs.u32, rs.u32] => { - return rs.cellReplace(log, [2166136261, 0]); + const pointer = log.v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, [2166136261, 0]); + return old; })(LOG.get()); const hash: rs.u32 = _5[0]; const count: rs.u32 = _5[1]; @@ -118,7 +132,10 @@ export function put$array22_u32(v: rs.Slice>, out: rs.ByteVe /** Puts the log's hash and count, then each tag in order, or a hash of them past 64. */ export function put$array30_u32(v: rs.Slice>, out: rs.ByteVec): void { const _5: [rs.u32, rs.u32] = ((log: rs.Cell<[rs.u32, rs.u32]>): [rs.u32, rs.u32] => { - return rs.cellReplace(log, [2166136261, 0]); + const pointer = log.v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, [2166136261, 0]); + return old; })(LOG.get()); const hash: rs.u32 = _5[0]; const count: rs.u32 = _5[1]; @@ -138,7 +155,10 @@ export function put$array30_u32(v: rs.Slice>, out: rs.ByteVe /** Puts the log's hash and count, then each tag in order, or a hash of them past 64. */ export function put$sorts_Held(v: rs.Slice>, out: rs.ByteVec): void { const _5: [rs.u32, rs.u32] = ((log: rs.Cell<[rs.u32, rs.u32]>): [rs.u32, rs.u32] => { - return rs.cellReplace(log, [2166136261, 0]); + const pointer = log.v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, [2166136261, 0]); + return old; })(LOG.get()); const hash: rs.u32 = _5[0]; const count: rs.u32 = _5[1]; @@ -158,7 +178,10 @@ export function put$sorts_Held(v: rs.Slice>, out: rs.ByteVe /** Puts the log's hash and count, then each tag in order, or a hash of them past 64. */ export function put$unit(v: rs.Slice>, out: rs.ByteVec): void { const _5: [rs.u32, rs.u32] = ((log: rs.Cell<[rs.u32, rs.u32]>): [rs.u32, rs.u32] => { - return rs.cellReplace(log, [2166136261, 0]); + const pointer = log.v; + const old = rs.objectCopy(pointer); + rs.ptrWrite(pointer, [2166136261, 0]); + return old; })(LOG.get()); const hash: rs.u32 = _5[0]; const count: rs.u32 = _5[1]; diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index e9a6ad9fc..235dddc76 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -11,6 +11,7 @@ fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept +fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index a2b9ac865..c40eb6ac0 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -15,6 +15,7 @@ fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept +fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index 40b9c29a6..c57b97d78 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -39,6 +39,16 @@ pub fn cell_pointer_kept(c: &std::cell::UnsafeCell, n: u32) -> u32 { unsafe { *std::cell::UnsafeCell::raw_get(p) + y as u32 } } +/// A pointer `UnsafeCell::raw_get` makes of a reference to a cell, read after the variable +/// holding the cell is given another. +pub fn cell_pointer_reassigned(n: u32) -> u32 { + let mut c = std::cell::UnsafeCell::new(n); + let p = std::cell::UnsafeCell::raw_get(&c); + c = std::cell::UnsafeCell::new(n ^ 1); + let _ = &c; + unsafe { *p } +} + /// Casts between pointers to bytes and to values outside memory from `alloc`, which read /// the bytes of one type as another: of an array's bytes to `u32`s, of memory from /// `alloc` to the crate's struct, which JS keeps as an object, and of a pointer to values diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index 3c4180043..9687cf6a7 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -5,6 +5,7 @@ roots = [ "alloc::allocated", "alloc::bytes_as_values", "alloc::cell_pointer_kept", + "alloc::cell_pointer_reassigned", "alloc::reinterpreted", "alloc::vec_pointer", "alloc::zeroed", diff --git a/ts/fixtures/task/expected/drops/called.ts b/ts/fixtures/task/expected/drops/called.ts index 868d8b4f9..96d79fb7c 100644 --- a/ts/fixtures/task/expected/drops/called.ts +++ b/ts/fixtures/task/expected/drops/called.ts @@ -437,7 +437,7 @@ export function called(log: Log.Log, id: rs.u32, arg: rs.u8): void { }); rs.cellUpdate(new rs.Cell(1), rs.once(add)); const pair: rs.Cell = new rs.Cell([1, 2]); - rs.cellUpdate(pair, (p: rs.u32[]): rs.u32[] => { + const _510: (a0: rs.u32[]) => rs.u32[] = (p: rs.u32[]): rs.u32[] => { p = copy$array_u32(p); p[0] = rs.u32Add(p[0], 10); const _155: rs.u32[] = copy$array_u32(pair.v); @@ -446,7 +446,9 @@ export function called(log: Log.Log, id: rs.u32, arg: rs.u8): void { const args76 = [(f: rs.Formatter) => rs.u32Display(part2, f), (f: rs.Formatter) => rs.u32Display(part3, f)]; Log.note(log, rs.fmtFormat(["pair ", args76[0], " ", args76[1]])); return copy$array_u32(p); - }); + }; + const pointer = rs.sliceFrom(pair.v); + rs.sliceSet(pointer, _510(rs.sliceToArray(pointer))); const part4 = rs.sliceFrom(copy$array_u32(pair.v)); Log.note(log, rs.fmtFormat(["paired ", (f: rs.Formatter) => rs.fmtList(rs.sliceItems(part4), f, rs.u32Debug)])); const part5 = rs.refCellReplaceWith(cell2, rs.once(doubled), true); diff --git a/ts/runtime/rs.ts b/ts/runtime/rs.ts index 145f43216..c704a2b2e 100644 --- a/ts/runtime/rs.ts +++ b/ts/runtime/rs.ts @@ -149,9 +149,9 @@ export const NULL: never = null as never; /** A dangling raw pointer, `NonNull::dangling`, of any type: nothing reads through it. */ export const DANGLING: never = /* @__PURE__ */ Object.freeze({}) as never; -/** The raw pointer `p` unsized by `make`, or `p` itself if it is null or dangling, which point at nothing. */ +/** The raw pointer `p` unsized or projected by `make`, or `p` itself if it points at no memory. */ export function rawUnsize(p: P, make: (p: P) => Q): Q { - return p === NULL || p === DANGLING ? (p as never) : make(p); + return ptrNowhere(p as object) ? (p as never) : make(p); } /** diff --git a/ts/runtime/rs/task.ts b/ts/runtime/rs/task.ts index 9a14842ed..4507456ba 100644 --- a/ts/runtime/rs/task.ts +++ b/ts/runtime/rs/task.ts @@ -912,13 +912,12 @@ export function cellOfPlace(p: Place): Cell { } /** - * `Cell::from_mut` of the object or the bytes a `&mut` is: a read is a copy, as a `Cell` - * only moves or copies its value out, and a write lands in place, a JS array's taking - * the new one's length. A read copies as `objectCopy` copies to `depth`. + * `Cell::from_mut` of an object a `&mut` is, which a pointer to is a place of: a read is + * a copy, as a `Cell` only moves or copies its value out, and a write lands in place. */ -export function cellIn(p: T, depth = 0): Cell { +export function cellIn(p: T): Cell { const write = partPlace(p); - return cellOfPlace(place(() => objectCopy(p, depth), (value) => (write.v = value))); + return cellOfPlace(place(() => objectCopy(p), (value) => (write.v = value))); } /** diff --git a/ts/src/lower/copy.rs b/ts/src/lower/copy.rs index 92ca19873..43fc94b59 100644 --- a/ts/src/lower/copy.rs +++ b/ts/src/lower/copy.rs @@ -638,8 +638,8 @@ fn by_value<'tcx>(tcx: TyCtxt<'tcx>, env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>) -> /// The value std's `def_id`, given `this` as its `self`, writes whole through that shared /// reference, putting another object in its place: the value of the `RefCell` it is, or -/// that a thread-local's `RefCell` holds. A `Cell`'s are left out, as none of its methods -/// that translate lends a reference into its value. +/// that a thread-local's `RefCell` holds. A `Cell`'s and an `UnsafeCell`'s are left out, +/// as each writes into the object it holds, which [`cell_writes`] says. fn replaces<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, this: Ty<'tcx>) -> Option> { let (owner, name) = super::library::key(tcx, def_id)?; let replaces = matches!( @@ -662,6 +662,19 @@ fn replaces<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, this: Ty<'tcx>) -> Option(tcx: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Option> { + let (owner, name) = super::library::key(tcx, def_id)?; + let writes = match owner.as_str() { + "Cell" => matches!(name.as_str(), "replace" | "set" | "swap" | "take" | "update"), + "UnsafeCell" => name == "replace", + _ => false, + }; + (writes && super::library::is_std(tcx, def_id)).then(|| args.type_at(0)) +} + /// The place each local of `body` borrows, where its one assignment is a borrow and /// nothing borrows the local itself, through which it could be pointed elsewhere. fn borrows<'tcx>(body: &mir::Body<'tcx>) -> IndexVec>> { @@ -1200,6 +1213,10 @@ impl<'tcx> Visitor<'tcx> for BodyWrites<'_, '_, '_, 'tcx> { { self.replace(value); } + if let Some(held) = cell_writes(self.tcx, def_id, args.skip_binder()) { + self.insert(held); + self.replace(held); + } // Those that point a pointer elsewhere or read through it replace nothing. let replace = !super::pointer::keeps(self.tcx, def_id, args.skip_binder()); // A call's argument may point into memory nothing else points into yet. diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index c1cd9a1ed..2c62ee162 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -340,7 +340,10 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { return err; } if crate::lower::pointer::from_reference(tcx, def_id, gargs) - && let Some(why) = self.made_out_of_step(def_id, gargs, &args[0].node) + && let Some(why) = args[0] + .node + .place() + .and_then(|p| self.made_out_of_step(def_id, gargs, tcx.mk_place_deref(p))) { return self.err(span, why); } @@ -541,6 +544,9 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { .place() .and_then(|p| self.raw_cells.get(&p.as_local()?).copied()) => { + if let Some(why) = self.made_out_of_step(def_id, gargs, cell) { + return Some(Some((self.err(span, why), false))); + } let cell = self.place_expr(cell, span, out); let get = tcx .associated_items(tcx.parent(def_id)) @@ -590,7 +596,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { && let (Some((s, i)), Some((t, j))) = (self.element_in(&a, ty), self.element_in(&c, ty)) { let mut made = vec![s, i, t, j]; - made.extend(self.copy_depth(ty)); + made.extend(self.b.t.copy_depth(ty)); out.push(StmtKind::Expr(Expr::rs("sliceSwapAcross", made)).at(None)); return done(Expr::undefined(), false); } @@ -1403,10 +1409,11 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { /// `slice::from_mut`, `array::from_mut`, or `from_mut` of `owner`, `Cell` or /// `UnsafeCell`, of `op`, a `&mut` to a `ty`: of the `rs.Place` it is or of the place it - /// names, or else in place of the bytes or the object it is, which a `Cell` reads a copy - /// of, as it only moves or copies a value out, an `UnsafeCell` lends as itself, as its - /// `get` points at it, and a slice writes its element into. A slice of one byte no array - /// holds is refused, as is any of an object JS can't write a whole value into. + /// names, or else in place of the bytes or the object it is, which a cell lends as itself, + /// as its `get` or `as_ptr` points at it, but a `Cell` of an object a pointer is the + /// place of reads a copy of, as its writes replace the value it read, and a slice writes + /// its element into. A slice of one byte no array holds is refused, as is any of an + /// object JS can't write a whole value into. fn one_of( &mut self, owner: &str, @@ -1424,11 +1431,10 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { _ => "sliceOfPlace", }; let of = |made: Expr| Expr::rs(of_place, vec![made]); - // A `Cell` of an object reads a copy, to the depth a copy of it goes. - let depth = (owner == "Cell").then(|| self.copy_depth(ty)).flatten(); + let place_of = self.b.t.pointee(ty) == crate::lower::pointer::Pointee::Place; let of_object = |object: Expr| match owner { - "Cell" => Expr::rs("cellIn", [object].into_iter().chain(depth.clone()).collect()), - "UnsafeCell" => of(Expr::rs("partPlace", vec![object])), + "Cell" if place_of => Expr::rs("cellIn", vec![object]), + "Cell" | "UnsafeCell" => of(Expr::rs("partPlace", vec![object])), _ => Expr::rs("sliceIn", vec![object]), }; let cell = owner != "raw" && owner != "array"; @@ -1771,7 +1777,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { None => place.0, }; let mut made = vec![object, value]; - made.extend(self.copy_depth(ty)); + made.extend(self.b.t.copy_depth(ty)); let old = Expr::rs("replaceIn", made); let Some(base) = base else { out.push(StmtKind::Expr(old).at(None)); @@ -1788,7 +1794,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { let name = self.b.fresh(base); if let Some((slice, index)) = self.element_in(&place, ty) { let mut made = vec![slice, index, value]; - made.extend(self.copy_depth(ty)); + made.extend(self.b.t.copy_depth(ty)); let old = Expr::rs("sliceReplace", made); out.push(StmtKind::decl(false, name.clone(), None, Some(old)).at(None)); return Expr::ident(name); @@ -1839,21 +1845,6 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { Some(((**slice).clone(), (**index).clone())) } - /// How many levels of arrays of objects a write may land in `ty` nests, whose items a - /// copy of a view of one copies too, unlike references, which move as they are: the - /// runtime's `depth`, if any. - fn copy_depth(&self, ty: Ty<'tcx>) -> Option { - let mut depth = 0; - let mut at = ty; - while let ty::Array(elem, _) = at.kind() - && self.b.t.write_in(*elem).is_some() - { - depth += 1; - at = *elem; - } - (depth > 0).then(|| Expr::lit(depth.to_string())) - } - /// Set what a `&mut` borrows to `value`. fn set_borrowed(&mut self, (read, place): &Borrowed<'tcx>, value: Expr, span: Span, out: &mut Vec) { match place { diff --git a/ts/src/lower/mir/place.rs b/ts/src/lower/mir/place.rs index 03fc45246..340fcddad 100644 --- a/ts/src/lower/mir/place.rs +++ b/ts/src/lower/mir/place.rs @@ -2232,18 +2232,17 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { && (t.pointee(ty) != crate::lower::pointer::Pointee::Place || cells && t.std_of(ty) == Some(Std::Cell)) } - /// Why the pointer std's `def_id` makes of what `reference` points at would not keep - /// pointing where Rust's does, if it would not, judged as [`Self::out_of_step`] judges a - /// raw pointer: `UnsafeCell::get`'s, holding a `T`, is the `rs.Cell` itself or the - /// object it holds. + /// Why the pointer std's `def_id` makes of `pointee`, what a reference points at, would + /// not keep pointing where Rust's does, if it would not, judged as [`Self::out_of_step`] + /// judges a raw pointer: `UnsafeCell::get`'s, holding a `T`, is the `rs.Cell` itself or + /// the object it holds. pub(super) fn made_out_of_step( &self, def_id: rustc_hir::def_id::DefId, gargs: ty::GenericArgsRef<'tcx>, - reference: &Operand<'tcx>, + pointee: Place<'tcx>, ) -> Option { let tcx = self.tcx; - let pointee = tcx.mk_place_deref(reference.place()?); let owner = library::key(tcx, def_id).map(|(owner, _)| owner); let cell = matches!(owner.as_deref(), Some("UnsafeCell" | "Cell")); let held = gargs.type_at(0); diff --git a/ts/src/lower/mod.rs b/ts/src/lower/mod.rs index 4ea15ff68..41aa030e0 100644 --- a/ts/src/lower/mod.rs +++ b/ts/src/lower/mod.rs @@ -1449,6 +1449,21 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { } } + /// How many levels of arrays of objects a write may land in `ty` nests, whose items a + /// copy of a view of one copies too, unlike references, which move as they are: the + /// runtime's `depth`, if any. + pub fn copy_depth(&self, ty: Ty<'tcx>) -> Option { + let mut depth = 0; + let mut at = ty; + while let ty::Array(elem, _) = at.kind() + && self.write_in(*elem).is_some() + { + depth += 1; + at = *elem; + } + (depth > 0).then(|| ts::Expr::lit(depth.to_string())) + } + /// What `ty` points at, if it is a `&mut` that is an `rs.Place`. pub fn place_pointee(&self, ty: Ty<'tcx>) -> Option> { match ty.kind() { diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index 77f086fec..0f0f27282 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -514,13 +514,6 @@ pub fn call<'tcx>( true => Expr::rs("byteSliceFrom", vec![e]), false => super::library::slice_from(e), }; - // The pointer an `rs.Cell` gives to what it holds: the cell, the place of a value JS - // copies, or else the object or array it holds. - let cell_pointer = |b: &Body<'_, '_, 'tcx>, cell: Expr| match b.t.pointee(pointee()) { - Pointee::Place => cell, - Pointee::Array => to_pointer(b, cell.member("v")), - _ => cell.member("v"), - }; let to_reference = |b: &Body<'_, '_, 'tcx>, e: Expr| match super::byte_array(b.t.uninit_inner(pointee())) { true => Expr::rs("byteSliceView", vec![e]), false => super::library::slice_view(e), @@ -763,13 +756,8 @@ pub fn call<'tcx>( replace(b, p, value, pointee(), span, out) } ("ptr", "swap") => { - let (x, y) = (b.spill(arg(), "x", out), b.spill(arg(), "y", out)); - let old = read(b, x.clone(), pointee(), span); - let old = b.spill(old, "old", out); - let from_y = read(b, y.clone(), pointee(), span); - let into_x = write(b, x, from_y, pointee(), span); - out.push(StmtKind::Expr(into_x).at(None)); - write(b, y, old, pointee(), span) + let (x, y) = (arg(), arg()); + swap(b, x, y, pointee(), span, out) } ("ptr", "drop_in_place") => { let p = arg(); @@ -846,20 +834,27 @@ pub fn call<'tcx>( ("UnsafeCell", "new") => Expr::New(Box::new(Expr::Rs("Cell".into())), vec![arg()]), ("UnsafeCell", "get") | ("Cell", "as_ptr") => { let cell = arg(); - cell_pointer(b, cell) + cell_pointer(b, cell, pointee()) } - // `get` of the cell where the pointer points, an `rs.Place` of where it is kept. + // `get` of the cell where the pointer points, an `rs.Place` of where it is kept, or + // the pointer itself if it points at no memory, as a cast of it would. ("UnsafeCell", "raw_get") => { let tcx = b.t.tcx; let cell = tcx .type_of(tcx.parent(call.def_id)) .instantiate(tcx, gargs) .skip_norm_wip(); - let cell = match b.t.pointee(cell) { - Pointee::Object => arg(), - _ => arg().member("v"), - }; - cell_pointer(b, cell) + let object = b.t.pointee(cell) == Pointee::Object; + if object && b.t.pointee(pointee()) == Pointee::Place { + return Ok(Some(arg())); + } + let name = b.fresh("p"); + let p = Expr::ident(&name); + let made = cell_pointer(b, if object { p } else { p.member("v") }, pointee()); + // Typed, as a null pointer's own type is none. + let ty = Some(b.t.ty(Ty::new_imm_ptr(tcx, cell), span)); + let make = Expr::Lambda(vec![crate::ts::Param { name, ty }], Box::new(made)); + Expr::rs("rawUnsize", vec![arg(), make]) } // `ptr::replace` of the pointer `get` gives. ("UnsafeCell", "replace") if b.t.zero_sized(pointee()) => { @@ -868,7 +863,7 @@ pub fn call<'tcx>( } ("UnsafeCell", "replace") => { let cell = arg(); - let p = cell_pointer(b, cell); + let p = cell_pointer(b, cell, pointee()); let value = arg(); replace(b, p, value, pointee(), span, out) } @@ -1142,8 +1137,26 @@ pub fn raw_pointee<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option } } +/// The pointer an `rs.Cell` holding a `ty` gives to it, as `UnsafeCell::get` and +/// `Cell::as_ptr` do: the cell, the place of a value JS copies, or else the object, array, +/// or slice it holds. +pub(super) fn cell_pointer<'tcx>(b: &Body<'_, '_, 'tcx>, cell: Expr, ty: Ty<'tcx>) -> Expr { + let pointee = b.t.pointee(ty); + if pointee == Pointee::Place { + return cell; + } + let held = cell.member("v"); + match pointee { + Pointee::Array if super::byte_array(b.t.uninit_inner(ty)) => Expr::rs("byteSliceFrom", vec![held]), + Pointee::Array => super::library::slice_from(held), + Pointee::Slice if slice_of_bytes(ty) => Expr::rs("rawByteSliceOf", vec![held]), + Pointee::Slice => Expr::rs("rawSliceOf", vec![held]), + Pointee::Place | Pointee::Object => held, + } +} + /// The value `p`, a pointer to `ty`, points at, as a value of its own. -fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { +pub(super) fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { let value = match b.t.pointee(ty) { Pointee::Place => p.member("v"), Pointee::Object => p, @@ -1158,7 +1171,7 @@ fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> /// `ptr::replace` of `p`, a pointer to `ty`, with `value`: what it held, as a value of its /// own. -fn replace<'tcx>( +pub(super) fn replace<'tcx>( b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, @@ -1167,15 +1180,51 @@ fn replace<'tcx>( out: &mut Vec, ) -> Expr { let p = b.spill(p, "pointer", out); - let old = read(b, p.clone(), ty, span); + let old = copied_out(b, p.clone(), ty, span); let old = b.spill(old, "old", out); let write = write(b, p, value, ty, span); out.push(StmtKind::Expr(write).at(None)); old } +/// What `p`, a pointer to `ty`, points at, as a value of its own that a whole write there +/// after leaves as it was. The write may land in the object there rather than replace it, +/// as in one a `from_mut` cell holds, so an object, or an array to the depth `objectCopy` +/// copies its elements, is copied one object deep. +fn copied_out<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { + match b.t.pointee(ty) { + _ if b.t.zero_sized(ty) => read(b, p, ty, span), + Pointee::Object => Expr::rs("objectCopy", vec![p]), + Pointee::Array if !super::byte_array(b.t.uninit_inner(ty)) => { + let depth = b.t.copy_depth(b.t.uninit_inner(ty)); + let view = super::library::slice_view(p); + Expr::rs("objectCopy", std::iter::once(view).chain(depth).collect()) + } + Pointee::Place if b.t.write_in(ty).is_some() => Expr::rs("objectCopy", vec![p.member("v")]), + _ => read(b, p, ty, span), + } +} + +/// `ptr::swap` of `x` and `y`, pointers to `ty`. +pub(super) fn swap<'tcx>( + b: &mut Body<'_, '_, 'tcx>, + x: Expr, + y: Expr, + ty: Ty<'tcx>, + span: Span, + out: &mut Vec, +) -> Expr { + let (x, y) = (b.spill(x, "x", out), b.spill(y, "y", out)); + let old = copied_out(b, x.clone(), ty, span); + let old = b.spill(old, "old", out); + let from_y = copied_out(b, y.clone(), ty, span); + let into_x = write(b, x, from_y, ty, span); + out.push(StmtKind::Expr(into_x).at(None)); + write(b, y, old, ty, span) +} + /// The write of `value`, a `ty`, where `p` points. -fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { +pub(super) fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { if b.t.zero_sized(ty) { return Expr::undefined(); } diff --git a/ts/src/lower/task.rs b/ts/src/lower/task.rs index e46390a12..1720f9cfd 100644 --- a/ts/src/lower/task.rs +++ b/ts/src/lower/task.rs @@ -589,26 +589,65 @@ fn cell<'tcx>( args: Vec, out: &mut Vec, ) -> Result, Vec> { + use super::pointer::{self, Pointee}; let tcx = b.t.tcx; + let span = call.span; let ty = call.args.type_at(0); + // A pointer `as_ptr` gave to an object or an array is it, so a write lands in it. + let in_place = matches!(b.t.pointee(ty), Pointee::Object | Pointee::Array) && !b.t.zero_sized(ty); + let default = |b: &mut Body<'_, '_, 'tcx>, out: &mut Vec| { + let f = super::collections::trait_fn(tcx, "Default", "default"); + b.call_def(f, tcx.mk_args(&[ty.into()]), Vec::new(), span, out) + }; let e = match name { "new" => Expr::New(Box::new(Expr::Rs("Cell".into())), args), - "get" => b.t.copied(first(args).member("v"), ty, call.span), + "get" => b.t.copied(first(args).member("v"), ty, span), "into_inner" => first(args).member("v"), // As its `UnsafeCell`'s `get` and `get_mut`, the same `rs.Cell`. "as_ptr" | "get_mut" => return Err(args), + // With what drops the value it replaces. + "set" if in_place => { + let [cell, value] = two(args); + let p = pointer::cell_pointer(b, cell, ty); + match b.t.drops(ty) { + true => { + let old = pointer::replace(b, p, value, ty, span, out); + b.t.drop_value(old, ty, span).unwrap_or_else(Expr::undefined) + } + false => pointer::write(b, p, value, ty, span), + } + } + "replace" | "take" if in_place => { + let [cell, value] = two(args); + let value = match name { + "take" => default(b, out), + _ => value, + }; + let p = pointer::cell_pointer(b, cell, ty); + pointer::replace(b, p, value, ty, span, out) + } + "update" if in_place => { + let [cell, f] = two(args); + let p = b.spill(pointer::cell_pointer(b, cell, ty), "pointer", out); + let new = f.call(vec![pointer::read(b, p.clone(), ty, span)]); + pointer::write(b, p, new, ty, span) + } + "swap" => { + let [x, y] = two(args); + let (x, y) = (pointer::cell_pointer(b, x, ty), pointer::cell_pointer(b, y, ty)); + pointer::swap(b, x, y, ty, span, out) + } "replace" => Expr::rs("cellReplace", args), "update" => Expr::rs("cellUpdate", args), // With what drops the value it replaces. "set" => { - let Some(droppers) = b.t.droppers([ty], call.span) else { + let Some(droppers) = b.t.droppers([ty], span) else { return Ok(Some(Expr::undefined())); }; Expr::rs("cellSet", args.into_iter().chain(droppers).collect()) } "take" => { - let f = super::collections::trait_fn(tcx, "Default", "default"); - let value = b.call_def(f, tcx.mk_args(&[ty.into()]), Vec::new(), call.span, out); + let value = default(b, out); Expr::rs("cellReplace", vec![first(args), value]) } _ => return Ok(None), From fb8b478f18cf21af067ab40c9c2942599a17849c Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 22:40:13 -0800 Subject: [PATCH 06/17] fix(ts): refuse UnsafeCell::from_mut of an object a pointer is the place of, and keep a Box's allocation through ptr::replace and ptr::swap Narrows the scope per the round-3 design checkpoint: UnsafeCell::from_mut of a Vec, String, or pinned Box, and Cell::from_mut of a pinned Box, are refused with a span, and copied_out reads such types as main did, so a Box keeps its allocation identity. Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/pointers/caller.ts | 2 +- .../lang/pointers/expected/pointers.ts | 213 +++++++++++------- ts/fixtures/lang/pointers/src/pointers.rs | 55 +++-- ts/fixtures/reject/census.txt | 4 + ts/fixtures/reject/expected.txt | 4 + ts/fixtures/reject/src/alloc.rs | 16 ++ ts/fixtures/reject/ts.toml | 1 + ts/src/lower/mir/call.rs | 14 +- ts/src/lower/pointer.rs | 8 +- 9 files changed, 207 insertions(+), 110 deletions(-) diff --git a/ts/fixtures/lang/pointers/caller.ts b/ts/fixtures/lang/pointers/caller.ts index a325c6541..d9616c730 100644 --- a/ts/fixtures/lang/pointers/caller.ts +++ b/ts/fixtures/lang/pointers/caller.ts @@ -18,7 +18,7 @@ const unsafeCells = [ /** * `pointers::raw_cells` scripts: each operation, then each replace before and after writes * through pointers to where the cells are kept, through cells of what a `&mut` borrows, and - * of pointers to no cell beside cells of a `Vec` and a `String`. + * of pointers to no cell beside boxes replaced and swapped. */ const rawCells = [ [0, 8, 9, 2, 3, 4, 5, 6, 7], diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 03b644c37..735943618 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -3059,8 +3059,9 @@ export function genericUnions(data: rs.ByteSlice, out: rs.ByteVec): void { * `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, * an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a * pointer taken before sees, giving back what it held undropped; what `as_ref_unchecked` - * and `as_mut_unchecked` lend; `raw_get` of pointers to no cell; and cells of a `Vec` and - * a `String` a `&mut` borrows, replaced and swapped through `get`. + * and `as_mut_unchecked` lend; `raw_get` of pointers to no cell; and the allocations of + * boxes and pinned boxes replaced and swapped through pointers, `UnsafeCell`s, one + * `from_mut` makes, and `Cell`s. */ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const count: rs.Arc = new rs.Arc(undefined); @@ -3112,16 +3113,16 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _110: rs.Slice = rs.sliceFrom(wide.v); rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_110.data[_110.start + 1]).reverse()), undefined, true); } else if (_41 === 1) { - const p: rs.Place> = rs.place(() => num, (value5) => num = value5); + const p: rs.Place> = rs.place(() => num, (value9) => num = value9); const _115: rs.u32 = rs.u32WrappingMul(rs.rawUnsize(p, (p10: rs.Place>) => p10.v).v, ((n as rs.u32) | 1) >>> 0); rs.rawUnsize(p, (p11: rs.Place>) => p11.v).v = _115; const s: Slots.Slots = slots; const _122: rs.u32 = n as rs.u32; const _123: rs.Place = Slots.num(s); _123.v = rs.u32Add(_123.v, _122); - const _125: Held.Held = rs.rawUnsize(rs.place(() => s.held, (value6) => s.held = value6), (p12: rs.Place>) => p12.v.v); + const _125: Held.Held = rs.rawUnsize(rs.place(() => s.held, (value10) => s.held = value10), (p12: rs.Place>) => p12.v.v); _125.n = _125.n ^ n; - const _127: [rs.u8, rs.u16] = rs.rawUnsize(rs.place(() => s.pair, (value7) => s.pair = value7), (p13: rs.Place>) => p13.v.v); + const _127: [rs.u8, rs.u16] = rs.rawUnsize(rs.place(() => s.pair, (value11) => s.pair = value11), (p13: rs.Place>) => p13.v.v); _127[0] = _127[0] ^ n; const _129: rs.u32 = n as rs.u32; const _130: rs.Place = rs.rawUnsize({ v: slot }, (p14: rs.Place>) => p14.v); @@ -3131,7 +3132,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(slots.held.v.n, slots.pair.v[0])), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slot.v).reverse()), undefined, true); } else if (_41 === 2) { - const c: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value3) => local = value3)); + const c: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value7) => local = value7)); c.v = rs.intRotateLeft(c.v, n as rs.u32, 32, false); const _168: Held.Held = rs.cellOfPlace(rs.partPlace(whole)).v; _168.n = _168.n ^ n; @@ -3140,7 +3141,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _172); } const borrowed2 = elems; - const e: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed2[_172], (value4) => borrowed2[_172] = value4)); + const e: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed2[_172], (value8) => borrowed2[_172] = value8)); const _176: rs.u16 = n as rs.u16; const _177: rs.Place = e; _177.v = _177.v ^ _176; @@ -3152,66 +3153,63 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { } else if (_41 === 3) { const p2: rs.Place = num; const _193: rs.u32 = ((n as rs.u32) << 3) >>> 0; - const pointer8 = num; - const old19 = pointer8.v; - pointer8.v = _193; - rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old19).reverse()), undefined, true); + const pointer9 = num; + const old22 = pointer9.v; + pointer9.v = _193; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old22).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(p2.v).reverse()), undefined, true); const p3: Held.Held = held.v; - const _207: rs.Arc = rs.arcClone(count); - const _206: Held.Held = new Held.Held(n, _207); - const pointer9 = held.v; - const old20 = rs.objectCopy(pointer9); - rs.ptrWrite(pointer9, _206); - const old2: Held.Held = old20; + const _206: Held.Held = new Held.Held(n, rs.arcClone(count)); + const pointer10 = held.v; + const old23 = rs.objectCopy(pointer10); + rs.ptrWrite(pointer10, _206); + const old2: Held.Held = old23; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old2.n, p3.n, count.strong & 0xff)), undefined, true); old2[Symbol.dispose](); rs.byteVecPush(out, count.strong & 0xff); const p4: [rs.u8, rs.u16] = pair.v; const _225: [rs.u8, rs.u16] = [n, rs.u16Mul(n as rs.u16, 3)]; - const pointer10 = pair.v; - const old21 = rs.objectCopy(pointer10); - rs.ptrWrite(pointer10, _225); - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old21[0], p4[0])), undefined, true); + const pointer11 = pair.v; + const old24 = rs.objectCopy(pointer11); + rs.ptrWrite(pointer11, _225); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old24[0], p4[0])), undefined, true); const p5: rs.ByteSlice = rs.byteSliceFrom(bytes.v); - const _237: Uint8Array = Uint8Array.of(n, ~n & 0xff); - const pointer11 = rs.byteSliceFrom(bytes.v); - const old22 = rs.byteSliceToArray(pointer11); - rs.byteSliceSet(pointer11, _237); - rs.iterExtendBytes(out, rs.iterSlice(old22), undefined, true); + const pointer12 = rs.byteSliceFrom(bytes.v); + const old25 = rs.byteSliceToArray(pointer12); + rs.byteSliceSet(pointer12, Uint8Array.of(n, ~n & 0xff)); + rs.iterExtendBytes(out, rs.iterSlice(old25), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.byteSliceToArray(p5)), undefined, true); - const _244: Duo.Duo = { _0: n, _1: n ^ 1 }; - const pointer12 = duo.v; - const old23 = rs.objectCopy(pointer12); - rs.ptrWrite(pointer12, _244); - const old5: Duo.Duo = old23; + const pointer13 = duo.v; + const old26 = rs.objectCopy(pointer13); + rs.ptrWrite(pointer13, { _0: n, _1: n ^ 1 }); + const old5: Duo.Duo = old26; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old5._0, old5._1, duo.v._0)), undefined, true); } else if (_41 === 4) { - const _255: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value) => local = value)); + const _255: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value5) => local = value5)); const _257: rs.u32 = n as rs.u32; - const pointer5 = _255; - const old16 = pointer5.v; - pointer5.v = _257; - rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old16).reverse()), undefined, true); + const pointer6 = _255; + const old19 = pointer6.v; + pointer6.v = _257; + rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(old19).reverse()), undefined, true); const h2: rs.Cell = rs.cellOfPlace(rs.partPlace(whole)); const p6: Held.Held = h2.v; const _266: Held.Held = new Held.Held(~n & 0xff, rs.arcClone(count)); - const pointer6 = h2.v; - const old17 = rs.objectCopy(pointer6); - rs.ptrWrite(pointer6, _266); - const old7: Held.Held = old17; + const pointer7 = h2.v; + const old20 = rs.objectCopy(pointer7); + rs.ptrWrite(pointer7, _266); + const old7: Held.Held = old20; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old7.n, p6.n, whole.n, count.strong & 0xff)), undefined, true); const _282: rs.usize = rs.u8Rem(n, 3) as rs.usize; if (!(_282 < 3)) { rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _282); } const borrowed = elems; - const _280: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_282], (value2) => borrowed[_282] = value2)); + const _280: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_282], (value6) => borrowed[_282] = value6)); const _286: rs.u16 = n as rs.u16; - const pointer7 = _280; - const old18 = pointer7.v; - pointer7.v = _286; - rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old18).reverse()), undefined, true); + const pointer8 = _280; + const old21 = pointer8.v; + pointer8.v = _286; + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old21).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(local).reverse()), undefined, true); old7[Symbol.dispose](); } else if (_41 === 5) { @@ -3227,8 +3225,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { _302[1] = _302[1] ^ _301; const _304: rs.ByteSlice = rs.byteSliceFrom(bytes.v); _304.data[_304.start + 1] = _304.data[_304.start + 1] ^ n; - const _308: rs.u16 = n as rs.u16; - rs.sliceSet(rs.sliceFrom(wide.v), [_308, 1]); + rs.sliceSet(rs.sliceFrom(wide.v), [n as rs.u16, 1]); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(held.v.n, duo.v._0)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(pair.v[1]).reverse()), undefined, true); @@ -3243,54 +3240,98 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _344); } _342.data[_342.start + _344] = _342.data[_342.start + _344] ^ 64; - const _350: Uint8Array = Uint8Array.of(3, n, 4); - const pointer4 = rs.byteSliceFrom(a.v); - const old15 = rs.byteSliceToArray(pointer4); - rs.byteSliceSet(pointer4, _350); - rs.iterExtendBytes(out, rs.iterSlice(old15), undefined, true); + const pointer5 = rs.byteSliceFrom(a.v); + const old18 = rs.byteSliceToArray(pointer5); + rs.byteSliceSet(pointer5, Uint8Array.of(3, n, 4)); + rs.iterExtendBytes(out, rs.iterSlice(old18), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(copy$array_u8(arr)), undefined, true); } else if (_41 === 6) { const null_: rs.Place> = rs.NULL; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(rs.rawUnsize(null_, (p7: rs.Place>) => p7.v) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.NULL, (p8: rs.Place>) => p8.v.v) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.ptrWrappingAdd(null_, rs.u8Rem(n, 4) as rs.usize, 4, 4), (p9: rs.Place>) => p9.v) === rs.NULL ? 1 : 0)), undefined, true); - const list: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(n, 1)); - const other: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(2)); - const text: rs.String = rs.stringFrom("ab"); - const c2: rs.Cell = rs.cellOfPlace(rs.partPlace(list)); - const _381: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(n ^ 1, 3, 4)); - const pointer = c2; - const old11 = rs.objectCopy(pointer.v); - pointer.v = _381; - const old10: rs.ByteVec = old11; - const x = c2; - const y = rs.cellOfPlace(rs.partPlace(other)); - const old12 = rs.objectCopy(x.v); - x.v = rs.objectCopy(y.v); - y.v = old12; - const _392: rs.Place = c2; - const _394: rs.ByteVec = rs.byteVecFrom(Uint8Array.of(5)); - const pointer2 = _392; - const old13 = rs.objectCopy(pointer2.v); - pointer2.v = _394; - const swapped: rs.ByteVec = old13; - const _398: rs.Cell = rs.cellOfPlace(rs.partPlace(text)); - const _400: rs.String = rs.fmtToString((f: rs.Formatter) => rs.u8Display(n, f), 0); - const pointer3 = _398; - const old14 = rs.objectCopy(pointer3.v); - pointer3.v = _400; - const was: rs.String = old14; - rs.iterExtendBytes(out, rs.byteVecIntoIter(old10), undefined, true); - rs.iterExtendBytes(out, rs.byteVecIntoIter(swapped), undefined, true); - rs.iterExtendBytes(out, rs.byteVecIntoIter(other), undefined, true); - rs.iterExtendBytes(out, rs.byteVecIntoIter(list), undefined, true); - rs.iterExtendBytes(out, rs.iterChain(rs.strBytes(was.v), rs.strBytes(text.v)), undefined, true); + const held2: (a0: rs.u8) => Held.Held = (n2: rs.u8): Held.Held => { + return new Held.Held(n2, rs.arcClone(count)); + }; + let a2: Held.Held = held2(n); + const atA: Held.Held = a2; + const _381: rs.Place = rs.place(() => a2, (value) => a2 = value); + const _383: Held.Held = held2(~n & 0xff); + const pointer = _381; + const old11 = pointer.v; + pointer.v = _383; + const old10: Held.Held = old11; + let other: Held.Held = held2(n ^ 1); + const _395: [Held.Held, Held.Held] = [a2, other]; + const atNew: Held.Held = _395[0]; + const atOther: Held.Held = _395[1]; + const x3 = rs.place(() => a2, (value2) => a2 = value2); + const y3 = rs.place(() => other, (value3) => other = value3); + const old12 = x3.v; + x3.v = y3.v; + y3.v = old12; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(atA === old10 ? 1 : 0, atNew === other ? 1 : 0)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(atOther === a2 ? 1 : 0, old10.n, a2.n, other.n)), undefined, true); + const pinned: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([n, 1] as [rs.u8, rs.u16]); + const atPinned: [rs.u8, rs.u16] = pinned.v; + const _434: [rs.u8, rs.u16] = [n ^ 2, 2]; + const pointer2 = pinned; + const old13 = pointer2.v; + pointer2.v = _434; + const was: [rs.u8, rs.u16] = old13; + const _439: [rs.Cell<[rs.u8, rs.u16]>, rs.Cell<[rs.u8, rs.u16]>] = [new rs.Cell([n, 3] as [rs.u8, rs.u16]), new rs.Cell([~n & 0xff, 4] as [rs.u8, rs.u16])]; + const x: rs.Cell<[rs.u8, rs.u16]> = _439[0]; + const y: rs.Cell<[rs.u8, rs.u16]> = _439[1]; + const atX: [rs.u8, rs.u16] = x.v; + const x4 = x; + const y4 = y; + const old14 = x4.v; + x4.v = y4.v; + y4.v = old14; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(atX === y.v ? 1 : 0, x.v[0])), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(atPinned === was ? 1 : 0, was[0], pinned.v[0])), undefined, true); + const cell: rs.Cell = new rs.Cell(held2(n ^ 3)); + const atCell: Held.Held = cell.v; + const _496: Held.Held = held2(n ^ 4); + const pointer3 = cell; + const old15 = pointer3.v; + pointer3.v = _496; + const was2: Held.Held = old15; + let boxed: Held.Held = held2(n ^ 7); + const atBoxed: Held.Held = boxed; + const _510: rs.Cell = rs.cellOfPlace(rs.place(() => boxed, (value4) => boxed = value4)); + const _512: Held.Held = held2(n); + const pointer4 = _510; + const old16 = pointer4.v; + pointer4.v = _512; + const lent: Held.Held = old16; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(atBoxed === lent ? 1 : 0, lent.n, boxed.n)), undefined, true); + const _526: [rs.Cell, rs.Cell] = [new rs.Cell(held2(n ^ 5)), new rs.Cell(held2(n ^ 6))]; + const x2: rs.Cell = _526[0]; + const y2: rs.Cell = _526[1]; + const atX2: Held.Held = x2.v; + const x5 = x2; + const y5 = y2; + const old17 = x5.v; + x5.v = y5.v; + y5.v = old17; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(atCell === was2 ? 1 : 0, was2.n, cell.v.n)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(atX2 === y2.v ? 1 : 0, count.strong & 0xff)), undefined, true); + y2[Symbol.dispose](); + x2[Symbol.dispose](); + lent[Symbol.dispose](); + boxed[Symbol.dispose](); + was2[Symbol.dispose](); + cell[Symbol.dispose](); + other[Symbol.dispose](); + old10[Symbol.dispose](); + a2[Symbol.dispose](); } else { rs.byteVecPush(out, n); } } rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); - const _423: Held.Held = held.v; - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_423.n, count.strong & 0xff)), undefined, true); - _423[Symbol.dispose](); + const _574: Held.Held = held.v; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_574.n, count.strong & 0xff)), undefined, true); + _574[Symbol.dispose](); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); slots.held[Symbol.dispose](); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(whole.n, count.strong & 0xff)), undefined, true); diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index c1b456b4b..0a42d4194 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2480,8 +2480,9 @@ impl Slots { /// `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, /// an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a /// pointer taken before sees, giving back what it held undropped; what `as_ref_unchecked` -/// and `as_mut_unchecked` lend; `raw_get` of pointers to no cell; and cells of a `Vec` and -/// a `String` a `&mut` borrows, replaced and swapped through `get`. +/// and `as_mut_unchecked` lend; `raw_get` of pointers to no cell; and the allocations of +/// boxes and pinned boxes replaced and swapped through pointers, `UnsafeCell`s, one +/// `from_mut` makes, and `Cell`s. pub fn raw_cells(data: &[u8], out: &mut Vec) { let count = Rc::new(()); let num = UnsafeCell::new(7u32); @@ -2612,9 +2613,8 @@ pub fn raw_cells(data: &[u8], out: &mut Vec) { out.extend(old); out.extend(arr); }, - // Pointers to no cell, which `raw_get` gives back as they are, and cells of what - // a `&mut` borrows that is an object JS writes whole values into, replaced and - // swapped while the value given back is kept. + // Pointers to no cell, which `raw_get` gives back as they are, and boxes replaced + // and swapped through pointers and cells, each allocation moving with its box. 6 => unsafe { let null = ptr::null::>(); let pair = ptr::null_mut::>(); @@ -2623,19 +2623,38 @@ pub fn raw_cells(data: &[u8], out: &mut Vec) { u8::from(UnsafeCell::raw_get(pair).is_null()), u8::from(UnsafeCell::raw_get(null.wrapping_add(usize::from(n % 4))).is_null()), ]); - let mut list = vec![n, 1]; - let mut other = vec![2]; - let mut text = String::from("ab"); - let c = UnsafeCell::from_mut(&mut list); - let old = c.replace(vec![n ^ 1, 3, 4]); - ptr::swap(c.get(), UnsafeCell::from_mut(&mut other).get()); - let swapped = ptr::replace(c.get(), vec![5]); - let was = UnsafeCell::from_mut(&mut text).replace(n.to_string()); - out.extend(old); - out.extend(swapped); - out.extend(other); - out.extend(list); - out.extend(was.bytes().chain(text.bytes())); + let held = |n| Held { + n, + count: Rc::clone(&count), + }; + let mut a = Box::new(held(n)); + let at_a: *const Held = &*a; + let old = ptr::replace(&mut a, Box::new(held(!n))); + let mut other = Box::new(held(n ^ 1)); + let (at_new, at_other): (*const Held, *const Held) = (&*a, &*other); + ptr::swap(&mut a, &mut other); + out.extend([u8::from(ptr::eq(at_a, &*old)), u8::from(ptr::eq(at_new, &*other))]); + out.extend([u8::from(ptr::eq(at_other, &*a)), old.n, a.n, other.n]); + let pinned = UnsafeCell::new(Box::pin((n, 1u16))); + let at_pinned: *const (u8, u16) = &**pinned.get(); + let was = pinned.replace(Box::pin((n ^ 2, 2))); + let (x, y) = (Cell::new(Box::pin((n, 3u16))), Cell::new(Box::pin((!n, 4u16)))); + let at_x: *const (u8, u16) = &**x.as_ptr(); + x.swap(&y); + out.extend([u8::from(ptr::eq(at_x, &**y.as_ptr())), x.into_inner().0]); + out.extend([u8::from(ptr::eq(at_pinned, &*was)), was.0, pinned.into_inner().0]); + let cell = UnsafeCell::new(Box::new(held(n ^ 3))); + let at_cell: *const Held = &**cell.get(); + let was = cell.replace(Box::new(held(n ^ 4))); + let mut boxed = Box::new(held(n ^ 7)); + let at_boxed: *const Held = &*boxed; + let lent = UnsafeCell::from_mut(&mut boxed).replace(Box::new(held(n))); + out.extend([u8::from(ptr::eq(at_boxed, &*lent)), lent.n, boxed.n]); + let (x, y) = (Cell::new(Box::new(held(n ^ 5))), Cell::new(Box::new(held(n ^ 6)))); + let at_x: *const Held = &**x.as_ptr(); + x.swap(&y); + out.extend([u8::from(ptr::eq(at_cell, &*was)), was.n, (*cell.get()).n]); + out.extend([u8::from(ptr::eq(at_x, &**y.as_ptr())), Rc::strong_count(&count) as u8]); }, _ => out.push(n), } diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index e170b4984..3c584f189 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -10,6 +10,10 @@ fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another +fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+4:10: `Cell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index efd8371cf..4284d286e 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -14,6 +14,10 @@ fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another +fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+4:10: `Cell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index c57b97d78..994c0dcea 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -1,5 +1,7 @@ //! Memory from `alloc` and raw pointers read as other types' bytes or as addresses. +use std::pin::Pin; + use crate::pointers::Halves; /// A pointer read as another type's bytes. @@ -30,6 +32,20 @@ pub fn vec_pointer(v: &mut Vec) -> usize { unsafe { (*p).len() } } +/// `UnsafeCell`s of objects a pointer is the place of, which a whole write through `get` +/// lands in, so a value `replace` reads out before would change too or be a copy, and a +/// `Cell` of a pinned `Box`, whose copy is another allocation. +pub fn cell_of_objects(v: &mut Vec, s: &mut String, p: &mut Pin>, q: &mut Pin>) -> u8 { + let v = std::cell::UnsafeCell::from_mut(v); + let s = std::cell::UnsafeCell::from_mut(s); + let p = std::cell::UnsafeCell::from_mut(p); + let q = std::cell::Cell::from_mut(q); + let first = unsafe { + v.replace(Vec::new()).len() as u8 ^ s.replace(String::new()).len() as u8 ^ p.replace(Box::pin((3, 4))).0 + }; + first ^ q.replace(Box::pin((5, 6))).0 +} + /// A pointer to a cell behind a reference, made before a call that `UnsafeCell::raw_get` /// comes after, which reads it in its place only right after it is made. pub fn cell_pointer_kept(c: &std::cell::UnsafeCell, n: u32) -> u32 { diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index d01d8f825..961055668 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -4,6 +4,7 @@ roots = [ "alloc::alloc_retyped", "alloc::allocated", "alloc::bytes_as_values", + "alloc::cell_of_objects", "alloc::cell_pointer_kept", "alloc::cell_pointer_reassigned", "alloc::reinterpreted", diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 2c62ee162..96056d238 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -1413,7 +1413,8 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { /// as its `get` or `as_ptr` points at it, but a `Cell` of an object a pointer is the /// place of reads a copy of, as its writes replace the value it read, and a slice writes /// its element into. A slice of one byte no array holds is refused, as is any of an - /// object JS can't write a whole value into. + /// object JS can't write a whole value into, an `UnsafeCell` of an object a pointer is + /// the place of, such as a `Vec`, and either cell of a pinned `Box`. fn one_of( &mut self, owner: &str, @@ -1444,6 +1445,17 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { "a slice of one byte's place, which a byte slice can view only in an array of bytes", )); } + // A write through the cell's pointer lands in the object, so a value read out before + // would change with it unless copied, as a `Cell`'s read is, but a pinned `Box`'s copy + // is another allocation. + if cell && place_of && self.b.t.write_in(ty).is_some() && (owner == "UnsafeCell" || self.b.t.erased(ty) != ty) { + return Some(self.err( + span, + format!( + "`{owner}::from_mut` of `{ty}`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address" + ), + )); + } if self.in_array(deref) { let slice = self.place_expr(deref, span, out); return Some(of_object(self.b.t.array_rs(ty, "View").call(vec![slice]))); diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index d298eb672..7bb0dbe44 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -1189,9 +1189,10 @@ pub(super) fn replace<'tcx>( } /// What `p`, a pointer to `ty`, points at, as a value of its own that a whole write there -/// after leaves as it was. The write may land in the object there rather than replace it, -/// as in one a `from_mut` cell holds, so an object, or an array to the depth `objectCopy` -/// copies its elements, is copied one object deep. +/// after leaves as it was. The write lands in the object or array a pointer is, so one is +/// copied one object deep, an array's elements to the depth `objectCopy` copies them. One +/// a pointer is the place of is read as it is, keeping a `Box`'s allocation, as its write +/// puts another in the place. fn copied_out<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { match b.t.pointee(ty) { _ if b.t.zero_sized(ty) => read(b, p, ty, span), @@ -1201,7 +1202,6 @@ fn copied_out<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Spa let view = super::library::slice_view(p); Expr::rs("objectCopy", std::iter::once(view).chain(depth).collect()) } - Pointee::Place if b.t.write_in(ty).is_some() => Expr::rs("objectCopy", vec![p.member("v")]), _ => read(b, p, ty, span), } } From c6d97ccfd4ee825eca505f508dd824999cc5faaf Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 23:32:01 -0800 Subject: [PATCH 07/17] fix(ts): judge raw_get's pointer as get's, and refuse any from_mut of a pinned Box raw_get of a pointer one &raw made is judged by the place it was made of, so a cell assigned again while the pointer may be read is refused. slice::from_mut and array::from_mut of a pinned Box are refused as its cells are, as a whole write through the view lands in its allocation. Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/reject/census.txt | 10 ++++++---- ts/fixtures/reject/expected.txt | 10 ++++++---- ts/fixtures/reject/src/alloc.rs | 18 +++++++++++++++--- ts/fixtures/reject/ts.toml | 1 + ts/src/lower/mir/call.rs | 29 +++++++++++++++++++++-------- 5 files changed, 49 insertions(+), 19 deletions(-) diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index 3c584f189..c25a14ecd 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -10,12 +10,14 @@ fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another -fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_of_objects+4:10: `Cell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+7:15: `Cell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+8:34: `array::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+3:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 4284d286e..980cee516 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -14,12 +14,14 @@ fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another -fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_of_objects+4:10: `Cell::from_mut` of `std::pin::Pin>`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+7:15: `Cell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address +fixtures/reject/src/alloc.rs:cell_of_objects+8:34: `array::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+3:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index 994c0dcea..d3055c4b9 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -34,16 +34,17 @@ pub fn vec_pointer(v: &mut Vec) -> usize { /// `UnsafeCell`s of objects a pointer is the place of, which a whole write through `get` /// lands in, so a value `replace` reads out before would change too or be a copy, and a -/// `Cell` of a pinned `Box`, whose copy is another allocation. +/// `Cell` and an array of a pinned `Box`, whose copy is another allocation. pub fn cell_of_objects(v: &mut Vec, s: &mut String, p: &mut Pin>, q: &mut Pin>) -> u8 { let v = std::cell::UnsafeCell::from_mut(v); let s = std::cell::UnsafeCell::from_mut(s); let p = std::cell::UnsafeCell::from_mut(p); - let q = std::cell::Cell::from_mut(q); let first = unsafe { v.replace(Vec::new()).len() as u8 ^ s.replace(String::new()).len() as u8 ^ p.replace(Box::pin((3, 4))).0 }; - first ^ q.replace(Box::pin((5, 6))).0 + let second = std::cell::Cell::from_mut(q).replace(Box::pin((5, 6))).0; + let [third] = std::mem::replace(std::array::from_mut(q), [Box::pin((7, 8))]); + first ^ second ^ third.0 } /// A pointer to a cell behind a reference, made before a call that `UnsafeCell::raw_get` @@ -65,6 +66,17 @@ pub fn cell_pointer_reassigned(n: u32) -> u32 { unsafe { *p } } +/// A pointer `UnsafeCell::raw_get` makes of a raw pointer to a cell, read after the +/// variable holding the cell is given another. +pub fn cell_raw_reassigned(n: u32) -> u32 { + let mut c = std::cell::UnsafeCell::new(n); + let to = &raw const c; + let p = unsafe { std::cell::UnsafeCell::raw_get(to) }; + c = std::cell::UnsafeCell::new(n ^ 1); + let _ = &c; + unsafe { *p } +} + /// Casts between pointers to bytes and to values outside memory from `alloc`, which read /// the bytes of one type as another: of an array's bytes to `u32`s, of memory from /// `alloc` to the crate's struct, which JS keeps as an object, and of a pointer to values diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index 961055668..331789240 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -7,6 +7,7 @@ roots = [ "alloc::cell_of_objects", "alloc::cell_pointer_kept", "alloc::cell_pointer_reassigned", + "alloc::cell_raw_reassigned", "alloc::reinterpreted", "alloc::vec_pointer", "alloc::zeroed", diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 96056d238..666de0f7e 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -339,11 +339,23 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { if let Some(err) = self.std_places(def_id, gargs, result, span) { return err; } - if crate::lower::pointer::from_reference(tcx, def_id, gargs) + // `raw_get`'s pointer is judged as `get`'s, of the cell it points at: the place + // it was made of, where one `&raw` made it. + let raw_get = library::key(tcx, def_id).is_some_and(|(o, n)| o == "UnsafeCell" && n == "raw_get"); + let made = |p: Place<'tcx>| { + p.as_local() + .filter(|&l| raw_get && self.defs[l] == 1) + .and_then(|l| self.def_rvalue(l)) + }; + let pointee = |p: Place<'tcx>| match made(p) { + Some(rustc_middle::mir::Rvalue::RawPtr(_, made)) => *made, + _ => tcx.mk_place_deref(p), + }; + if (raw_get || crate::lower::pointer::from_reference(tcx, def_id, gargs)) && let Some(why) = args[0] .node .place() - .and_then(|p| self.made_out_of_step(def_id, gargs, tcx.mk_place_deref(p))) + .and_then(|p| self.made_out_of_step(def_id, gargs, pointee(p))) { return self.err(span, why); } @@ -1414,7 +1426,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { /// place of reads a copy of, as its writes replace the value it read, and a slice writes /// its element into. A slice of one byte no array holds is refused, as is any of an /// object JS can't write a whole value into, an `UnsafeCell` of an object a pointer is - /// the place of, such as a `Vec`, and either cell of a pinned `Box`. + /// the place of, such as a `Vec`, and any of a pinned `Box`. fn one_of( &mut self, owner: &str, @@ -1445,14 +1457,15 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { "a slice of one byte's place, which a byte slice can view only in an array of bytes", )); } - // A write through the cell's pointer lands in the object, so a value read out before - // would change with it unless copied, as a `Cell`'s read is, but a pinned `Box`'s copy - // is another allocation. - if cell && place_of && self.b.t.write_in(ty).is_some() && (owner == "UnsafeCell" || self.b.t.erased(ty) != ty) { + // A whole write through what this makes lands in the object, so a value read out + // before would change with it unless copied, as a `Cell`'s or a slice's read is, but + // a pinned `Box`'s copy is another allocation. + if place_of && self.b.t.write_in(ty).is_some() && (owner == "UnsafeCell" || self.b.t.erased(ty) != ty) { + let path = if owner == "raw" { "slice" } else { owner }; return Some(self.err( span, format!( - "`{owner}::from_mut` of `{ty}`, an object a write through the cell's pointer changes in place, so a value read out before would change too or be a copy at another address" + "`{path}::from_mut` of `{ty}`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address" ), )); } From 8e5d534bc7d988d9da6789d2435ef7c4a0f665c8 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 04:00:10 -0800 Subject: [PATCH 08/17] fix(ts): judge every origin of raw_get's pointer, and refuse lending a cell's pinned Box raw_get, and get through a reference that reborrows a pointer, are judged by each place the pointer may have been made of, through every assignment. get_mut and as_mut_unchecked of a cell of a pinned Box are refused, as a whole write through them lands in the allocation. Checkpoint: just ci not yet run on this commit (host overloaded). Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/reject/census.txt | 6 ++- ts/fixtures/reject/expected.txt | 6 ++- ts/fixtures/reject/src/alloc.rs | 21 +++++++-- ts/fixtures/reject/ts.toml | 1 + ts/src/lower/mir/call.rs | 82 ++++++++++++++++++++++++++------- ts/src/lower/mod.rs | 16 +++++++ ts/src/lower/pointer.rs | 11 +++++ 7 files changed, 121 insertions(+), 22 deletions(-) diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index c25a14ecd..e831a55bc 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -17,7 +17,11 @@ fixtures/reject/src/alloc.rs:cell_of_objects+7:15: `Cell::from_mut` of `std::pin fixtures/reject/src/alloc.rs:cell_of_objects+8:34: `array::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there -fixtures/reject/src/alloc.rs:cell_raw_reassigned+3:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+4:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+6:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+7:25: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +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:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 980cee516..87eb77ecd 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -21,7 +21,11 @@ fixtures/reject/src/alloc.rs:cell_of_objects+7:15: `Cell::from_mut` of `std::pin fixtures/reject/src/alloc.rs:cell_of_objects+8:34: `array::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there -fixtures/reject/src/alloc.rs:cell_raw_reassigned+3:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+4:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+6:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+7:25: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +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:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index d3055c4b9..d09c857b6 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -66,15 +66,28 @@ pub fn cell_pointer_reassigned(n: u32) -> u32 { unsafe { *p } } -/// A pointer `UnsafeCell::raw_get` makes of a raw pointer to a cell, read after the -/// variable holding the cell is given another. +/// Pointers `UnsafeCell::raw_get` makes of a raw pointer to a cell, and of one to either +/// of two, and `get` through the first, read after the variable holding the cell is given +/// another. pub fn cell_raw_reassigned(n: u32) -> u32 { let mut c = std::cell::UnsafeCell::new(n); + let d = std::cell::UnsafeCell::new(n ^ 2); let to = &raw const c; let p = unsafe { std::cell::UnsafeCell::raw_get(to) }; + let either = if n > 3 { &raw const c } else { &raw const d }; + let q = unsafe { std::cell::UnsafeCell::raw_get(either) }; + let r = unsafe { (*to).get() }; c = std::cell::UnsafeCell::new(n ^ 1); - let _ = &c; - unsafe { *p } + let _ = (&c, &d); + unsafe { *p ^ *q ^ *r } +} + +/// A `get_mut` and an `as_mut_unchecked` of a cell of a pinned `Box`, whose old value +/// `mem::replace` would give back as a copy. +pub fn pinned_lent(c: &mut std::cell::UnsafeCell>>) -> u8 { + let old = std::mem::replace(c.get_mut(), Box::pin((1, 2))); + let older = std::mem::replace(unsafe { c.as_mut_unchecked() }, Box::pin((3, 4))); + old.0 ^ older.0 } /// Casts between pointers to bytes and to values outside memory from `alloc`, which read diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index 331789240..c9c3a2e0d 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -8,6 +8,7 @@ roots = [ "alloc::cell_pointer_kept", "alloc::cell_pointer_reassigned", "alloc::cell_raw_reassigned", + "alloc::pinned_lent", "alloc::reinterpreted", "alloc::vec_pointer", "alloc::zeroed", diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 666de0f7e..5a3215b28 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -339,24 +339,32 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { if let Some(err) = self.std_places(def_id, gargs, result, span) { return err; } - // `raw_get`'s pointer is judged as `get`'s, of the cell it points at: the place - // it was made of, where one `&raw` made it. + // The cell `get`'s reference or `raw_get`'s pointer points at is judged as each + // place a pointer it is made through may have been made of, where every + // assignment of that pointer is known. let raw_get = library::key(tcx, def_id).is_some_and(|(o, n)| o == "UnsafeCell" && n == "raw_get"); - let made = |p: Place<'tcx>| { - p.as_local() - .filter(|&l| raw_get && self.defs[l] == 1) - .and_then(|l| self.def_rvalue(l)) - }; - let pointee = |p: Place<'tcx>| match made(p) { - Some(rustc_middle::mir::Rvalue::RawPtr(_, made)) => *made, - _ => tcx.mk_place_deref(p), + let pointees = |p: Place<'tcx>| { + let local = p.as_local(); + // A reference that reborrows what a pointer points at, as `(*p).get()` does. + let reborrowed = |l: rustc_middle::mir::Local| match self.def_rvalue(l) { + Some(rustc_middle::mir::Rvalue::Ref(_, _, at)) + if self.defs[l] == 1 && at.projection.as_slice() == [ProjectionElem::Deref] => + { + Some(at.local) + } + _ => None, + }; + let pointer = if raw_get { local } else { local.and_then(reborrowed) }; + pointer + .and_then(|l| self.raw_origins(l, &mut Vec::new())) + .unwrap_or_else(|| vec![tcx.mk_place_deref(p)]) }; if (raw_get || crate::lower::pointer::from_reference(tcx, def_id, gargs)) - && let Some(why) = args[0] - .node - .place() - .and_then(|p| self.made_out_of_step(def_id, gargs, pointee(p))) - { + && let Some(why) = args[0].node.place().and_then(|p| { + pointees(p) + .into_iter() + .find_map(|at| self.made_out_of_step(def_id, gargs, at)) + }) { return self.err(span, why); } crate::lower::pointer::pointees(self.b.t, def_id, gargs) @@ -1419,6 +1427,48 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { Some(borrowed) } + /// The places the raw pointer `local` may have been made of, by `&raw` or a reference, + /// through copies and casts, or `None` if any assignment of it is something else. + fn raw_origins( + &self, + local: rustc_middle::mir::Local, + seen: &mut Vec, + ) -> Option>> { + use rustc_middle::mir::{Rvalue, StatementKind}; + if seen.contains(&local) { + return Some(Vec::new()); + } + seen.push(local); + if (1..=self.mir.arg_count).contains(&local.as_usize()) || self.mutated[local] { + return None; + } + let (mut found, mut origins) = (0, Vec::new()); + for (bb, data) in self.mir.basic_blocks.iter_enumerated() { + for (i, stmt) in data.statements.iter().enumerate() { + let StatementKind::Assign(assign) = &stmt.kind else { + continue; + }; + let (place, rvalue) = &**assign; + if place.as_local() != Some(local) + || self.noops.contains(&(bb, i)) + || self.in_ignored(stmt.source_info.span) + { + continue; + } + found += 1; + match rvalue { + Rvalue::RawPtr(_, made) | Rvalue::Ref(_, _, made) => origins.push(*made), + Rvalue::Use(Operand::Copy(from) | Operand::Move(from), _) + | Rvalue::Cast(_, Operand::Copy(from) | Operand::Move(from), _) => { + origins.extend(self.raw_origins(from.as_local()?, seen)?) + } + _ => return None, + } + } + } + (found == self.defs[local]).then_some(origins) + } + /// `slice::from_mut`, `array::from_mut`, or `from_mut` of `owner`, `Cell` or /// `UnsafeCell`, of `op`, a `&mut` to a `ty`: of the `rs.Place` it is or of the place it /// names, or else in place of the bytes or the object it is, which a cell lends as itself, @@ -1460,7 +1510,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { // A whole write through what this makes lands in the object, so a value read out // before would change with it unless copied, as a `Cell`'s or a slice's read is, but // a pinned `Box`'s copy is another allocation. - if place_of && self.b.t.write_in(ty).is_some() && (owner == "UnsafeCell" || self.b.t.erased(ty) != ty) { + if owner == "UnsafeCell" && place_of && self.b.t.write_in(ty).is_some() || self.b.t.boxed_object(ty) { let path = if owner == "raw" { "slice" } else { owner }; return Some(self.err( span, diff --git a/ts/src/lower/mod.rs b/ts/src/lower/mod.rs index a259c0cfb..026d9ba5c 100644 --- a/ts/src/lower/mod.rs +++ b/ts/src/lower/mod.rs @@ -1426,6 +1426,22 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { erased } + /// Whether `ty` is a `Box` of an object a whole write lands in, through any `Pin`s and + /// `AssertUnwindSafe`s, as a pinned `Box` is: a copy of the value there is another + /// allocation. + pub fn boxed_object(&self, ty: Ty<'tcx>) -> bool { + use library::Std; + let mut at = ty; + while let ty::Adt(adt, args) = at.kind() { + match Std::of(self.tcx, adt.did()) { + Some(Std::Box) => return self.write_in(ty).is_some(), + Some(Std::Pin | Std::AssertUnwindSafe) => at = args.type_at(0), + _ => break, + } + } + false + } + /// The kind of a `pointee`'s object, through the `Pin`s, `Box`es, and `AssertUnwindSafe`s /// JS erases, where a whole value of it can be written into the object in place: one only /// its owner holds, unlike an `Arc`'s or a `LocalKey`'s, which its copies share, and whose diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index 7bb0dbe44..a874f3e95 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -867,6 +867,17 @@ pub fn call<'tcx>( let value = arg(); replace(b, p, value, pointee(), span, out) } + // A whole write through the `&mut` lands in the allocation, which a value read out + // before could keep only as a copy, another allocation. + ("UnsafeCell" | "Cell", "get_mut") | ("UnsafeCell", "as_mut_unchecked") if b.t.boxed_object(pointee()) => { + let ty = pointee(); + b.err( + span, + format!( + "`{owner}::{name}` of `{ty}`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address" + ), + ) + } // What `get` points at, lent as `get_mut` lends it. ("UnsafeCell" | "Cell", "get_mut") | ("UnsafeCell", "as_mut_unchecked") if b.t.needs_place(pointee()) => arg(), ("UnsafeCell", "get_mut" | "as_ref_unchecked" | "as_mut_unchecked" | "into_inner") | ("Cell", "get_mut") => { From 797323dcb18fc852b7cf65677fff3768ddc79819 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 06:53:32 -0800 Subject: [PATCH 09/17] fix(ts): judge raw_get's known origins beside unknown ones, and keep nested cells through whole writes `raw_origins` now returns the known origins along with whether any is unknown, so a pointer to `c` or else null or a parameter still judges `c`, and `*p` too. A whole write through the pointer helpers (`UnsafeCell::replace`, `Cell`'s writes, `ptr::write`, `replace`, and `swap`) of a value that keeps cells writes into each existing cell in place, through cells at any depth and the cell fields of tuples and structs, so a pointer saved into an inner cell sees the new value. The write analysis counts these writes the same way. Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/dyns/expected/dyns.ts | 8 +- ts/fixtures/lang/pointers/expected/lib.ts | 27 +- .../lang/pointers/expected/pointers.Celled.ts | 11 + .../pointers/expected/pointers.Segment.ts | 19 +- .../lang/pointers/expected/pointers.ts | 296 +++++++++++++++++- ts/fixtures/lang/pointers/src/lib.rs | 3 + ts/fixtures/lang/pointers/src/pointers.rs | 108 +++++++ ts/fixtures/reject/census.txt | 14 +- ts/fixtures/reject/expected.txt | 14 +- ts/fixtures/reject/src/alloc.rs | 14 +- ts/fixtures/reject/src/kept.rs | 29 -- ts/fixtures/reject/ts.toml | 2 - ts/src/lower/copy.rs | 48 ++- ts/src/lower/mir/call.rs | 48 +-- ts/src/lower/pointer.rs | 190 ++++++++++- ts/src/lower/task.rs | 12 +- 16 files changed, 745 insertions(+), 98 deletions(-) create mode 100644 ts/fixtures/lang/pointers/expected/pointers.Celled.ts diff --git a/ts/fixtures/lang/dyns/expected/dyns.ts b/ts/fixtures/lang/dyns/expected/dyns.ts index a89507525..57297b26d 100644 --- a/ts/fixtures/lang/dyns/expected/dyns.ts +++ b/ts/fixtures/lang/dyns/expected/dyns.ts @@ -2208,7 +2208,13 @@ export function rests(dataData: Uint8Array, dataStart: number, dataLen: number, const thin: rs.Place> = rs.place(() => slot, (value22) => slot = value22); const wide: rs.Place> = rs.rawUnsizePlace(thin, (p12) => rs.tailDyn(p12, { v: (value23: rs.Cell) => new ofPlace$u32(value23) })); const null_: boolean = wide === rs.NULL; - thin.v = new rs.Cell(a as rs.u32); + const pointer = thin; + const value24 = new rs.Cell(a as rs.u32); + if (pointer.v === undefined) { + pointer.v = value24; + } else { + pointer.v.v = value24.v; + } wide.v.v.grow$Shape(b); const area5: rs.u32 = wide.v.v.area$Shape(); const _677: rs.u32 = slot.v; diff --git a/ts/fixtures/lang/pointers/expected/lib.ts b/ts/fixtures/lang/pointers/expected/lib.ts index e0bf9590c..56d783a3f 100644 --- a/ts/fixtures/lang/pointers/expected/lib.ts +++ b/ts/fixtures/lang/pointers/expected/lib.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays, arrayCasts, genericUnions } from "./pointers.ts"; +import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays, arrayCasts, genericUnions, rawCells, cellWrites, nestedCells } from "./pointers.ts"; import type * as Aimed from "./pointers.Aimed.ts"; import * as Bin from "./pointers.Bin.ts"; import type * as ByCall from "./pointers.ByCall.ts"; @@ -197,6 +197,10 @@ export function copy$tuple2_u8_sync_Arc_u8(v: [rs.u8, rs.Arc]): [rs.u8, r return [v[0], v[1]]; } +export function copy$tuple2_u8_u16(v: [rs.u8, rs.u16]): [rs.u8, rs.u16] { + return [v[0], v[1]]; +} + export function copy$tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs.u8] { return [v[0], v[1]]; } @@ -306,7 +310,28 @@ export function cases(): rs.Vec<[string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.By arrayCasts(data28, out28); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::generic_unions", (_238: rs.u8, data29: rs.ByteSlice, out29: rs.ByteVec): undefined => { genericUnions(data29, out29); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::raw_cells", (_239: rs.u8, data30: rs.ByteSlice, out30: rs.ByteVec): undefined => { + rawCells(data30, out30); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::cell_writes", (_240: rs.u8, data31: rs.ByteSlice, out31: rs.ByteVec): undefined => { + cellWrites(data31, out31); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::nested_cells", (_241: rs.u8, data32: rs.ByteSlice, out32: rs.ByteVec): undefined => { + nestedCells(data32, out32); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]]); } +/** + * `UnsafeCell::get` and `raw_get` of a cell of a slice, which only a caller can give, as + * translated code can't unsize a cell. + */ +export function cellSlice(c: rs.Cell>, at: rs.usize): rs.u16 { + const _4: rs.RawSlice = rs.rawSliceOf(c.v); + rs.idx(_4, at); + const slice = _4.deref(); + const _3: rs.u16 = slice.data[slice.start + at]; + const _9: rs.RawSlice = rs.rawSliceOf(c.v); + rs.idx(_9, 0); + const slice2 = _9.deref(); + return _3 ^ slice2.data[slice2.start]; +} + //# sourceMappingURL=lib.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.Celled.ts b/ts/fixtures/lang/pointers/expected/pointers.Celled.ts new file mode 100644 index 000000000..ca6835c66 --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.Celled.ts @@ -0,0 +1,11 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +/** A struct holding a cell beside a number. */ +export interface Celled { + c: rs.Cell; + n: rs.u8; +} + +//# sourceMappingURL=pointers.Celled.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.Segment.ts b/ts/fixtures/lang/pointers/expected/pointers.Segment.ts index 1d0029b17..f64c74703 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.Segment.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.Segment.ts @@ -27,7 +27,14 @@ function new_(): Segment { } const _15: Lent.Lent<[rs.u8, rs.u8]>[] = slots; const _13: Lent.Lent<[rs.u8, rs.u8]> = Lent.new$mem_MaybeUninit_tuple2_u8_u8(([undefined, undefined] as unknown) as [rs.u8, rs.u8]); - rs.ptrWrite(_15[at], _13); + const object = _15[at]; + const value = _13; + const cell = object._0; + rs.ptrWrite(object, value); + if (cell !== undefined) { + object._0 = cell; + rs.ptrWrite(cell.v, value._0.v); + } } return { filled: 0, slots }; } @@ -44,7 +51,15 @@ export function iterated(): Segment { break; } const uninit = _8; - rs.ptrWrite(uninit, Lent.new$mem_MaybeUninit_tuple2_u8_u8(([undefined, undefined] as unknown) as [rs.u8, rs.u8])); + const object = uninit; + const value = Lent.new$mem_MaybeUninit_tuple2_u8_u8(([undefined, undefined] as unknown) as [rs.u8, rs.u8]); + const cell = object._0; + rs.ptrWrite(object, value); + if (cell !== undefined) { + object._0 = cell; + rs.ptrWrite(cell.v, value._0.v); + } + undefined; uninit; } return { filled: 0, slots }; diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 17df0fce2..2fc103480 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -9,6 +9,7 @@ import * as Bin from "./pointers.Bin.ts"; import type * as ByCall from "./pointers.ByCall.ts"; import * as ByClosure from "./pointers.ByClosure.ts"; import type * as ByObject from "./pointers.ByObject.ts"; +import type * as Celled from "./pointers.Celled.ts"; import * as Counted from "./pointers.Counted.ts"; import type * as Duo from "./pointers.Duo.ts"; import type * as Either from "./pointers.Either.ts"; @@ -1991,7 +1992,14 @@ export function through$Cell_u8(nest: Enclosed.Enclosed>, n: rs.u } rs.byteVecPush(out, _43 | (_50 ? 1 : 0)); if (rs.u8Rem(n, 5) === 0) { - (nest.inner.at as rs.Place>).v = made$pointers_Aim_Cell_u8(n); + const pointer = nest.inner.at as rs.Place>; + const value = made$pointers_Aim_Cell_u8(n); + if (pointer.v === undefined) { + pointer.v = value; + } else { + pointer.v.v = value.v; + } + undefined; } rs.byteVecPush(out, get$pointers_Aim_Cell_u8((nest.inner.seen as rs.Place>).v)); } @@ -3621,4 +3629,290 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { count[Symbol.dispose](); } +/** + * Whole writes of values holding cells into a cell, a tuple, and a struct, each read back + * through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, + * `ptr::write`, `ptr::swap`, and `Cell`'s writes; a value `ptr::read` copies out before + * one; and `ptr::write`s into memory not written yet. + */ +export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { + const nest: rs.Cell> = new rs.Cell(new rs.Cell(1)); + const deep: rs.Cell>> = new rs.Cell(new rs.Cell(new rs.Cell(2))); + const pair: rs.Cell<[rs.Cell, rs.u8]> = new rs.Cell([new rs.Cell(3), 4] as [rs.Cell, rs.u8]); + const strukt: rs.Cell = new rs.Cell({ c: new rs.Cell(5), n: 6 }); + const obj: rs.Cell> = new rs.Cell(new rs.Cell([7, 8] as [rs.u8, rs.u8])); + const cell: rs.Cell<[rs.Cell, rs.u8]> = new rs.Cell([new rs.Cell(9), 10] as [rs.Cell, rs.u8]); + const wrapped: rs.Cell> = new rs.Cell(new rs.Cell(11)); + const toWrapped: rs.Place = wrapped.v; + const toNest: rs.Place = nest.v; + const toDeep: rs.Place = deep.v.v; + const toPair: rs.Place = pair.v[0]; + const toStrukt: rs.Place = strukt.v.c; + const toObj: [rs.u8, rs.u8] = obj.v.v; + const toCell: rs.Place = cell.v[0]; + for (const op of rs.byteSliceView(data)) { + const n: rs.u8 = rs.u8Div(op, 8); + const _61: rs.u8 = rs.u8Rem(op, 8); + if (_61 === 0) { + const _65: rs.Cell = new rs.Cell(n as rs.u32); + const pointer13 = nest; + const old16 = new rs.Cell(pointer13.v.v); + const pointer14 = pointer13; + const value17 = _65; + if (pointer14.v === undefined) { + pointer14.v = value17; + } else { + pointer14.v.v = value17.v; + } + undefined; + rs.byteVecPush(out, old16.v & 0xff); + const _72: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); + const pointer15 = deep; + const old17 = new rs.Cell(new rs.Cell(pointer15.v.v.v)); + const pointer16 = pointer15; + const value18 = _72; + if (pointer16.v === undefined) { + pointer16.v = value18; + } else { + pointer16.v.v.v = value18.v.v; + } + undefined; + rs.byteVecPush(out, old17.v.v & 0xff); + const _83: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; + const pointer17 = pair.v; + const object12 = pointer17; + const copy5 = rs.objectCopy(object12); + copy5[0] = new rs.Cell(object12[0].v); + const old18 = copy5; + const object13 = pointer17; + const value19 = _83; + const cell9 = object13[0]; + rs.ptrWrite(object13, value19); + if (cell9 !== undefined) { + object13[0] = cell9; + cell9.v = value19[0].v; + } + undefined; + const old3: [rs.Cell, rs.u8] = old18; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old3[0].v & 0xff, old3[1])), undefined, true); + const _96: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; + const pointer18 = strukt.v; + const object14 = pointer18; + const copy6 = rs.objectCopy(object14); + copy6.c = new rs.Cell(object14.c.v); + const old19 = copy6; + const object15 = pointer18; + const value20 = _96; + const cell10 = object15.c; + rs.ptrWrite(object15, value20); + if (cell10 !== undefined) { + object15.c = cell10; + cell10.v = value20.c.v; + } + undefined; + const old4: Celled.Celled = old19; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old4.c.v & 0xff, old4.n)), undefined, true); + const _109: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); + const pointer19 = obj; + const old20 = new rs.Cell(rs.objectCopy(pointer19.v.v)); + const pointer20 = pointer19; + const value21 = _109; + if (pointer20.v === undefined) { + pointer20.v = value21; + } else { + rs.ptrWrite(pointer20.v.v, value21.v); + } + undefined; + rs.byteVecPush(out, old20.v[1]); + } else if (_61 === 1) { + const _116: rs.Place> = nest; + const _118: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); + const pointer10 = _116; + const old14 = new rs.Cell(pointer10.v.v); + const pointer11 = pointer10; + const value15 = _118; + if (pointer11.v === undefined) { + pointer11.v = value15; + } else { + pointer11.v.v = value15.v; + } + undefined; + rs.byteVecPush(out, old14.v & 0xff); + const _125: [rs.Cell, rs.u8] = pair.v; + const _127: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; + const pointer12 = _125; + const object10 = pointer12; + const copy4 = rs.objectCopy(object10); + copy4[0] = new rs.Cell(object10[0].v); + const old15 = copy4; + const object11 = pointer12; + const value16 = _127; + const cell8 = object11[0]; + rs.ptrWrite(object11, value16); + if (cell8 !== undefined) { + object11[0] = cell8; + cell8.v = value16[0].v; + } + undefined; + rs.byteVecPush(out, old15[0].v & 0xff); + } else if (_61 === 2) { + const pointer9 = nest; + const value13 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); + if (pointer9.v === undefined) { + pointer9.v = value13; + } else { + pointer9.v.v = value13.v; + } + undefined; + const object9 = strukt.v; + const value14 = { c: new rs.Cell(((n as rs.u32) ^ 4) >>> 0), n }; + const cell7 = object9.c; + rs.ptrWrite(object9, value14); + if (cell7 !== undefined) { + object9.c = cell7; + cell7.v = value14.c.v; + } + undefined; + } else if (_61 === 3) { + const other: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 5) >>> 0)); + const x = nest; + const y = other; + const old12 = new rs.Cell(x.v.v); + const pointer7 = x; + const value9 = new rs.Cell(y.v.v); + if (pointer7.v === undefined) { + pointer7.v = value9; + } else { + pointer7.v.v = value9.v; + } + undefined; + const pointer8 = y; + const value10 = old12; + if (pointer8.v === undefined) { + pointer8.v = value10; + } else { + pointer8.v.v = value10.v; + } + undefined; + rs.byteVecPush(out, other.v.v & 0xff); + const other2: Celled.Celled = { c: new rs.Cell(((n as rs.u32) ^ 12) >>> 0), n: ~n & 0xff }; + const x2 = strukt.v; + const y2 = other2; + const object5 = x2; + const copy2 = rs.objectCopy(object5); + copy2.c = new rs.Cell(object5.c.v); + const old13 = copy2; + const object6 = y2; + const copy3 = rs.objectCopy(object6); + copy3.c = new rs.Cell(object6.c.v); + const object7 = x2; + const value11 = copy3; + const cell5 = object7.c; + rs.ptrWrite(object7, value11); + if (cell5 !== undefined) { + object7.c = cell5; + cell5.v = value11.c.v; + } + undefined; + const object8 = y2; + const value12 = old13; + const cell6 = object8.c; + rs.ptrWrite(object8, value12); + if (cell6 !== undefined) { + object8.c = cell6; + cell6.v = value12.c.v; + } + undefined; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(other2.c.v & 0xff, other2.n)), undefined, true); + } else if (_61 === 4) { + const _179: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; + const pointer6 = cell.v; + const object2 = pointer6; + const copy = rs.objectCopy(object2); + copy[0] = new rs.Cell(object2[0].v); + const old11 = copy; + const object3 = pointer6; + const value7 = _179; + const cell3 = object3[0]; + rs.ptrWrite(object3, value7); + if (cell3 !== undefined) { + object3[0] = cell3; + cell3.v = value7[0].v; + } + undefined; + rs.byteVecPush(out, old11[0].v & 0xff); + const _189: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; + const object4 = cell.v; + const value8 = _189; + const cell4 = object4[0]; + rs.ptrWrite(object4, value8); + if (cell4 !== undefined) { + object4[0] = cell4; + cell4.v = value8[0].v; + } + undefined; + } else if (_61 === 5) { + toNest.v = rs.u32Mul(n as rs.u32, 3); + toPair.v = rs.u32Mul(n as rs.u32, 5); + const kept: rs.Cell = new rs.Cell(nest.v.v); + const pointer5 = nest; + const value6 = new rs.Cell(rs.u32Add(n as rs.u32, 7)); + if (pointer5.v === undefined) { + pointer5.v = value6; + } else { + pointer5.v.v = value6.v; + } + undefined; + rs.byteVecPush(out, kept.v & 0xff); + } else if (_61 === 6) { + const _213: rs.Cell = new rs.Cell(((n as rs.u32) ^ 8) >>> 0); + const pointer = wrapped; + const old10 = new rs.Cell(pointer.v.v); + const pointer2 = pointer; + const value = _213; + if (pointer2.v === undefined) { + pointer2.v = value; + } else { + pointer2.v.v = value.v; + } + undefined; + rs.byteVecPush(out, old10.v & 0xff); + const _221: rs.Cell = new rs.Cell(((n as rs.u32) ^ 9) >>> 0); + const pointer3 = wrapped; + const value2 = _221; + if (pointer3.v === undefined) { + pointer3.v = value2; + } else { + pointer3.v.v = value2.v; + } + undefined; + let fresh: rs.Cell = (undefined as unknown) as rs.Cell; + const pointer4 = rs.place(() => fresh, (value3) => fresh = value3); + const value4 = new rs.Cell(((n as rs.u32) ^ 10) >>> 0); + if (pointer4.v === undefined) { + pointer4.v = value4; + } else { + pointer4.v.v = value4.v; + } + undefined; + const parts: [rs.Cell, rs.u8] = ([undefined, undefined] as unknown) as [rs.Cell, rs.u8]; + const object = parts; + const value5 = [new rs.Cell(((n as rs.u32) ^ 11) >>> 0), n] as [rs.Cell, rs.u8]; + const cell2 = object[0]; + rs.ptrWrite(object, value5); + if (cell2 !== undefined) { + object[0] = cell2; + cell2.v = value5[0].v; + } + undefined; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(fresh.v & 0xff, parts[0].v & 0xff)), undefined, true); + } else { + rs.byteVecPush(out, n); + } + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toNest.v & 0xff, toDeep.v & 0xff, toPair.v & 0xff, toStrukt.v & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toObj[0], toObj[1], toCell.v & 0xff, toWrapped.v & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(nest.v.v & 0xff, pair.v[1], strukt.v.n)), undefined, true); + } +} + //# sourceMappingURL=pointers.ts.map diff --git a/ts/fixtures/lang/pointers/src/lib.rs b/ts/fixtures/lang/pointers/src/lib.rs index dd044fcc6..7d61ab7a0 100644 --- a/ts/fixtures/lang/pointers/src/lib.rs +++ b/ts/fixtures/lang/pointers/src/lib.rs @@ -55,6 +55,9 @@ fn cases() -> Vec<(&'static str, Case)> { }), ("pointers::raw_cells", |_, data, out| pointers::raw_cells(data, out)), ("pointers::cell_writes", |_, data, out| pointers::cell_writes(data, out)), + ("pointers::nested_cells", |_, data, out| { + pointers::nested_cells(data, out) + }), ] } diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index 0bcb6255a..d66d23d6a 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2823,3 +2823,111 @@ pub fn cell_writes(data: &[u8], out: &mut Vec) { out.extend([pair.get().0, duo.get().1, owner.into_inner().n, bytes.get()[1]]); out.push(Rc::strong_count(&count) as u8); } + +/// A struct holding a cell beside a number. +struct Celled { + c: UnsafeCell, + n: u8, +} + +/// Whole writes of values holding cells into a cell, a tuple, and a struct, each read back +/// through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, +/// `ptr::write`, `ptr::swap`, and `Cell`'s writes; a value `ptr::read` copies out before +/// one; and `ptr::write`s into memory not written yet. +pub fn nested_cells(data: &[u8], out: &mut Vec) { + let nest = UnsafeCell::new(UnsafeCell::new(1u32)); + let deep = UnsafeCell::new(UnsafeCell::new(UnsafeCell::new(2u32))); + let pair = UnsafeCell::new((UnsafeCell::new(3u32), 4u8)); + let strukt = UnsafeCell::new(Celled { + c: UnsafeCell::new(5), + n: 6, + }); + let obj = UnsafeCell::new(UnsafeCell::new((7u8, 8u8))); + let cell = Cell::new((UnsafeCell::new(9u32), 10u8)); + let wrapped = Cell::new(UnsafeCell::new(11u32)); + unsafe { + let to_wrapped = (*wrapped.as_ptr()).get(); + let to_nest = (*nest.get()).get(); + let to_deep = (*(*deep.get()).get()).get(); + let to_pair = (*pair.get()).0.get(); + let to_strukt = (*strukt.get()).c.get(); + let to_obj = (*obj.get()).get(); + let to_cell = (*cell.as_ptr()).0.get(); + for &op in data { + let n = op / 8; + match op % 8 { + 0 => { + let old = nest.replace(UnsafeCell::new(u32::from(n))); + out.push(old.into_inner() as u8); + let old = deep.replace(UnsafeCell::new(UnsafeCell::new(u32::from(n) + 1))); + out.push(old.into_inner().into_inner() as u8); + let old = pair.replace((UnsafeCell::new(u32::from(n) + 2), n)); + out.extend([old.0.into_inner() as u8, old.1]); + let old = strukt.replace(Celled { + c: UnsafeCell::new(u32::from(n) + 3), + n, + }); + out.extend([old.c.into_inner() as u8, old.n]); + let old = obj.replace(UnsafeCell::new((n, n ^ 1))); + out.push(old.into_inner().1); + } + 1 => { + let old = ptr::replace(nest.get(), UnsafeCell::new(u32::from(n) ^ 1)); + out.push(old.into_inner() as u8); + let old = ptr::replace(pair.get(), (UnsafeCell::new(u32::from(n) ^ 2), n)); + out.push(old.0.into_inner() as u8); + } + 2 => { + ptr::write(nest.get(), UnsafeCell::new(u32::from(n) ^ 3)); + ptr::write( + strukt.get(), + Celled { + c: UnsafeCell::new(u32::from(n) ^ 4), + n, + }, + ); + } + 3 => { + let other = UnsafeCell::new(UnsafeCell::new(u32::from(n) ^ 5)); + ptr::swap(nest.get(), other.get()); + out.push(other.into_inner().into_inner() as u8); + let mut other = Celled { + c: UnsafeCell::new(u32::from(n) ^ 12), + n: !n, + }; + ptr::swap(strukt.get(), &raw mut other); + out.extend([other.c.into_inner() as u8, other.n]); + } + 4 => { + let old = cell.replace((UnsafeCell::new(u32::from(n) ^ 6), n)); + out.push(old.0.into_inner() as u8); + cell.set((UnsafeCell::new(u32::from(n) ^ 7), n)); + } + 5 => { + *to_nest = u32::from(n) * 3; + *to_pair = u32::from(n) * 5; + let kept = ptr::read(nest.get()); + ptr::write(nest.get(), UnsafeCell::new(u32::from(n) + 7)); + out.push(kept.into_inner() as u8); + } + 6 => { + let old = wrapped.replace(UnsafeCell::new(u32::from(n) ^ 8)); + out.push(old.into_inner() as u8); + wrapped.set(UnsafeCell::new(u32::from(n) ^ 9)); + let mut fresh = MaybeUninit::>::uninit(); + ptr::write(fresh.as_mut_ptr(), UnsafeCell::new(u32::from(n) ^ 10)); + let mut parts = MaybeUninit::<(UnsafeCell, u8)>::uninit(); + ptr::write(parts.as_mut_ptr(), (UnsafeCell::new(u32::from(n) ^ 11), n)); + out.extend([ + *fresh.assume_init_ref().get() as u8, + *parts.assume_init_ref().0.get() as u8, + ]); + } + _ => out.push(n), + } + out.extend([*to_nest as u8, *to_deep as u8, *to_pair as u8, *to_strukt as u8]); + out.extend([(*to_obj).0, (*to_obj).1, *to_cell as u8, *to_wrapped as u8]); + out.extend([(*nest.get()).get().read() as u8, (*pair.get()).1, (*strukt.get()).n]); + } + } +} diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index 150f3ba00..53f20997a 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -20,6 +20,8 @@ fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std fixtures/reject/src/alloc.rs:cell_raw_reassigned+4:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+6:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+7:25: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+9:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+11:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there 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:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another @@ -62,18 +64,16 @@ fixtures/reject/src/kept.rs:collected_parts+2:22: a reference through which a ra fixtures/reject/src/kept.rs:collected_parts+2:44: a reference through which a raw pointer may be made into `[u8; 2]` in `kept::Collected`, which the crate may put another object in place of fixtures/reject/src/kept.rs:collected_parts+7:14: a raw pointer to `(u8, u8)` in `kept::Collected`, which the crate may put another object in place of fixtures/reject/src/kept.rs:impl Clone for Copied+2:31: a raw pointer to `(u16, u16)` in `kept::Copied`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:keep_part+1:43: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Escaped`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:keep_part+1:43: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_across_iterations+10:18: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Repeated`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_across_writes+9:26: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Looped`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:kept_across_writes+9:26: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_boxed+5:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByBox`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_called+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByClosure`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_instance+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByInstance`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_object+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByObject`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_part+1:28: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Called`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:kept_part+1:28: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_read+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByRead`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_replaced+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByReplace`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_swapped+7:35: a raw pointer to `std::cell::UnsafeCell` in `kept::BySwap`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_written+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByWrite`, which the crate may put another object in place of fixtures/reject/src/kept.rs:key_replaced+2:55: a raw pointer to `kept::ByKeyReplace` in `(u16, kept::ByKeyReplace)`, which the crate may put another object in place of fixtures/reject/src/kept.rs:key_set+2:51: a raw pointer to `kept::ByKeySet` in `(u16, kept::ByKeySet)`, which the crate may put another object in place of fixtures/reject/src/kept.rs:key_taken+1:52: a raw pointer to `kept::ByKeyTake` in `(u16, kept::ByKeyTake)`, which the crate may put another object in place of @@ -87,8 +87,8 @@ fixtures/reject/src/kept.rs:ref_taken+2:32: a raw pointer to `kept::ByRefTake` i fixtures/reject/src/kept.rs:reversed+3:13: a raw pointer to `(u16, u16)` in `kept::Reversed`, which the crate may put another object in place of fixtures/reject/src/kept.rs:rezipped_parts+8:16: a reference through which a raw pointer may be made into `[u8; 3]` in `[[u8; 3]; 2]`, which the crate may put another object in place of fixtures/reject/src/kept.rs:uninit_rewritten+3:47: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Rewritten`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:written_through_alias+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Aliased`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:written_through_loaded+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Loaded`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:written_through_alias+3:29: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:written_through_loaded+3:29: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:zipped_parts+2:22: a reference through which a raw pointer may be made into `[u8; 2]` in `kept::Zipped`, which the crate may put another object in place of fixtures/reject/src/kept.rs:zipped_parts+2:44: a reference through which a raw pointer may be made into `[u8; 2]` in `kept::Zipped`, which the crate may put another object in place of fixtures/reject/src/kept.rs:zipped_parts+6:14: a raw pointer to `(u8, u8)` in `kept::Zipped`, 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 f6337265f..cbde63faf 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -24,6 +24,8 @@ fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std fixtures/reject/src/alloc.rs:cell_raw_reassigned+4:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+6:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+7:25: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+9:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there +fixtures/reject/src/alloc.rs:cell_raw_reassigned+11:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there 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:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another @@ -65,18 +67,16 @@ fixtures/reject/src/kept.rs:collected_parts+2:22: a reference through which a ra fixtures/reject/src/kept.rs:collected_parts+2:44: a reference through which a raw pointer may be made into `[u8; 2]` in `kept::Collected`, which the crate may put another object in place of fixtures/reject/src/kept.rs:collected_parts+7:14: a raw pointer to `(u8, u8)` in `kept::Collected`, which the crate may put another object in place of fixtures/reject/src/kept.rs:impl Clone for Copied+2:31: a raw pointer to `(u16, u16)` in `kept::Copied`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:keep_part+1:43: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Escaped`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:keep_part+1:43: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_across_iterations+10:18: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Repeated`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_across_writes+9:26: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Looped`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:kept_across_writes+9:26: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_boxed+5:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByBox`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_called+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByClosure`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_instance+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByInstance`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_object+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByObject`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_part+1:28: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Called`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:kept_part+1:28: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_read+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByRead`, which the crate may put another object in place of fixtures/reject/src/kept.rs:kept_replaced+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByReplace`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_swapped+7:35: a raw pointer to `std::cell::UnsafeCell` in `kept::BySwap`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:kept_written+6:35: a raw pointer to `std::cell::UnsafeCell` in `kept::ByWrite`, which the crate may put another object in place of fixtures/reject/src/kept.rs:key_replaced+2:55: a raw pointer to `kept::ByKeyReplace` in `(u16, kept::ByKeyReplace)`, which the crate may put another object in place of fixtures/reject/src/kept.rs:key_set+2:51: a raw pointer to `kept::ByKeySet` in `(u16, kept::ByKeySet)`, which the crate may put another object in place of fixtures/reject/src/kept.rs:key_taken+1:52: a raw pointer to `kept::ByKeyTake` in `(u16, kept::ByKeyTake)`, which the crate may put another object in place of @@ -90,8 +90,8 @@ fixtures/reject/src/kept.rs:ref_taken+2:32: a raw pointer to `kept::ByRefTake` i fixtures/reject/src/kept.rs:reversed+3:13: a raw pointer to `(u16, u16)` in `kept::Reversed`, which the crate may put another object in place of fixtures/reject/src/kept.rs:rezipped_parts+8:16: a reference through which a raw pointer may be made into `[u8; 3]` in `[[u8; 3]; 2]`, which the crate may put another object in place of fixtures/reject/src/kept.rs:uninit_rewritten+3:47: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Rewritten`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:written_through_alias+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Aliased`, which the crate may put another object in place of -fixtures/reject/src/kept.rs:written_through_loaded+3:29: a raw pointer to `std::cell::UnsafeCell<(u16, u16)>` in `kept::Loaded`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:written_through_alias+3:29: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of +fixtures/reject/src/kept.rs:written_through_loaded+3:29: a raw pointer to `(u16, u16)` in `std::cell::UnsafeCell<(u16, u16)>`, which the crate may put another object in place of fixtures/reject/src/kept.rs:zipped_parts+2:22: a reference through which a raw pointer may be made into `[u8; 2]` in `kept::Zipped`, which the crate may put another object in place of fixtures/reject/src/kept.rs:zipped_parts+2:44: a reference through which a raw pointer may be made into `[u8; 2]` in `kept::Zipped`, which the crate may put another object in place of fixtures/reject/src/kept.rs:zipped_parts+6:14: a raw pointer to `(u8, u8)` in `kept::Zipped`, which the crate may put another object in place of diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index d09c857b6..a35e5bdff 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -66,10 +66,10 @@ pub fn cell_pointer_reassigned(n: u32) -> u32 { unsafe { *p } } -/// Pointers `UnsafeCell::raw_get` makes of a raw pointer to a cell, and of one to either -/// of two, and `get` through the first, read after the variable holding the cell is given -/// another. -pub fn cell_raw_reassigned(n: u32) -> u32 { +/// Pointers `UnsafeCell::raw_get` makes of a raw pointer to a cell, of one to either of +/// two, and of one to it or else null or a parameter, and `get` through the first, read +/// after the variable holding the cell is given another. +pub fn cell_raw_reassigned(n: u32, given: *const std::cell::UnsafeCell) -> u32 { let mut c = std::cell::UnsafeCell::new(n); let d = std::cell::UnsafeCell::new(n ^ 2); let to = &raw const c; @@ -77,9 +77,13 @@ pub fn cell_raw_reassigned(n: u32) -> u32 { let either = if n > 3 { &raw const c } else { &raw const d }; let q = unsafe { std::cell::UnsafeCell::raw_get(either) }; let r = unsafe { (*to).get() }; + let or_null = if n > 5 { &raw const c } else { std::ptr::null() }; + let s = unsafe { std::cell::UnsafeCell::raw_get(or_null) }; + let or_given = if n > 7 { &raw const c } else { given }; + let t = unsafe { std::cell::UnsafeCell::raw_get(or_given) }; c = std::cell::UnsafeCell::new(n ^ 1); let _ = (&c, &d); - unsafe { *p ^ *q ^ *r } + unsafe { *p ^ *q ^ *r ^ *s ^ *t } } /// A `get_mut` and an `as_mut_unchecked` of a cell of a pinned `Box`, whose old value diff --git a/ts/fixtures/reject/src/kept.rs b/ts/fixtures/reject/src/kept.rs index 5a8e93ab3..c420f5f15 100644 --- a/ts/fixtures/reject/src/kept.rs +++ b/ts/fixtures/reject/src/kept.rs @@ -208,10 +208,6 @@ pub fn pushed_part_kept(n: u16) -> u16 { /// may still be read. pub struct ByRead(pub std::cell::UnsafeCell); -pub struct ByWrite(pub std::cell::UnsafeCell); - -pub struct BySwap(pub std::cell::UnsafeCell); - pub struct ByReplace(pub std::cell::UnsafeCell); pub struct ByBox(pub std::cell::UnsafeCell); @@ -260,31 +256,6 @@ pub fn kept_read(n: u16) -> u16 { unsafe { *part } } -/// Read back from memory, then written whole by `ptr::write`. -pub fn kept_written(n: u16) -> u16 { - let mut whole = ByWrite(cell(n)); - let mut kept = Kept { - at: std::ptr::null_mut(), - }; - kept.at = &raw mut whole; - let part = unsafe { (*kept.at).0.get() }; - unsafe { kept.at.write(ByWrite(cell(0))) }; - unsafe { *part } -} - -/// Read back from memory, then swapped whole by `ptr::swap`. -pub fn kept_swapped(n: u16) -> u16 { - let mut whole = BySwap(cell(n)); - let mut other = BySwap(cell(0)); - let mut kept = Kept { - at: std::ptr::null_mut(), - }; - kept.at = &raw mut whole; - let part = unsafe { (*kept.at).0.get() }; - unsafe { std::ptr::swap(kept.at, &raw mut other) }; - unsafe { *part } -} - /// Read back from memory, then replaced whole by `mem::replace` through a `&mut`. pub fn kept_replaced(n: u16) -> u16 { let mut whole = ByReplace(cell(n)); diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index c5179bf27..61b3d24d8 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -55,8 +55,6 @@ roots = [ "kept::kept_object", "kept::kept_read", "kept::kept_replaced", - "kept::kept_swapped", - "kept::kept_written", "kept::key_replaced", "kept::key_set", "kept::key_taken", diff --git a/ts/src/lower/copy.rs b/ts/src/lower/copy.rs index 448127d7a..06eecea89 100644 --- a/ts/src/lower/copy.rs +++ b/ts/src/lower/copy.rs @@ -521,6 +521,7 @@ impl<'tcx> Writes<'tcx> { } BodyWrites { tcx, + t, body, def_id, writes: &mut writes, @@ -676,6 +677,18 @@ fn cell_writes<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef< (writes && super::library::is_std(tcx, def_id)).then(|| args.type_at(0)) } +/// The type std's `def_id` writes whole through a raw pointer to one, by the runtime's +/// write of a pointer's value: `ptr::write`, `replace`, or `swap`. +fn pointer_writes<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Option> { + let (owner, name) = super::library::key(tcx, def_id)?; + let writes = owner == "ptr" + && matches!( + name.as_str(), + "write" | "write_unaligned" | "write_volatile" | "replace" | "swap" + ); + (writes && super::library::is_std(tcx, def_id)).then(|| args.type_at(0)) +} + /// The place each local of `body` borrows, where its one assignment is a borrow and /// nothing borrows the local itself, through which it could be pointed elsewhere. fn borrows<'tcx>(body: &mir::Body<'tcx>) -> IndexVec>> { @@ -922,6 +935,7 @@ impl<'tcx> Visitor<'tcx> for UsesOf<'_> { /// The writes of one body. struct BodyWrites<'w, 'm, 'a, 'tcx> { tcx: TyCtxt<'tcx>, + t: &'m Translator<'a, 'tcx>, body: &'m mir::Body<'tcx>, def_id: DefId, writes: &'w mut Writes<'tcx>, @@ -952,6 +966,30 @@ impl<'tcx> BodyWrites<'_, '_, '_, 'tcx> { } } + /// A whole `ty` written into in place by the runtime's write of a cell's value, which + /// keeps each cell it [keeps](Translator::keeps_cells), writing into it in place too, + /// and replaces the rest as [`Self::replace`] does. + fn replace_kept(&mut self, ty: Ty<'tcx>) { + use super::pointer::KeptCells; + self.insert(ty); + match self.t.kept_cells(ty) { + Some(KeptCells::Cell(held)) => self.replace_kept(held), + Some(KeptCells::Fields(fields)) => { + for (i, (field, kept)) in fields.into_iter().enumerate() { + match kept { + true => self.replace_kept(field), + false => { + let holders = self.writes.holders.entry(Some(FieldIdx::from_usize(i))).or_default(); + holders.insert(self.tcx, self.def_id, ty); + self.replace(field); + } + } + } + } + None => self.replace(ty), + } + } + /// `ty` and each type it holds by value, at any depth, a part of which may be kept as a /// JS object. fn parts(&self, ty: Ty<'tcx>) -> Vec> { @@ -1215,11 +1253,15 @@ impl<'tcx> Visitor<'tcx> for BodyWrites<'_, '_, '_, 'tcx> { self.replace(value); } if let Some(held) = cell_writes(self.tcx, def_id, args.skip_binder()) { - self.insert(held); - self.replace(held); + self.replace_kept(held); + } + // A pointer's whole write keeps the cells a value keeps, as a cell's does. + let kept = pointer_writes(self.tcx, def_id, args.skip_binder()).filter(|&ty| self.t.keeps_cells(ty)); + if let Some(ty) = kept { + self.replace_kept(ty); } // Those that point a pointer elsewhere or read through it replace nothing. - let replace = !super::pointer::keeps(self.tcx, def_id, args.skip_binder()); + let replace = kept.is_none() && !super::pointer::keeps(self.tcx, def_id, args.skip_binder()); // A call's argument may point into memory nothing else points into yet. let body = self.body; let call = match &body[location.block].terminator().kind { diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 5a3215b28..027fe621d 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -340,8 +340,8 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { return err; } // The cell `get`'s reference or `raw_get`'s pointer points at is judged as each - // place a pointer it is made through may have been made of, where every - // assignment of that pointer is known. + // place a pointer it is made through may have been made of, and as what it + // points at where not every assignment of that pointer is known. let raw_get = library::key(tcx, def_id).is_some_and(|(o, n)| o == "UnsafeCell" && n == "raw_get"); let pointees = |p: Place<'tcx>| { let local = p.as_local(); @@ -355,9 +355,11 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { _ => None, }; let pointer = if raw_get { local } else { local.and_then(reborrowed) }; - pointer - .and_then(|l| self.raw_origins(l, &mut Vec::new())) - .unwrap_or_else(|| vec![tcx.mk_place_deref(p)]) + let mut origins = Vec::new(); + if !pointer.is_some_and(|l| self.raw_origins(l, &mut Vec::new(), &mut origins)) { + origins.push(tcx.mk_place_deref(p)); + } + origins }; if (raw_get || crate::lower::pointer::from_reference(tcx, def_id, gargs)) && let Some(why) = args[0].node.place().and_then(|p| { @@ -1427,22 +1429,23 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { Some(borrowed) } - /// The places the raw pointer `local` may have been made of, by `&raw` or a reference, - /// through copies and casts, or `None` if any assignment of it is something else. + /// Pushes to `origins` the places the raw pointer `local` may have been made of, by + /// `&raw` or a reference, through copies and casts, and returns whether those are all + /// of them, which they are not if it is a parameter, is written through, or any + /// assignment of it is something else. fn raw_origins( &self, local: rustc_middle::mir::Local, seen: &mut Vec, - ) -> Option>> { + origins: &mut Vec>, + ) -> bool { use rustc_middle::mir::{Rvalue, StatementKind}; if seen.contains(&local) { - return Some(Vec::new()); + return true; } seen.push(local); - if (1..=self.mir.arg_count).contains(&local.as_usize()) || self.mutated[local] { - return None; - } - let (mut found, mut origins) = (0, Vec::new()); + let mut known = !(1..=self.mir.arg_count).contains(&local.as_usize()) && !self.mutated[local]; + let mut found = 0; for (bb, data) in self.mir.basic_blocks.iter_enumerated() { for (i, stmt) in data.statements.iter().enumerate() { let StatementKind::Assign(assign) = &stmt.kind else { @@ -1456,17 +1459,20 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { continue; } found += 1; - match rvalue { - Rvalue::RawPtr(_, made) | Rvalue::Ref(_, _, made) => origins.push(*made), - Rvalue::Use(Operand::Copy(from) | Operand::Move(from), _) - | Rvalue::Cast(_, Operand::Copy(from) | Operand::Move(from), _) => { - origins.extend(self.raw_origins(from.as_local()?, seen)?) + known &= match rvalue { + Rvalue::RawPtr(_, made) | Rvalue::Ref(_, _, made) => { + origins.push(*made); + true } - _ => return None, - } + Rvalue::Use(Operand::Copy(from) | Operand::Move(from), _) + | Rvalue::Cast(_, Operand::Copy(from) | Operand::Move(from), _) => from + .as_local() + .is_some_and(|from| self.raw_origins(from, seen, origins)), + _ => false, + }; } } - (found == self.defs[local]).then_some(origins) + known && found == self.defs[local] } /// `slice::from_mut`, `array::from_mut`, or `from_mut` of `owner`, `Cell` or diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index c78ec7a21..de8a7fc9b 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -38,6 +38,14 @@ use super::library::{Call, Std, targ}; use super::{AdtRepr, ObjectKind, Translator}; use crate::ts::{Expr, Key, Stmt, StmtKind, Type}; +/// What a type that [keeps cells](Translator::keeps_cells) holds. +pub enum KeptCells<'tcx> { + /// A cell's value. + Cell(Ty<'tcx>), + /// Each field's type, and whether it keeps cells. + Fields(Vec<(Ty<'tcx>, bool)>), +} + /// What a raw pointer to a type is in JS. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Pointee { @@ -156,6 +164,32 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { ty } + /// Whether a whole write of a `ty` through the pointer helpers below keeps each + /// `rs.Cell` it holds, writing into it in place, so a pointer `get` gave into one before + /// sees the write: a cell, or a tuple or a crate's struct a pointer is the object of, a + /// field of which keeps cells. Any other part is given the new value's, one object deep. + pub fn keeps_cells(&self, ty: Ty<'tcx>) -> bool { + self.kept_cells(ty).is_some() + } + + /// What a `ty` that [keeps cells](Self::keeps_cells) holds: a cell's value, or each + /// field of a tuple or struct, and whether it keeps cells too. + pub fn kept_cells(&self, ty: Ty<'tcx>) -> Option> { + let fields: Vec> = match ty.kind() { + ty::Adt(adt, args) if Std::of(self.tcx, adt.did()) == Some(Std::Cell) => { + return Some(KeptCells::Cell(args.type_at(0))); + } + ty::Tuple(tys) => tys.to_vec(), + ty::Adt(adt, args) if adt.is_struct() && Std::of(self.tcx, adt.did()).is_none() && self.own(adt.did()) => { + adt.all_fields().map(|f| self.field_ty(f.did, args)).collect() + } + _ => return None, + }; + let fields: Vec<_> = fields.into_iter().map(|f| (f, self.keeps_cells(f))).collect(); + (self.pointee(ty) == Pointee::Object && fields.iter().any(|&(_, kept)| kept)) + .then_some(KeptCells::Fields(fields)) + } + /// Whether only an instance decides what a pointer or reference to `ty` is, as in a /// generic type's definition: `ty` a type parameter, an array of one, either through /// `MaybeUninit`s, or a binding's generic type, which an instance decides is `Copy`. @@ -739,11 +773,11 @@ pub fn call<'tcx>( }, ("ptr", "read" | "read_unaligned" | "read_volatile") => { let p = arg(); - read(b, p, pointee(), span) + read(b, p, pointee(), span, out) } ("ptr", "write" | "write_unaligned" | "write_volatile") => { let (p, value) = (arg(), arg()); - write(b, p, value, pointee(), span) + write(b, p, value, pointee(), span, out) } // A place of no bytes has nothing to read or write, and every value of its type is // the one it holds. @@ -806,7 +840,7 @@ pub fn call<'tcx>( let value = arg(); let write = match array(b, pointee()) { true => Expr::rs("ptrWrite", vec![p.clone(), value]), - false => write(b, p.clone(), value, pointee(), span), + false => write(b, p.clone(), value, pointee(), span, out), }; out.push(StmtKind::Expr(write).at(None)); p @@ -1187,7 +1221,11 @@ pub(super) fn cell_pointer<'tcx>(b: &Body<'_, '_, 'tcx>, cell: Expr, ty: Ty<'tcx } /// The value `p`, a pointer to `ty`, points at, as a value of its own. -pub(super) fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { +pub(super) fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span, out: &mut Vec) -> Expr { + if b.t.keeps_cells(ty) { + let at = pointed(b, p, ty); + return copy_kept(b, at, ty, span, out); + } let value = match b.t.pointee(ty) { Pointee::Place => p.member("v"), Pointee::Object => p, @@ -1211,9 +1249,9 @@ pub(super) fn replace<'tcx>( out: &mut Vec, ) -> Expr { let p = b.spill(p, "pointer", out); - let old = copied_out(b, p.clone(), ty, span); + let old = copied_out(b, p.clone(), ty, span, out); let old = b.spill(old, "old", out); - let write = write(b, p, value, ty, span); + let write = write(b, p, value, ty, span, out); out.push(StmtKind::Expr(write).at(None)); old } @@ -1223,16 +1261,16 @@ pub(super) fn replace<'tcx>( /// copied one object deep, an array's elements to the depth `objectCopy` copies them. One /// a pointer is the place of is read as it is, keeping a `Box`'s allocation, as its write /// puts another in the place. -fn copied_out<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { +fn copied_out<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span, out: &mut Vec) -> Expr { match b.t.pointee(ty) { - _ if b.t.zero_sized(ty) => read(b, p, ty, span), + _ if b.t.zero_sized(ty) || b.t.keeps_cells(ty) => read(b, p, ty, span, out), Pointee::Object => Expr::rs("objectCopy", vec![p]), Pointee::Array if !super::byte_array(b.t.uninit_inner(ty)) => { let depth = b.t.copy_depth(b.t.uninit_inner(ty)); let view = super::library::slice_view(p); Expr::rs("objectCopy", std::iter::once(view).chain(depth).collect()) } - _ => read(b, p, ty, span), + _ => read(b, p, ty, span, out), } } @@ -1246,19 +1284,47 @@ pub(super) fn swap<'tcx>( out: &mut Vec, ) -> Expr { let (x, y) = (b.spill(x, "x", out), b.spill(y, "y", out)); - let old = copied_out(b, x.clone(), ty, span); + let old = copied_out(b, x.clone(), ty, span, out); let old = b.spill(old, "old", out); - let from_y = copied_out(b, y.clone(), ty, span); - let into_x = write(b, x, from_y, ty, span); + let from_y = copied_out(b, y.clone(), ty, span, out); + let into_x = write(b, x, from_y, ty, span, out); out.push(StmtKind::Expr(into_x).at(None)); - write(b, y, old, ty, span) + write(b, y, old, ty, span, out) } /// The write of `value`, a `ty`, where `p` points. -pub(super) fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { +pub(super) fn write<'tcx>( + b: &mut Body<'_, '_, 'tcx>, + p: Expr, + value: Expr, + ty: Ty<'tcx>, + span: Span, + out: &mut Vec, +) -> Expr { if b.t.zero_sized(ty) { return Expr::undefined(); } + if b.t.keeps_cells(ty) { + if b.t.pointee(ty) == Pointee::Object { + write_kept(b, p, value, ty, span, out); + return Expr::undefined(); + } + // Memory not written yet, as `ptr::write` may fill, holds no cell to keep. + let (p, value) = (b.spill(p, "pointer", out), b.spill(value, "value", out)); + let mut kept = Vec::new(); + write_kept(b, p.clone().member("v"), value.clone(), ty, span, &mut kept); + let whole = p.clone().member("v").assign(value); + let cond = p.member("v").binary("===", Expr::undefined()); + out.push( + StmtKind::If { + cond, + then: vec![StmtKind::Expr(whole).at(None)], + els: kept, + } + .at(None), + ); + return Expr::undefined(); + } match b.t.pointee(ty) { Pointee::Place => p.member("v").assign(value), Pointee::Object => Expr::rs("ptrWrite", vec![p, value]), @@ -1267,6 +1333,102 @@ pub(super) fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: } } +/// The JS value of the `ty` that `p` points at: a cell's `rs.Cell`, which a pointer to +/// it is the place of, or the object a pointer to it is. +fn pointed<'tcx>(b: &Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>) -> Expr { + match b.t.pointee(ty) { + Pointee::Place => p.member("v"), + _ => p, + } +} + +/// The write of `value` into `at`, the JS value of a `ty` that keeps cells, in place: a +/// cell's value written into the cell, as a write through its `get` would be, and a +/// tuple's or struct's other fields given `value`'s, as is a cell field of one not +/// written yet. +fn write_kept<'tcx>(b: &mut Body<'_, '_, 'tcx>, at: Expr, value: Expr, ty: Ty<'tcx>, span: Span, out: &mut Vec) { + match b.t.kept_cells(ty) { + Some(KeptCells::Cell(held)) if b.t.keeps_cells(held) => { + write_kept(b, at.member("v"), value.member("v"), held, span, out) + } + Some(KeptCells::Cell(held)) => { + let p = cell_pointer(b, at, held); + let write = write(b, p, value.member("v"), held, span, out); + out.push(StmtKind::Expr(write).at(None)); + } + Some(KeptCells::Fields(fields)) => { + let (at, value) = (b.spill(at, "object", out), b.spill(value, "value", out)); + let kept: Vec<_> = (fields.iter().enumerate()) + .filter(|(_, (_, kept))| *kept) + .map(|(i, &(field, _))| { + let part = object_field(b, at.clone(), ty, i, span); + (i, field, b.spill(part, "cell", out)) + }) + .collect(); + out.push(StmtKind::Expr(Expr::rs("ptrWrite", vec![at.clone(), value.clone()])).at(None)); + for (i, field, cell) in kept { + let part = object_field(b, at.clone(), ty, i, span); + let mut then = vec![StmtKind::Expr(part.assign(cell.clone())).at(None)]; + let from = object_field(b, value.clone(), ty, i, span); + write_kept(b, cell.clone(), from, field, span, &mut then); + let cond = cell.binary("!==", Expr::undefined()); + out.push( + StmtKind::If { + cond, + then, + els: Vec::new(), + } + .at(None), + ); + } + } + None => unreachable!("`{ty}` keeps no cells"), + } +} + +/// A copy of `at`, the JS value of a `ty` that keeps cells, with a cell of its own for +/// each it keeps, so a write into those in place leaves the copy be. +fn copy_kept<'tcx>(b: &mut Body<'_, '_, 'tcx>, at: Expr, ty: Ty<'tcx>, span: Span, out: &mut Vec) -> Expr { + match b.t.kept_cells(ty) { + Some(KeptCells::Cell(held)) => { + let value = match b.t.keeps_cells(held) { + true => copy_kept(b, at.member("v"), held, span, out), + false => { + let p = cell_pointer(b, at, held); + copied_out(b, p, held, span, out) + } + }; + Expr::New(Box::new(Expr::Rs("Cell".into())), vec![value]) + } + Some(KeptCells::Fields(fields)) => { + let at = b.spill(at, "object", out); + let copy = b.spill(Expr::rs("objectCopy", vec![at.clone()]), "copy", out); + for (i, &(field, kept)) in fields.iter().enumerate() { + if kept { + let from = object_field(b, at.clone(), ty, i, span); + let cell = copy_kept(b, from, field, span, out); + let part = object_field(b, copy.clone(), ty, i, span); + out.push(StmtKind::Expr(part.assign(cell)).at(None)); + } + } + copy + } + None => unreachable!("`{ty}` keeps no cells"), + } +} + +/// Field `i` of `object`, the JS object of a tuple or a crate's struct `ty`. +fn object_field<'tcx>(b: &mut Body<'_, '_, 'tcx>, object: Expr, ty: Ty<'tcx>, i: usize, span: Span) -> Expr { + match ty.kind() { + ty::Tuple(_) => object.index(Expr::lit(i.to_string())), + ty::Adt(adt, _) => match b.t.adt(*adt, span).repr { + AdtRepr::Struct { fields, .. } => Translator::read_field(object, fields[i]), + _ => unreachable!("`{ty}` is a struct"), + }, + _ => unreachable!("`{ty}` is a tuple or a struct"), + } +} + /// Whether `ty` is `[u8]`, one of `MaybeUninit`, or an `UninitSlice`, whose slices are /// `rs.ByteSlice`s. pub fn slice_of_bytes(ty: Ty<'_>) -> bool { diff --git a/ts/src/lower/task.rs b/ts/src/lower/task.rs index 1720f9cfd..a851c94cc 100644 --- a/ts/src/lower/task.rs +++ b/ts/src/lower/task.rs @@ -593,8 +593,10 @@ fn cell<'tcx>( let tcx = b.t.tcx; let span = call.span; let ty = call.args.type_at(0); - // A pointer `as_ptr` gave to an object or an array is it, so a write lands in it. - let in_place = matches!(b.t.pointee(ty), Pointee::Object | Pointee::Array) && !b.t.zero_sized(ty); + // A pointer `as_ptr` gave to an object or an array is it, so a write lands in it, as it + // does in each cell a value keeps. + let in_place = + (matches!(b.t.pointee(ty), Pointee::Object | Pointee::Array) || b.t.keeps_cells(ty)) && !b.t.zero_sized(ty); let default = |b: &mut Body<'_, '_, 'tcx>, out: &mut Vec| { let f = super::collections::trait_fn(tcx, "Default", "default"); b.call_def(f, tcx.mk_args(&[ty.into()]), Vec::new(), span, out) @@ -614,7 +616,7 @@ fn cell<'tcx>( let old = pointer::replace(b, p, value, ty, span, out); b.t.drop_value(old, ty, span).unwrap_or_else(Expr::undefined) } - false => pointer::write(b, p, value, ty, span), + false => pointer::write(b, p, value, ty, span, out), } } "replace" | "take" if in_place => { @@ -629,8 +631,8 @@ fn cell<'tcx>( "update" if in_place => { let [cell, f] = two(args); let p = b.spill(pointer::cell_pointer(b, cell, ty), "pointer", out); - let new = f.call(vec![pointer::read(b, p.clone(), ty, span)]); - pointer::write(b, p, new, ty, span) + let new = f.call(vec![pointer::read(b, p.clone(), ty, span, out)]); + pointer::write(b, p, new, ty, span, out) } "swap" => { let [x, y] = two(args); From 95f8bbdf4c148b23a3c43e32525d0f8eb9f0ae7e Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 07:22:23 -0800 Subject: [PATCH 10/17] fix(ts): keep nested cells through a whole write through a raw pointer An assignment through a raw pointer of a value that keeps cells writes into the existing cells in place, as `ptr::write` does, so `*c.get() = UnsafeCell::new(2)` leaves a pointer into the old inner cell reading 2. The write analysis still counts such stores as replacing, which is sound. Co-Authored-By: Claude Opus 5.5 --- .../lang/pointers/expected/pointers.ts | 189 ++++++++++-------- ts/fixtures/lang/pointers/src/pointers.rs | 9 +- ts/src/lower/mir/place.rs | 12 ++ ts/src/lower/pointer.rs | 52 +++-- 4 files changed, 154 insertions(+), 108 deletions(-) diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 2fc103480..14bb96199 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -3632,8 +3632,8 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { /** * Whole writes of values holding cells into a cell, a tuple, and a struct, each read back * through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, - * `ptr::write`, `ptr::swap`, and `Cell`'s writes; a value `ptr::read` copies out before - * one; and `ptr::write`s into memory not written yet. + * `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a + * value `ptr::read` copies out before one; and `ptr::write`s into memory not written yet. */ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const nest: rs.Cell> = new rs.Cell(new rs.Cell(1)); @@ -3652,146 +3652,146 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const toCell: rs.Place = cell.v[0]; for (const op of rs.byteSliceView(data)) { const n: rs.u8 = rs.u8Div(op, 8); - const _61: rs.u8 = rs.u8Rem(op, 8); - if (_61 === 0) { - const _65: rs.Cell = new rs.Cell(n as rs.u32); + const _60: rs.u8 = rs.u8Rem(op, 8); + if (_60 === 0) { + const _64: rs.Cell = new rs.Cell(n as rs.u32); const pointer13 = nest; const old16 = new rs.Cell(pointer13.v.v); const pointer14 = pointer13; - const value17 = _65; + const value19 = _64; if (pointer14.v === undefined) { - pointer14.v = value17; + pointer14.v = value19; } else { - pointer14.v.v = value17.v; + pointer14.v.v = value19.v; } undefined; rs.byteVecPush(out, old16.v & 0xff); - const _72: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); + const _71: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); const pointer15 = deep; const old17 = new rs.Cell(new rs.Cell(pointer15.v.v.v)); const pointer16 = pointer15; - const value18 = _72; + const value20 = _71; if (pointer16.v === undefined) { - pointer16.v = value18; + pointer16.v = value20; } else { - pointer16.v.v.v = value18.v.v; + pointer16.v.v.v = value20.v.v; } undefined; rs.byteVecPush(out, old17.v.v & 0xff); - const _83: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; + const _82: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; const pointer17 = pair.v; const object12 = pointer17; const copy5 = rs.objectCopy(object12); copy5[0] = new rs.Cell(object12[0].v); const old18 = copy5; const object13 = pointer17; - const value19 = _83; + const value21 = _82; const cell9 = object13[0]; - rs.ptrWrite(object13, value19); + rs.ptrWrite(object13, value21); if (cell9 !== undefined) { object13[0] = cell9; - cell9.v = value19[0].v; + cell9.v = value21[0].v; } undefined; const old3: [rs.Cell, rs.u8] = old18; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old3[0].v & 0xff, old3[1])), undefined, true); - const _96: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; + const _95: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; const pointer18 = strukt.v; const object14 = pointer18; const copy6 = rs.objectCopy(object14); copy6.c = new rs.Cell(object14.c.v); const old19 = copy6; const object15 = pointer18; - const value20 = _96; + const value22 = _95; const cell10 = object15.c; - rs.ptrWrite(object15, value20); + rs.ptrWrite(object15, value22); if (cell10 !== undefined) { object15.c = cell10; - cell10.v = value20.c.v; + cell10.v = value22.c.v; } undefined; const old4: Celled.Celled = old19; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old4.c.v & 0xff, old4.n)), undefined, true); - const _109: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); + const _108: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); const pointer19 = obj; const old20 = new rs.Cell(rs.objectCopy(pointer19.v.v)); const pointer20 = pointer19; - const value21 = _109; + const value23 = _108; if (pointer20.v === undefined) { - pointer20.v = value21; + pointer20.v = value23; } else { - rs.ptrWrite(pointer20.v.v, value21.v); + rs.ptrWrite(pointer20.v.v, value23.v); } undefined; rs.byteVecPush(out, old20.v[1]); - } else if (_61 === 1) { - const _116: rs.Place> = nest; - const _118: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); - const pointer10 = _116; + } else if (_60 === 1) { + const _115: rs.Place> = nest; + const _117: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); + const pointer10 = _115; const old14 = new rs.Cell(pointer10.v.v); const pointer11 = pointer10; - const value15 = _118; + const value17 = _117; if (pointer11.v === undefined) { - pointer11.v = value15; + pointer11.v = value17; } else { - pointer11.v.v = value15.v; + pointer11.v.v = value17.v; } undefined; rs.byteVecPush(out, old14.v & 0xff); - const _125: [rs.Cell, rs.u8] = pair.v; - const _127: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; - const pointer12 = _125; + const _124: [rs.Cell, rs.u8] = pair.v; + const _126: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; + const pointer12 = _124; const object10 = pointer12; const copy4 = rs.objectCopy(object10); copy4[0] = new rs.Cell(object10[0].v); const old15 = copy4; const object11 = pointer12; - const value16 = _127; + const value18 = _126; const cell8 = object11[0]; - rs.ptrWrite(object11, value16); + rs.ptrWrite(object11, value18); if (cell8 !== undefined) { object11[0] = cell8; - cell8.v = value16[0].v; + cell8.v = value18[0].v; } undefined; rs.byteVecPush(out, old15[0].v & 0xff); - } else if (_61 === 2) { + } else if (_60 === 2) { const pointer9 = nest; - const value13 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); + const value15 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); if (pointer9.v === undefined) { - pointer9.v = value13; + pointer9.v = value15; } else { - pointer9.v.v = value13.v; + pointer9.v.v = value15.v; } undefined; const object9 = strukt.v; - const value14 = { c: new rs.Cell(((n as rs.u32) ^ 4) >>> 0), n }; + const value16 = { c: new rs.Cell(((n as rs.u32) ^ 4) >>> 0), n }; const cell7 = object9.c; - rs.ptrWrite(object9, value14); + rs.ptrWrite(object9, value16); if (cell7 !== undefined) { object9.c = cell7; - cell7.v = value14.c.v; + cell7.v = value16.c.v; } undefined; - } else if (_61 === 3) { + } else if (_60 === 3) { const other: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 5) >>> 0)); const x = nest; const y = other; const old12 = new rs.Cell(x.v.v); const pointer7 = x; - const value9 = new rs.Cell(y.v.v); + const value11 = new rs.Cell(y.v.v); if (pointer7.v === undefined) { - pointer7.v = value9; + pointer7.v = value11; } else { - pointer7.v.v = value9.v; + pointer7.v.v = value11.v; } undefined; const pointer8 = y; - const value10 = old12; + const value12 = old12; if (pointer8.v === undefined) { - pointer8.v = value10; + pointer8.v = value12; } else { - pointer8.v.v = value10.v; + pointer8.v.v = value12.v; } undefined; rs.byteVecPush(out, other.v.v & 0xff); @@ -3806,108 +3806,123 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const copy3 = rs.objectCopy(object6); copy3.c = new rs.Cell(object6.c.v); const object7 = x2; - const value11 = copy3; + const value13 = copy3; const cell5 = object7.c; - rs.ptrWrite(object7, value11); + rs.ptrWrite(object7, value13); if (cell5 !== undefined) { object7.c = cell5; - cell5.v = value11.c.v; + cell5.v = value13.c.v; } undefined; const object8 = y2; - const value12 = old13; + const value14 = old13; const cell6 = object8.c; - rs.ptrWrite(object8, value12); + rs.ptrWrite(object8, value14); if (cell6 !== undefined) { object8.c = cell6; - cell6.v = value12.c.v; + cell6.v = value14.c.v; } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(other2.c.v & 0xff, other2.n)), undefined, true); - } else if (_61 === 4) { - const _179: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; + } else if (_60 === 4) { + const _178: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; const pointer6 = cell.v; const object2 = pointer6; const copy = rs.objectCopy(object2); copy[0] = new rs.Cell(object2[0].v); const old11 = copy; const object3 = pointer6; - const value7 = _179; + const value9 = _178; const cell3 = object3[0]; - rs.ptrWrite(object3, value7); + rs.ptrWrite(object3, value9); if (cell3 !== undefined) { object3[0] = cell3; - cell3.v = value7[0].v; + cell3.v = value9[0].v; } undefined; rs.byteVecPush(out, old11[0].v & 0xff); - const _189: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; + const _188: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; const object4 = cell.v; - const value8 = _189; + const value10 = _188; const cell4 = object4[0]; - rs.ptrWrite(object4, value8); + rs.ptrWrite(object4, value10); if (cell4 !== undefined) { object4[0] = cell4; - cell4.v = value8[0].v; + cell4.v = value10[0].v; } undefined; - } else if (_61 === 5) { + } else if (_60 === 5) { toNest.v = rs.u32Mul(n as rs.u32, 3); toPair.v = rs.u32Mul(n as rs.u32, 5); const kept: rs.Cell = new rs.Cell(nest.v.v); const pointer5 = nest; - const value6 = new rs.Cell(rs.u32Add(n as rs.u32, 7)); + const value8 = new rs.Cell(rs.u32Add(n as rs.u32, 7)); if (pointer5.v === undefined) { - pointer5.v = value6; + pointer5.v = value8; } else { - pointer5.v.v = value6.v; + pointer5.v.v = value8.v; } undefined; rs.byteVecPush(out, kept.v & 0xff); - } else if (_61 === 6) { - const _213: rs.Cell = new rs.Cell(((n as rs.u32) ^ 8) >>> 0); + } else if (_60 === 6) { + const _212: rs.Cell = new rs.Cell(((n as rs.u32) ^ 8) >>> 0); const pointer = wrapped; const old10 = new rs.Cell(pointer.v.v); const pointer2 = pointer; - const value = _213; + const value3 = _212; if (pointer2.v === undefined) { - pointer2.v = value; + pointer2.v = value3; } else { - pointer2.v.v = value.v; + pointer2.v.v = value3.v; } undefined; rs.byteVecPush(out, old10.v & 0xff); - const _221: rs.Cell = new rs.Cell(((n as rs.u32) ^ 9) >>> 0); + const _220: rs.Cell = new rs.Cell(((n as rs.u32) ^ 9) >>> 0); const pointer3 = wrapped; - const value2 = _221; + const value4 = _220; if (pointer3.v === undefined) { - pointer3.v = value2; + pointer3.v = value4; } else { - pointer3.v.v = value2.v; + pointer3.v.v = value4.v; } undefined; let fresh: rs.Cell = (undefined as unknown) as rs.Cell; - const pointer4 = rs.place(() => fresh, (value3) => fresh = value3); - const value4 = new rs.Cell(((n as rs.u32) ^ 10) >>> 0); + const pointer4 = rs.place(() => fresh, (value5) => fresh = value5); + const value6 = new rs.Cell(((n as rs.u32) ^ 10) >>> 0); if (pointer4.v === undefined) { - pointer4.v = value4; + pointer4.v = value6; } else { - pointer4.v.v = value4.v; + pointer4.v.v = value6.v; } undefined; const parts: [rs.Cell, rs.u8] = ([undefined, undefined] as unknown) as [rs.Cell, rs.u8]; const object = parts; - const value5 = [new rs.Cell(((n as rs.u32) ^ 11) >>> 0), n] as [rs.Cell, rs.u8]; + const value7 = [new rs.Cell(((n as rs.u32) ^ 11) >>> 0), n] as [rs.Cell, rs.u8]; const cell2 = object[0]; - rs.ptrWrite(object, value5); + rs.ptrWrite(object, value7); if (cell2 !== undefined) { object[0] = cell2; - cell2.v = value5[0].v; + cell2.v = value7[0].v; } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(fresh.v & 0xff, parts[0].v & 0xff)), undefined, true); } else { - rs.byteVecPush(out, n); + const _251: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); + const _254: rs.Place> = nest; + const value = _251; + if (_254.v === undefined) { + _254.v = value; + } else { + _254.v.v = value.v; + } + const _256: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); + const _260: rs.Place>> = deep; + const value2 = _256; + if (_260.v === undefined) { + _260.v = value2; + } else { + _260.v.v.v = value2.v.v; + } } rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toNest.v & 0xff, toDeep.v & 0xff, toPair.v & 0xff, toStrukt.v & 0xff)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toObj[0], toObj[1], toCell.v & 0xff, toWrapped.v & 0xff)), undefined, true); diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index d66d23d6a..95428d6c0 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2832,8 +2832,8 @@ struct Celled { /// Whole writes of values holding cells into a cell, a tuple, and a struct, each read back /// through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, -/// `ptr::write`, `ptr::swap`, and `Cell`'s writes; a value `ptr::read` copies out before -/// one; and `ptr::write`s into memory not written yet. +/// `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a +/// value `ptr::read` copies out before one; and `ptr::write`s into memory not written yet. pub fn nested_cells(data: &[u8], out: &mut Vec) { let nest = UnsafeCell::new(UnsafeCell::new(1u32)); let deep = UnsafeCell::new(UnsafeCell::new(UnsafeCell::new(2u32))); @@ -2923,7 +2923,10 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { *parts.assume_init_ref().0.get() as u8, ]); } - _ => out.push(n), + _ => { + *nest.get() = UnsafeCell::new(u32::from(n) ^ 13); + *deep.get() = UnsafeCell::new(UnsafeCell::new(u32::from(n) ^ 14)); + } } out.extend([*to_nest as u8, *to_deep as u8, *to_pair as u8, *to_strukt as u8]); out.extend([(*to_obj).0, (*to_obj).1, *to_cell as u8, *to_wrapped as u8]); diff --git a/ts/src/lower/mir/place.rs b/ts/src/lower/mir/place.rs index 253a8563a..de294ecc7 100644 --- a/ts/src/lower/mir/place.rs +++ b/ts/src/lower/mir/place.rs @@ -814,6 +814,18 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { out.push(StmtKind::Expr(e).at(self.b.t.loc(span))); return out; } + // A whole write through a raw pointer keeps the cells a value keeps, as `ptr::write` + // does. + if let [ProjectionElem::Deref] = place.projection.as_slice() + && matches!(self.mir.local_decls[place.local].ty.kind(), ty::RawPtr(..)) + && let pointee = place.ty(&self.mir.local_decls, self.tcx).ty + && self.b.t.keeps_cells(pointee) + { + let at = self.place_expr(place, span, &mut out); + let at = self.path(at, "kept", &mut out); + crate::lower::pointer::write_kept_at(self.b, at, value, pointee, span, &mut out); + return out; + } // `*r = v` replaces the object `r` points at in place, which other references and // captures share. An alias of a borrowed place assigns the place, and an `rs.Place` // its `v`. diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index de8a7fc9b..617eb49c8 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -1305,24 +1305,11 @@ pub(super) fn write<'tcx>( return Expr::undefined(); } if b.t.keeps_cells(ty) { - if b.t.pointee(ty) == Pointee::Object { - write_kept(b, p, value, ty, span, out); - return Expr::undefined(); - } - // Memory not written yet, as `ptr::write` may fill, holds no cell to keep. - let (p, value) = (b.spill(p, "pointer", out), b.spill(value, "value", out)); - let mut kept = Vec::new(); - write_kept(b, p.clone().member("v"), value.clone(), ty, span, &mut kept); - let whole = p.clone().member("v").assign(value); - let cond = p.member("v").binary("===", Expr::undefined()); - out.push( - StmtKind::If { - cond, - then: vec![StmtKind::Expr(whole).at(None)], - els: kept, - } - .at(None), - ); + let at = match b.t.pointee(ty) { + Pointee::Object => p, + _ => b.spill(p, "pointer", out).member("v"), + }; + write_kept_at(b, at, value, ty, span, out); return Expr::undefined(); } match b.t.pointee(ty) { @@ -1342,6 +1329,35 @@ fn pointed<'tcx>(b: &Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>) -> Expr { } } +/// The write of `value`, a `ty` that keeps cells, where `at` names its JS value: into the +/// cells there in place, or whole where memory not written yet, as `ptr::write` may fill, +/// holds no cell to keep. `at` is read more than once. +pub(super) fn write_kept_at<'tcx>( + b: &mut Body<'_, '_, 'tcx>, + at: Expr, + value: Expr, + ty: Ty<'tcx>, + span: Span, + out: &mut Vec, +) { + if b.t.pointee(ty) == Pointee::Object { + return write_kept(b, at, value, ty, span, out); + } + let value = b.spill(value, "value", out); + let mut kept = Vec::new(); + write_kept(b, at.clone(), value.clone(), ty, span, &mut kept); + let whole = StmtKind::Expr(at.clone().assign(value)).at(None); + let cond = at.binary("===", Expr::undefined()); + out.push( + StmtKind::If { + cond, + then: vec![whole], + els: kept, + } + .at(None), + ); +} + /// The write of `value` into `at`, the JS value of a `ty` that keeps cells, in place: a /// cell's value written into the cell, as a write through its `get` would be, and a /// tuple's or struct's other fields given `value`'s, as is a cell field of one not From 540749578009da20f7e764196e88f8771056ff0e Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 07:38:12 -0800 Subject: [PATCH 11/17] fix(ts): pass Box::write's statements to the shared pointer write Co-Authored-By: Claude Opus 5.5 --- .../lang/manual/expected/uninit.Zeroed.ts | 6 ++++-- ts/fixtures/lang/manual/expected/uninit.ts | 20 +++++++++---------- ts/src/lower/pointer.rs | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ts/fixtures/lang/manual/expected/uninit.Zeroed.ts b/ts/fixtures/lang/manual/expected/uninit.Zeroed.ts index 7b7409008..303500e0e 100644 --- a/ts/fixtures/lang/manual/expected/uninit.Zeroed.ts +++ b/ts/fixtures/lang/manual/expected/uninit.Zeroed.ts @@ -1,6 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; +import { copy$tuple2_u8_u8 } from "./lib.ts"; import type * as Tag from "./uninit.Tag.ts"; /** @@ -46,8 +47,9 @@ export class Zeroed { export function drop$ops_Drop(self: Zeroed): void { if (self.seen !== undefined) { const seen: rs.Cell<[rs.u8, rs.u8]> = self.seen; - const _6: [rs.u8, rs.u8] = seen.v; - rs.cellSet(seen, [_6[0], rs.u8WrappingAdd(_6[1], 1)] as [rs.u8, rs.u8]); + const _6: [rs.u8, rs.u8] = copy$tuple2_u8_u8(seen.v); + const _8: [rs.u8, rs.u8] = [_6[0], rs.u8WrappingAdd(_6[1], 1)]; + rs.ptrWrite(seen.v, _8); } } diff --git a/ts/fixtures/lang/manual/expected/uninit.ts b/ts/fixtures/lang/manual/expected/uninit.ts index 3c45b04f2..f221adfe4 100644 --- a/ts/fixtures/lang/manual/expected/uninit.ts +++ b/ts/fixtures/lang/manual/expected/uninit.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$array_array2_u16, copy$array_u16 } from "./lib.ts"; +import { copy$array_array2_u16, copy$array_u16, copy$tuple2_u8_u8 } from "./lib.ts"; import { unwrapOr$1fr498u as option_unwrapOr$1fr498u, map$1yjmt5m as option_map$1yjmt5m } from "./core/option.ts"; import * as Counted from "./uninit.Counted.ts"; import type * as Held from "./uninit.Held.ts"; @@ -169,13 +169,13 @@ export function boxedUninits(data: rs.ByteSlice, out: rs.ByteVec): void { }); rs.sliceWriteCloneOfSlice(counted, rs.sliceFrom(src2.items), Counted.clone$Clone); rs.sliceWriteCloneOfSlice(counted, rs.sliceFrom(src2.items), Counted.clone$Clone); - rs.byteVecPush(out, counts.v[0]); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[0]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); const counted2: rs.Slice = counted; rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(rs.sliceItems(counted2)), (c: Counted.Counted): rs.u8 => { return c._0; }), undefined, true); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); rs.byteVecExtendFromSlice(out, boxedClone$u8(data)); const _134: rs.Slice = boxedClone$uninit_Counted(counted2); rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(rs.sliceItems(_134)), (c2: Counted.Counted): rs.u8 => { @@ -183,7 +183,7 @@ export function boxedUninits(data: rs.ByteSlice, out: rs.ByteVec): void { }), undefined, true); rs.dropAll(_134); rs.dropAll(counted2); - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(counts.v[0], counts.v[1])), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy$tuple2_u8_u8(counts.v)[0], copy$tuple2_u8_u8(counts.v)[1])), undefined, true); const len: rs.usize = data.length; const p: rs.Place = rs.rawByteSliceOf(rs.byteSliceNewUninit(len)).ptr; rs.ptrCopy(rs.byteSliceAsPtr(data), p, len); @@ -283,15 +283,15 @@ export function boxedOne(data: rs.ByteSlice, out: rs.ByteVec): void { const counted2: Counted.Counted = counted; rs.byteVecPush(out, counted2._0); counted2[Symbol.dispose](); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); (new Counted.Counted((undefined as unknown) as rs.u8, (undefined as unknown) as rs.Cell<[rs.u8, rs.u8]>) as unknown) as Counted.Counted; rs.ptrWrite((new Counted.Counted((undefined as unknown) as rs.u8, (undefined as unknown) as rs.Cell<[rs.u8, rs.u8]>) as unknown) as Counted.Counted, new Counted.Counted(first, counts)); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); rs.byteVecPush(out, boxed$u8(rs.u8WrappingAdd(first, 1))); const _154: Counted.Counted = boxed$uninit_Counted(new Counted.Counted(first, counts)); rs.byteVecPush(out, _154._0); _154[Symbol.dispose](); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); } /** `value` boxed by way of a box not yet written. */ @@ -444,14 +444,14 @@ export function boxedZeroed(data: rs.ByteSlice, out: rs.ByteVec): void { const seen2: Zeroed.Zeroed = seen; rs.byteVecPush(out, seen2.many[299] & 0xff); seen2[Symbol.dispose](); - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); ((new Zeroed.Zeroed(0, false, 0, 0.0, 0, 0, Array.from({ length: 3 }, (): rs.u16 => { return 0; }), Array.from({ length: 300 }, (): rs.u32 => { return 0; }), [0, 0], "Zero", "Zero", rs.NULL, undefined) as unknown) as Zeroed.Zeroed).seen = counts; undefined; - rs.byteVecPush(out, counts.v[1]); + rs.byteVecPush(out, copy$tuple2_u8_u8(counts.v)[1]); rs.byteVecExtendFromSlice(out, rs.byteSliceFrom(rs.u64ToBeBytes(zeroedBox$u64()).reverse())); const _409: [rs.u8, rs.u16] = zeroedBox$tuple2_u8_u16(); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_409[0], _409[1] & 0xff)), undefined, true); diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index ef921e5c7..cc94528ac 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -856,7 +856,7 @@ pub fn call<'tcx>( let p = b.spill(boxed, "uninit", out); let write = match array(b, pointee()) { true => Expr::rs("ptrWrite", vec![p.clone(), value]), - false => write(b, p.clone(), value, pointee(), span), + false => write(b, p.clone(), value, pointee(), span, out), }; out.push(StmtKind::Expr(write).at(None)); p From c1ef35fe89df52a109d8df36ce08652b5df10595 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 08:27:03 -0800 Subject: [PATCH 12/17] fix(ts): give an uninitialized cell an rs.Cell, and copy kept values' other parts A cell not written yet is an `rs.Cell` of its value's placeholder, so a pointer `UnsafeCell::raw_get` makes into it before `ptr::write` sees the write, which lands in it. A copy of a value that keeps cells copies its other parts as a read does, so a later write into one leaves the copy be. Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/dyns/expected/dyns.ts | 2 +- .../pointers/expected/pointers.Segment.ts | 4 +- .../lang/pointers/expected/pointers.ts | 311 ++++++++++-------- ts/fixtures/lang/pointers/src/pointers.rs | 15 +- ts/src/lower/pointer.rs | 39 ++- 5 files changed, 217 insertions(+), 154 deletions(-) diff --git a/ts/fixtures/lang/dyns/expected/dyns.ts b/ts/fixtures/lang/dyns/expected/dyns.ts index 57297b26d..3b9eb51db 100644 --- a/ts/fixtures/lang/dyns/expected/dyns.ts +++ b/ts/fixtures/lang/dyns/expected/dyns.ts @@ -2204,7 +2204,7 @@ export function rests(dataData: Uint8Array, dataStart: number, dataLen: number, dropped(out); locked[Symbol.dispose](); dropped(out); - let slot: rs.Cell = (undefined as unknown) as rs.Cell; + let slot: rs.Cell = (new rs.Cell(undefined) as unknown) as rs.Cell; const thin: rs.Place> = rs.place(() => slot, (value22) => slot = value22); const wide: rs.Place> = rs.rawUnsizePlace(thin, (p12) => rs.tailDyn(p12, { v: (value23: rs.Cell) => new ofPlace$u32(value23) })); const null_: boolean = wide === rs.NULL; diff --git a/ts/fixtures/lang/pointers/expected/pointers.Segment.ts b/ts/fixtures/lang/pointers/expected/pointers.Segment.ts index f64c74703..876451e00 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.Segment.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.Segment.ts @@ -16,7 +16,7 @@ export interface Segment { function new_(): Segment { let _6!: rs.usize; const slots: Lent.Lent<[rs.u8, rs.u8]>[] = Array.from({ length: 4 }, (): Lent.Lent<[rs.u8, rs.u8]> => { - return (new Lent.Lent((undefined as unknown) as rs.Cell<[rs.u8, rs.u8]>) as unknown) as Lent.Lent<[rs.u8, rs.u8]>; + return (new Lent.Lent((new rs.Cell([undefined, undefined]) as unknown) as rs.Cell<[rs.u8, rs.u8]>) as unknown) as Lent.Lent<[rs.u8, rs.u8]>; }); let iterStart: rs.usize = 0; const iterEnd: rs.usize = 4; @@ -42,7 +42,7 @@ function new_(): Segment { /** As [`Self::new`], with kio's loop written with an iterator. */ export function iterated(): Segment { const slots: Lent.Lent<[rs.u8, rs.u8]>[] = Array.from({ length: 4 }, (): Lent.Lent<[rs.u8, rs.u8]> => { - return (new Lent.Lent((undefined as unknown) as rs.Cell<[rs.u8, rs.u8]>) as unknown) as Lent.Lent<[rs.u8, rs.u8]>; + return (new Lent.Lent((new rs.Cell([undefined, undefined]) as unknown) as rs.Cell<[rs.u8, rs.u8]>) as unknown) as Lent.Lent<[rs.u8, rs.u8]>; }); const iter: rs.Iter> = rs.iterSlice(slots); while (true) { diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 14bb96199..16e8aec3e 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -3633,7 +3633,9 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { * Whole writes of values holding cells into a cell, a tuple, and a struct, each read back * through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, * `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a - * value `ptr::read` copies out before one; and `ptr::write`s into memory not written yet. + * value `ptr::read` copies out before one, and before a write into a part holding no + * cell; `ptr::write`s into memory not written yet; and pointers `UnsafeCell::raw_get` + * makes into such memory before the write. */ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const nest: rs.Cell> = new rs.Cell(new rs.Cell(1)); @@ -3655,122 +3657,122 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _60: rs.u8 = rs.u8Rem(op, 8); if (_60 === 0) { const _64: rs.Cell = new rs.Cell(n as rs.u32); - const pointer13 = nest; - const old16 = new rs.Cell(pointer13.v.v); - const pointer14 = pointer13; - const value19 = _64; - if (pointer14.v === undefined) { - pointer14.v = value19; + const pointer15 = nest; + const old16 = new rs.Cell(pointer15.v.v); + const pointer16 = pointer15; + const value23 = _64; + if (pointer16.v === undefined) { + pointer16.v = value23; } else { - pointer14.v.v = value19.v; + pointer16.v.v = value23.v; } undefined; rs.byteVecPush(out, old16.v & 0xff); const _71: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); - const pointer15 = deep; - const old17 = new rs.Cell(new rs.Cell(pointer15.v.v.v)); - const pointer16 = pointer15; - const value20 = _71; - if (pointer16.v === undefined) { - pointer16.v = value20; + const pointer17 = deep; + const old17 = new rs.Cell(new rs.Cell(pointer17.v.v.v)); + const pointer18 = pointer17; + const value24 = _71; + if (pointer18.v === undefined) { + pointer18.v = value24; } else { - pointer16.v.v.v = value20.v.v; + pointer18.v.v.v = value24.v.v; } undefined; rs.byteVecPush(out, old17.v.v & 0xff); const _82: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; - const pointer17 = pair.v; - const object12 = pointer17; - const copy5 = rs.objectCopy(object12); - copy5[0] = new rs.Cell(object12[0].v); - const old18 = copy5; - const object13 = pointer17; - const value21 = _82; - const cell9 = object13[0]; - rs.ptrWrite(object13, value21); + const pointer19 = pair.v; + const object13 = pointer19; + const copy7 = rs.objectCopy(object13); + copy7[0] = new rs.Cell(object13[0].v); + const old18 = copy7; + const object14 = pointer19; + const value25 = _82; + const cell9 = object14[0]; + rs.ptrWrite(object14, value25); if (cell9 !== undefined) { - object13[0] = cell9; - cell9.v = value21[0].v; + object14[0] = cell9; + cell9.v = value25[0].v; } undefined; const old3: [rs.Cell, rs.u8] = old18; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old3[0].v & 0xff, old3[1])), undefined, true); const _95: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; - const pointer18 = strukt.v; - const object14 = pointer18; - const copy6 = rs.objectCopy(object14); - copy6.c = new rs.Cell(object14.c.v); - const old19 = copy6; - const object15 = pointer18; - const value22 = _95; - const cell10 = object15.c; - rs.ptrWrite(object15, value22); + const pointer20 = strukt.v; + const object15 = pointer20; + const copy8 = rs.objectCopy(object15); + copy8.c = new rs.Cell(object15.c.v); + const old19 = copy8; + const object16 = pointer20; + const value26 = _95; + const cell10 = object16.c; + rs.ptrWrite(object16, value26); if (cell10 !== undefined) { - object15.c = cell10; - cell10.v = value22.c.v; + object16.c = cell10; + cell10.v = value26.c.v; } undefined; const old4: Celled.Celled = old19; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old4.c.v & 0xff, old4.n)), undefined, true); const _108: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); - const pointer19 = obj; - const old20 = new rs.Cell(rs.objectCopy(pointer19.v.v)); - const pointer20 = pointer19; - const value23 = _108; - if (pointer20.v === undefined) { - pointer20.v = value23; + const pointer21 = obj; + const old20 = new rs.Cell(copy$tuple2_u8_u8(pointer21.v.v)); + const pointer22 = pointer21; + const value27 = _108; + if (pointer22.v === undefined) { + pointer22.v = value27; } else { - rs.ptrWrite(pointer20.v.v, value23.v); + rs.ptrWrite(pointer22.v.v, value27.v); } undefined; rs.byteVecPush(out, old20.v[1]); } else if (_60 === 1) { const _115: rs.Place> = nest; const _117: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); - const pointer10 = _115; - const old14 = new rs.Cell(pointer10.v.v); - const pointer11 = pointer10; - const value17 = _117; - if (pointer11.v === undefined) { - pointer11.v = value17; + const pointer12 = _115; + const old14 = new rs.Cell(pointer12.v.v); + const pointer13 = pointer12; + const value21 = _117; + if (pointer13.v === undefined) { + pointer13.v = value21; } else { - pointer11.v.v = value17.v; + pointer13.v.v = value21.v; } undefined; rs.byteVecPush(out, old14.v & 0xff); const _124: [rs.Cell, rs.u8] = pair.v; const _126: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; - const pointer12 = _124; - const object10 = pointer12; - const copy4 = rs.objectCopy(object10); - copy4[0] = new rs.Cell(object10[0].v); - const old15 = copy4; - const object11 = pointer12; - const value18 = _126; - const cell8 = object11[0]; - rs.ptrWrite(object11, value18); + const pointer14 = _124; + const object11 = pointer14; + const copy6 = rs.objectCopy(object11); + copy6[0] = new rs.Cell(object11[0].v); + const old15 = copy6; + const object12 = pointer14; + const value22 = _126; + const cell8 = object12[0]; + rs.ptrWrite(object12, value22); if (cell8 !== undefined) { - object11[0] = cell8; - cell8.v = value18[0].v; + object12[0] = cell8; + cell8.v = value22[0].v; } undefined; rs.byteVecPush(out, old15[0].v & 0xff); } else if (_60 === 2) { - const pointer9 = nest; - const value15 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); - if (pointer9.v === undefined) { - pointer9.v = value15; + const pointer11 = nest; + const value19 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); + if (pointer11.v === undefined) { + pointer11.v = value19; } else { - pointer9.v.v = value15.v; + pointer11.v.v = value19.v; } undefined; - const object9 = strukt.v; - const value16 = { c: new rs.Cell(((n as rs.u32) ^ 4) >>> 0), n }; - const cell7 = object9.c; - rs.ptrWrite(object9, value16); + const object10 = strukt.v; + const value20 = { c: new rs.Cell(((n as rs.u32) ^ 4) >>> 0), n }; + const cell7 = object10.c; + rs.ptrWrite(object10, value20); if (cell7 !== undefined) { - object9.c = cell7; - cell7.v = value16.c.v; + object10.c = cell7; + cell7.v = value20.c.v; } undefined; } else if (_60 === 3) { @@ -3778,89 +3780,89 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const x = nest; const y = other; const old12 = new rs.Cell(x.v.v); - const pointer7 = x; - const value11 = new rs.Cell(y.v.v); - if (pointer7.v === undefined) { - pointer7.v = value11; + const pointer9 = x; + const value15 = new rs.Cell(y.v.v); + if (pointer9.v === undefined) { + pointer9.v = value15; } else { - pointer7.v.v = value11.v; + pointer9.v.v = value15.v; } undefined; - const pointer8 = y; - const value12 = old12; - if (pointer8.v === undefined) { - pointer8.v = value12; + const pointer10 = y; + const value16 = old12; + if (pointer10.v === undefined) { + pointer10.v = value16; } else { - pointer8.v.v = value12.v; + pointer10.v.v = value16.v; } undefined; rs.byteVecPush(out, other.v.v & 0xff); const other2: Celled.Celled = { c: new rs.Cell(((n as rs.u32) ^ 12) >>> 0), n: ~n & 0xff }; const x2 = strukt.v; const y2 = other2; - const object5 = x2; - const copy2 = rs.objectCopy(object5); - copy2.c = new rs.Cell(object5.c.v); - const old13 = copy2; - const object6 = y2; - const copy3 = rs.objectCopy(object6); - copy3.c = new rs.Cell(object6.c.v); - const object7 = x2; - const value13 = copy3; - const cell5 = object7.c; - rs.ptrWrite(object7, value13); + const object6 = x2; + const copy4 = rs.objectCopy(object6); + copy4.c = new rs.Cell(object6.c.v); + const old13 = copy4; + const object7 = y2; + const copy5 = rs.objectCopy(object7); + copy5.c = new rs.Cell(object7.c.v); + const object8 = x2; + const value17 = copy5; + const cell5 = object8.c; + rs.ptrWrite(object8, value17); if (cell5 !== undefined) { - object7.c = cell5; - cell5.v = value13.c.v; + object8.c = cell5; + cell5.v = value17.c.v; } undefined; - const object8 = y2; - const value14 = old13; - const cell6 = object8.c; - rs.ptrWrite(object8, value14); + const object9 = y2; + const value18 = old13; + const cell6 = object9.c; + rs.ptrWrite(object9, value18); if (cell6 !== undefined) { - object8.c = cell6; - cell6.v = value14.c.v; + object9.c = cell6; + cell6.v = value18.c.v; } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(other2.c.v & 0xff, other2.n)), undefined, true); } else if (_60 === 4) { const _178: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; - const pointer6 = cell.v; - const object2 = pointer6; - const copy = rs.objectCopy(object2); - copy[0] = new rs.Cell(object2[0].v); - const old11 = copy; - const object3 = pointer6; - const value9 = _178; - const cell3 = object3[0]; - rs.ptrWrite(object3, value9); + const pointer8 = cell.v; + const object3 = pointer8; + const copy3 = rs.objectCopy(object3); + copy3[0] = new rs.Cell(object3[0].v); + const old11 = copy3; + const object4 = pointer8; + const value13 = _178; + const cell3 = object4[0]; + rs.ptrWrite(object4, value13); if (cell3 !== undefined) { - object3[0] = cell3; - cell3.v = value9[0].v; + object4[0] = cell3; + cell3.v = value13[0].v; } undefined; rs.byteVecPush(out, old11[0].v & 0xff); const _188: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; - const object4 = cell.v; - const value10 = _188; - const cell4 = object4[0]; - rs.ptrWrite(object4, value10); + const object5 = cell.v; + const value14 = _188; + const cell4 = object5[0]; + rs.ptrWrite(object5, value14); if (cell4 !== undefined) { - object4[0] = cell4; - cell4.v = value10[0].v; + object5[0] = cell4; + cell4.v = value14[0].v; } undefined; } else if (_60 === 5) { toNest.v = rs.u32Mul(n as rs.u32, 3); toPair.v = rs.u32Mul(n as rs.u32, 5); const kept: rs.Cell = new rs.Cell(nest.v.v); - const pointer5 = nest; - const value8 = new rs.Cell(rs.u32Add(n as rs.u32, 7)); - if (pointer5.v === undefined) { - pointer5.v = value8; + const pointer7 = nest; + const value12 = new rs.Cell(rs.u32Add(n as rs.u32, 7)); + if (pointer7.v === undefined) { + pointer7.v = value12; } else { - pointer5.v.v = value8.v; + pointer7.v.v = value12.v; } undefined; rs.byteVecPush(out, kept.v & 0xff); @@ -3886,7 +3888,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { pointer3.v.v = value4.v; } undefined; - let fresh: rs.Cell = (undefined as unknown) as rs.Cell; + let fresh: rs.Cell = (new rs.Cell(undefined) as unknown) as rs.Cell; const pointer4 = rs.place(() => fresh, (value5) => fresh = value5); const value6 = new rs.Cell(((n as rs.u32) ^ 10) >>> 0); if (pointer4.v === undefined) { @@ -3895,7 +3897,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { pointer4.v.v = value6.v; } undefined; - const parts: [rs.Cell, rs.u8] = ([undefined, undefined] as unknown) as [rs.Cell, rs.u8]; + const parts: [rs.Cell, rs.u8] = ([new rs.Cell(undefined), undefined] as unknown) as [rs.Cell, rs.u8]; const object = parts; const value7 = [new rs.Cell(((n as rs.u32) ^ 11) >>> 0), n] as [rs.Cell, rs.u8]; const cell2 = object[0]; @@ -3906,22 +3908,55 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(fresh.v & 0xff, parts[0].v & 0xff)), undefined, true); + let slot: rs.Cell = (new rs.Cell(undefined) as unknown) as rs.Cell; + let pairSlot: rs.Cell<[rs.u8, rs.u8]> = (new rs.Cell([undefined, undefined]) as unknown) as rs.Cell<[rs.u8, rs.u8]>; + const _255: [rs.Place>, rs.Place>] = [rs.place(() => slot, (value8) => slot = value8), rs.place(() => pairSlot, (value9) => pairSlot = value9)]; + const toSlot: rs.Place> = _255[0]; + const toPairSlot: rs.Place> = _255[1]; + const _262: [rs.Place, [rs.u8, rs.u8]] = [rs.rawUnsize(toSlot, (p: rs.Place>) => p.v), rs.rawUnsize(toPairSlot, (p2: rs.Place>) => p2.v.v)]; + const early: rs.Place = _262[0]; + const earlyPair: [rs.u8, rs.u8] = _262[1]; + const pointer5 = toSlot; + const value10 = new rs.Cell(((n as rs.u32) ^ 17) >>> 0); + if (pointer5.v === undefined) { + pointer5.v = value10; + } else { + pointer5.v.v = value10.v; + } + undefined; + const pointer6 = toPairSlot; + const value11 = new rs.Cell([n, ~n & 0xff] as [rs.u8, rs.u8]); + if (pointer6.v === undefined) { + pointer6.v = value11; + } else { + rs.ptrWrite(pointer6.v.v, value11.v); + } + undefined; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(early.v & 0xff, earlyPair[1])), undefined, true); + const src: rs.Cell<[rs.Cell, [rs.u32, rs.u32]]> = new rs.Cell([new rs.Cell(n as rs.u32), [2, 3] as [rs.u32, rs.u32]] as [rs.Cell, [rs.u32, rs.u32]]); + const object2 = src.v; + const copy2 = rs.objectCopy(object2); + copy2[0] = new rs.Cell(object2[0].v); + copy2[1] = copy$tuple2_u32_u32(object2[1]); + const copy: [rs.Cell, [rs.u32, rs.u32]] = copy2; + src.v[1][0] = 9; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy[1][0] & 0xff, src.v[1][0] & 0xff)), undefined, true); } else { - const _251: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); - const _254: rs.Place> = nest; - const value = _251; - if (_254.v === undefined) { - _254.v = value; + const _299: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); + const _302: rs.Place> = nest; + const value = _299; + if (_302.v === undefined) { + _302.v = value; } else { - _254.v.v = value.v; + _302.v.v = value.v; } - const _256: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); - const _260: rs.Place>> = deep; - const value2 = _256; - if (_260.v === undefined) { - _260.v = value2; + const _304: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); + const _308: rs.Place>> = deep; + const value2 = _304; + if (_308.v === undefined) { + _308.v = value2; } else { - _260.v.v.v = value2.v.v; + _308.v.v.v = value2.v.v; } } rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toNest.v & 0xff, toDeep.v & 0xff, toPair.v & 0xff, toStrukt.v & 0xff)), undefined, true); diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index 95428d6c0..b96c2c9d5 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2833,7 +2833,9 @@ struct Celled { /// Whole writes of values holding cells into a cell, a tuple, and a struct, each read back /// through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, /// `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a -/// value `ptr::read` copies out before one; and `ptr::write`s into memory not written yet. +/// value `ptr::read` copies out before one, and before a write into a part holding no +/// cell; `ptr::write`s into memory not written yet; and pointers `UnsafeCell::raw_get` +/// makes into such memory before the write. pub fn nested_cells(data: &[u8], out: &mut Vec) { let nest = UnsafeCell::new(UnsafeCell::new(1u32)); let deep = UnsafeCell::new(UnsafeCell::new(UnsafeCell::new(2u32))); @@ -2922,6 +2924,17 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { *fresh.assume_init_ref().get() as u8, *parts.assume_init_ref().0.get() as u8, ]); + let mut slot = MaybeUninit::>::uninit(); + let mut pair_slot = MaybeUninit::>::uninit(); + let (to_slot, to_pair_slot) = (slot.as_mut_ptr(), pair_slot.as_mut_ptr()); + let (early, early_pair) = (UnsafeCell::raw_get(to_slot), UnsafeCell::raw_get(to_pair_slot)); + ptr::write(to_slot, UnsafeCell::new(u32::from(n) ^ 17)); + ptr::write(to_pair_slot, UnsafeCell::new((n, !n))); + out.extend([*early as u8, (*early_pair).1]); + let src = UnsafeCell::new((UnsafeCell::new(u32::from(n)), (2u32, 3u32))); + let copy = ptr::read(src.get()); + (*src.get()).1.0 = 9; + out.extend([copy.1.0 as u8, (*src.get()).1.0 as u8]); } _ => { *nest.get() = UnsafeCell::new(u32::from(n) ^ 13); diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index cc94528ac..0b885bfbd 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -274,13 +274,20 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { } let ty = self.uninit_inner(ty); match (self.pointee(ty), ty.kind()) { + // A cell, so a pointer `raw_get` makes into it before a write sees the write, + // which lands in it. + (_, ty::Adt(adt, args)) if Std::of(self.tcx, adt.did()) == Some(Std::Cell) => Expr::New( + Box::new(Expr::Rs("Cell".into())), + vec![self.shell(args.type_at(0), span)], + ), (Pointee::Object, ty::Tuple(tys)) => Expr::Array(tys.iter().map(|t| self.shell(t, span)).collect()), (Pointee::Array, ty::Array(elem, len)) => { let len = Expr::lit(len.try_to_target_usize(self.tcx).unwrap_or(0).to_string()); + let cell = matches!(elem.kind(), ty::Adt(adt, _) if Std::of(self.tcx, adt.did()) == Some(Std::Cell)); match (super::byte(*elem), self.pointee(*elem)) { (true, _) => Expr::New(Box::new(Expr::ident("Uint8Array")), vec![len]), - (_, Pointee::Place) => Expr::New(Box::new(Expr::ident("Array")), vec![len]), - (_, Pointee::Object | Pointee::Array | Pointee::Slice) => { + (_, Pointee::Place) if !cell => Expr::New(Box::new(Expr::ident("Array")), vec![len]), + (_, Pointee::Place | Pointee::Object | Pointee::Array | Pointee::Slice) => { let elem_ty = self.ty(*elem, span); let each = Expr::Arrow( Vec::new(), @@ -1439,13 +1446,17 @@ fn write_kept<'tcx>(b: &mut Body<'_, '_, 'tcx>, at: Expr, value: Expr, ty: Ty<'t } /// A copy of `at`, the JS value of a `ty` that keeps cells, with a cell of its own for -/// each it keeps, so a write into those in place leaves the copy be. +/// each it keeps, so a write into those in place leaves the copy be, and each other part +/// copied as a read copies it. fn copy_kept<'tcx>(b: &mut Body<'_, '_, 'tcx>, at: Expr, ty: Ty<'tcx>, span: Span, out: &mut Vec) -> Expr { match b.t.kept_cells(ty) { Some(KeptCells::Cell(held)) => { - let value = match b.t.keeps_cells(held) { - true => copy_kept(b, at.member("v"), held, span, out), - false => { + let value = match (b.t.keeps_cells(held), b.t.pointee(held)) { + (true, _) => copy_kept(b, at.member("v"), held, span, out), + (false, Pointee::Object) if let Some(copy) = b.t.snapshot_fn(held, span) => { + copy.call(vec![at.member("v")]) + } + (false, _) => { let p = cell_pointer(b, at, held); copied_out(b, p, held, span, out) } @@ -1456,12 +1467,16 @@ fn copy_kept<'tcx>(b: &mut Body<'_, '_, 'tcx>, at: Expr, ty: Ty<'tcx>, span: Spa let at = b.spill(at, "object", out); let copy = b.spill(Expr::rs("objectCopy", vec![at.clone()]), "copy", out); for (i, &(field, kept)) in fields.iter().enumerate() { - if kept { - let from = object_field(b, at.clone(), ty, i, span); - let cell = copy_kept(b, from, field, span, out); - let part = object_field(b, copy.clone(), ty, i, span); - out.push(StmtKind::Expr(part.assign(cell)).at(None)); - } + let from = object_field(b, at.clone(), ty, i, span); + let value = match kept { + true => copy_kept(b, from, field, span, out), + false => match b.t.snapshot_fn(field, span) { + Some(copy) => copy.call(vec![from]), + None => continue, + }, + }; + let part = object_field(b, copy.clone(), ty, i, span); + out.push(StmtKind::Expr(part.assign(value)).at(None)); } copy } From 982a9a91770b0d32f0a355d00cfe92a3eacfc117 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 08:52:23 -0800 Subject: [PATCH 13/17] fix(ts): defer raw_get's place in a cell until it is read, and copy cells assume_init_read reads `UnsafeCell::raw_get` of a cell holding a value JS copies makes a place that reads the cell where the pointer points as it is used, so memory holding no cell yet, such as a `Vec`'s room past its length, works as in Rust. `MaybeUninit::assume_init_read` and `ManuallyDrop::take` of a value that keeps cells give it cells of its own. Co-Authored-By: Claude Opus 5.5 --- .../lang/pointers/expected/pointers.Slots.ts | 2 +- .../lang/pointers/expected/pointers.ts | 264 ++++++++++-------- ts/fixtures/lang/pointers/src/pointers.rs | 10 + ts/src/lower/pointer.rs | 28 +- 4 files changed, 182 insertions(+), 122 deletions(-) diff --git a/ts/fixtures/lang/pointers/expected/pointers.Slots.ts b/ts/fixtures/lang/pointers/expected/pointers.Slots.ts index 4b0db4c2d..ec8a8ca9d 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.Slots.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.Slots.ts @@ -25,7 +25,7 @@ export class Slots { /** The pointer to the count, through a pointer to the struct. */ export function num(this_: Slots): rs.Place { - return rs.rawUnsize(rs.place(() => this_.num, (value) => this_.num = value), (p: rs.Place>) => p.v); + return rs.rawUnsize(rs.place(() => this_.num, (value) => this_.num = value), (p: rs.Place>) => rs.place(() => p.v.v, (value2) => p.v.v = value2)); } //# sourceMappingURL=pointers.Slots.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 16e8aec3e..fc63bd28f 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -3202,26 +3202,26 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _110: rs.Slice = rs.sliceFrom(wide.v); rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(_110.data[_110.start + 1]).reverse()), undefined, true); } else if (_41 === 1) { - const p: rs.Place> = rs.place(() => num, (value9) => num = value9); - const _115: rs.u32 = rs.u32WrappingMul(rs.rawUnsize(p, (p10: rs.Place>) => p10.v).v, ((n as rs.u32) | 1) >>> 0); - rs.rawUnsize(p, (p11: rs.Place>) => p11.v).v = _115; + const p: rs.Place> = rs.place(() => num, (value11) => num = value11); + const _115: rs.u32 = rs.u32WrappingMul(rs.rawUnsize(p, (p10: rs.Place>) => rs.place(() => p10.v.v, (value12) => p10.v.v = value12)).v, ((n as rs.u32) | 1) >>> 0); + rs.rawUnsize(p, (p11: rs.Place>) => rs.place(() => p11.v.v, (value13) => p11.v.v = value13)).v = _115; const s: Slots.Slots = slots; const _122: rs.u32 = n as rs.u32; const _123: rs.Place = Slots.num(s); _123.v = rs.u32Add(_123.v, _122); - const _125: Held.Held = rs.rawUnsize(rs.place(() => s.held, (value10) => s.held = value10), (p12: rs.Place>) => p12.v.v); + const _125: Held.Held = rs.rawUnsize(rs.place(() => s.held, (value14) => s.held = value14), (p12: rs.Place>) => p12.v.v); _125.n = _125.n ^ n; - const _127: [rs.u8, rs.u16] = rs.rawUnsize(rs.place(() => s.pair, (value11) => s.pair = value11), (p13: rs.Place>) => p13.v.v); + const _127: [rs.u8, rs.u16] = rs.rawUnsize(rs.place(() => s.pair, (value15) => s.pair = value15), (p13: rs.Place>) => p13.v.v); _127[0] = _127[0] ^ n; const _129: rs.u32 = n as rs.u32; - const _130: rs.Place = rs.rawUnsize({ v: slot }, (p14: rs.Place>) => p14.v); + const _130: rs.Place = rs.rawUnsize({ v: slot }, (p14: rs.Place>) => rs.place(() => p14.v.v, (value16) => p14.v.v = value16)); _130.v = (_130.v ^ _129) >>> 0; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(slots.held.v.n, slots.pair.v[0])), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slot.v).reverse()), undefined, true); } else if (_41 === 2) { - const c: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value7) => local = value7)); + const c: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value9) => local = value9)); c.v = rs.intRotateLeft(c.v, n as rs.u32, 32, false); const _168: Held.Held = rs.cellOfPlace(rs.partPlace(whole)).v; _168.n = _168.n ^ n; @@ -3230,7 +3230,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _172); } const borrowed2 = elems; - const e: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed2[_172], (value8) => borrowed2[_172] = value8)); + const e: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed2[_172], (value10) => borrowed2[_172] = value10)); const _176: rs.u16 = n as rs.u16; const _177: rs.Place = e; _177.v = _177.v ^ _176; @@ -3274,7 +3274,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const old5: Duo.Duo = old26; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old5._0, old5._1, duo.v._0)), undefined, true); } else if (_41 === 4) { - const _255: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value5) => local = value5)); + const _255: rs.Cell = rs.cellOfPlace(rs.place(() => local, (value7) => local = value7)); const _257: rs.u32 = n as rs.u32; const pointer6 = _255; const old19 = pointer6.v; @@ -3293,7 +3293,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _282); } const borrowed = elems; - const _280: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_282], (value6) => borrowed[_282] = value6)); + const _280: rs.Cell = rs.cellOfPlace(rs.place(() => borrowed[_282], (value8) => borrowed[_282] = value8)); const _286: rs.u16 = n as rs.u16; const pointer8 = _280; const old21 = pointer8.v; @@ -3336,13 +3336,13 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { rs.iterExtendBytes(out, rs.iterSlice(copy$array_u8(arr)), undefined, true); } else if (_41 === 6) { const null_: rs.Place> = rs.NULL; - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(rs.rawUnsize(null_, (p7: rs.Place>) => p7.v) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.NULL, (p8: rs.Place>) => p8.v.v) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.ptrWrappingAdd(null_, rs.u8Rem(n, 4) as rs.usize, 4, 4), (p9: rs.Place>) => p9.v) === rs.NULL ? 1 : 0)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(rs.rawUnsize(null_, (p7: rs.Place>) => rs.place(() => p7.v.v, (value) => p7.v.v = value)) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.NULL, (p8: rs.Place>) => p8.v.v) === rs.NULL ? 1 : 0, rs.rawUnsize(rs.ptrWrappingAdd(null_, rs.u8Rem(n, 4) as rs.usize, 4, 4), (p9: rs.Place>) => rs.place(() => p9.v.v, (value2) => p9.v.v = value2)) === rs.NULL ? 1 : 0)), undefined, true); const held2: (a0: rs.u8) => Held.Held = (n2: rs.u8): Held.Held => { return new Held.Held(n2, rs.arcClone(count)); }; let a2: Held.Held = held2(n); const atA: Held.Held = a2; - const _381: rs.Place = rs.place(() => a2, (value) => a2 = value); + const _381: rs.Place = rs.place(() => a2, (value3) => a2 = value3); const _383: Held.Held = held2(~n & 0xff); const pointer = _381; const old11 = pointer.v; @@ -3352,8 +3352,8 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _395: [Held.Held, Held.Held] = [a2, other]; const atNew: Held.Held = _395[0]; const atOther: Held.Held = _395[1]; - const x3 = rs.place(() => a2, (value2) => a2 = value2); - const y3 = rs.place(() => other, (value3) => other = value3); + const x3 = rs.place(() => a2, (value4) => a2 = value4); + const y3 = rs.place(() => other, (value5) => other = value5); const old12 = x3.v; x3.v = y3.v; y3.v = old12; @@ -3386,7 +3386,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const was2: Held.Held = old15; let boxed: Held.Held = held2(n ^ 7); const atBoxed: Held.Held = boxed; - const _510: rs.Cell = rs.cellOfPlace(rs.place(() => boxed, (value4) => boxed = value4)); + const _510: rs.Cell = rs.cellOfPlace(rs.place(() => boxed, (value6) => boxed = value6)); const _512: Held.Held = held2(n); const pointer4 = _510; const old16 = pointer4.v; @@ -3657,122 +3657,122 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _60: rs.u8 = rs.u8Rem(op, 8); if (_60 === 0) { const _64: rs.Cell = new rs.Cell(n as rs.u32); - const pointer15 = nest; - const old16 = new rs.Cell(pointer15.v.v); - const pointer16 = pointer15; - const value23 = _64; - if (pointer16.v === undefined) { - pointer16.v = value23; + const pointer17 = nest; + const old16 = new rs.Cell(pointer17.v.v); + const pointer18 = pointer17; + const value29 = _64; + if (pointer18.v === undefined) { + pointer18.v = value29; } else { - pointer16.v.v = value23.v; + pointer18.v.v = value29.v; } undefined; rs.byteVecPush(out, old16.v & 0xff); const _71: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); - const pointer17 = deep; - const old17 = new rs.Cell(new rs.Cell(pointer17.v.v.v)); - const pointer18 = pointer17; - const value24 = _71; - if (pointer18.v === undefined) { - pointer18.v = value24; + const pointer19 = deep; + const old17 = new rs.Cell(new rs.Cell(pointer19.v.v.v)); + const pointer20 = pointer19; + const value30 = _71; + if (pointer20.v === undefined) { + pointer20.v = value30; } else { - pointer18.v.v.v = value24.v.v; + pointer20.v.v.v = value30.v.v; } undefined; rs.byteVecPush(out, old17.v.v & 0xff); const _82: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; - const pointer19 = pair.v; - const object13 = pointer19; + const pointer21 = pair.v; + const object13 = pointer21; const copy7 = rs.objectCopy(object13); copy7[0] = new rs.Cell(object13[0].v); const old18 = copy7; - const object14 = pointer19; - const value25 = _82; + const object14 = pointer21; + const value31 = _82; const cell9 = object14[0]; - rs.ptrWrite(object14, value25); + rs.ptrWrite(object14, value31); if (cell9 !== undefined) { object14[0] = cell9; - cell9.v = value25[0].v; + cell9.v = value31[0].v; } undefined; const old3: [rs.Cell, rs.u8] = old18; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old3[0].v & 0xff, old3[1])), undefined, true); const _95: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; - const pointer20 = strukt.v; - const object15 = pointer20; + const pointer22 = strukt.v; + const object15 = pointer22; const copy8 = rs.objectCopy(object15); copy8.c = new rs.Cell(object15.c.v); const old19 = copy8; - const object16 = pointer20; - const value26 = _95; + const object16 = pointer22; + const value32 = _95; const cell10 = object16.c; - rs.ptrWrite(object16, value26); + rs.ptrWrite(object16, value32); if (cell10 !== undefined) { object16.c = cell10; - cell10.v = value26.c.v; + cell10.v = value32.c.v; } undefined; const old4: Celled.Celled = old19; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old4.c.v & 0xff, old4.n)), undefined, true); const _108: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); - const pointer21 = obj; - const old20 = new rs.Cell(copy$tuple2_u8_u8(pointer21.v.v)); - const pointer22 = pointer21; - const value27 = _108; - if (pointer22.v === undefined) { - pointer22.v = value27; + const pointer23 = obj; + const old20 = new rs.Cell(copy$tuple2_u8_u8(pointer23.v.v)); + const pointer24 = pointer23; + const value33 = _108; + if (pointer24.v === undefined) { + pointer24.v = value33; } else { - rs.ptrWrite(pointer22.v.v, value27.v); + rs.ptrWrite(pointer24.v.v, value33.v); } undefined; rs.byteVecPush(out, old20.v[1]); } else if (_60 === 1) { const _115: rs.Place> = nest; const _117: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); - const pointer12 = _115; - const old14 = new rs.Cell(pointer12.v.v); - const pointer13 = pointer12; - const value21 = _117; - if (pointer13.v === undefined) { - pointer13.v = value21; + const pointer14 = _115; + const old14 = new rs.Cell(pointer14.v.v); + const pointer15 = pointer14; + const value27 = _117; + if (pointer15.v === undefined) { + pointer15.v = value27; } else { - pointer13.v.v = value21.v; + pointer15.v.v = value27.v; } undefined; rs.byteVecPush(out, old14.v & 0xff); const _124: [rs.Cell, rs.u8] = pair.v; const _126: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; - const pointer14 = _124; - const object11 = pointer14; + const pointer16 = _124; + const object11 = pointer16; const copy6 = rs.objectCopy(object11); copy6[0] = new rs.Cell(object11[0].v); const old15 = copy6; - const object12 = pointer14; - const value22 = _126; + const object12 = pointer16; + const value28 = _126; const cell8 = object12[0]; - rs.ptrWrite(object12, value22); + rs.ptrWrite(object12, value28); if (cell8 !== undefined) { object12[0] = cell8; - cell8.v = value22[0].v; + cell8.v = value28[0].v; } undefined; rs.byteVecPush(out, old15[0].v & 0xff); } else if (_60 === 2) { - const pointer11 = nest; - const value19 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); - if (pointer11.v === undefined) { - pointer11.v = value19; + const pointer13 = nest; + const value25 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); + if (pointer13.v === undefined) { + pointer13.v = value25; } else { - pointer11.v.v = value19.v; + pointer13.v.v = value25.v; } undefined; const object10 = strukt.v; - const value20 = { c: new rs.Cell(((n as rs.u32) ^ 4) >>> 0), n }; + const value26 = { c: new rs.Cell(((n as rs.u32) ^ 4) >>> 0), n }; const cell7 = object10.c; - rs.ptrWrite(object10, value20); + rs.ptrWrite(object10, value26); if (cell7 !== undefined) { object10.c = cell7; - cell7.v = value20.c.v; + cell7.v = value26.c.v; } undefined; } else if (_60 === 3) { @@ -3780,20 +3780,20 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const x = nest; const y = other; const old12 = new rs.Cell(x.v.v); - const pointer9 = x; - const value15 = new rs.Cell(y.v.v); - if (pointer9.v === undefined) { - pointer9.v = value15; + const pointer11 = x; + const value21 = new rs.Cell(y.v.v); + if (pointer11.v === undefined) { + pointer11.v = value21; } else { - pointer9.v.v = value15.v; + pointer11.v.v = value21.v; } undefined; - const pointer10 = y; - const value16 = old12; - if (pointer10.v === undefined) { - pointer10.v = value16; + const pointer12 = y; + const value22 = old12; + if (pointer12.v === undefined) { + pointer12.v = value22; } else { - pointer10.v.v = value16.v; + pointer12.v.v = value22.v; } undefined; rs.byteVecPush(out, other.v.v & 0xff); @@ -3808,61 +3808,61 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const copy5 = rs.objectCopy(object7); copy5.c = new rs.Cell(object7.c.v); const object8 = x2; - const value17 = copy5; + const value23 = copy5; const cell5 = object8.c; - rs.ptrWrite(object8, value17); + rs.ptrWrite(object8, value23); if (cell5 !== undefined) { object8.c = cell5; - cell5.v = value17.c.v; + cell5.v = value23.c.v; } undefined; const object9 = y2; - const value18 = old13; + const value24 = old13; const cell6 = object9.c; - rs.ptrWrite(object9, value18); + rs.ptrWrite(object9, value24); if (cell6 !== undefined) { object9.c = cell6; - cell6.v = value18.c.v; + cell6.v = value24.c.v; } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(other2.c.v & 0xff, other2.n)), undefined, true); } else if (_60 === 4) { const _178: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; - const pointer8 = cell.v; - const object3 = pointer8; + const pointer10 = cell.v; + const object3 = pointer10; const copy3 = rs.objectCopy(object3); copy3[0] = new rs.Cell(object3[0].v); const old11 = copy3; - const object4 = pointer8; - const value13 = _178; + const object4 = pointer10; + const value19 = _178; const cell3 = object4[0]; - rs.ptrWrite(object4, value13); + rs.ptrWrite(object4, value19); if (cell3 !== undefined) { object4[0] = cell3; - cell3.v = value13[0].v; + cell3.v = value19[0].v; } undefined; rs.byteVecPush(out, old11[0].v & 0xff); const _188: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; const object5 = cell.v; - const value14 = _188; + const value20 = _188; const cell4 = object5[0]; - rs.ptrWrite(object5, value14); + rs.ptrWrite(object5, value20); if (cell4 !== undefined) { object5[0] = cell4; - cell4.v = value14[0].v; + cell4.v = value20[0].v; } undefined; } else if (_60 === 5) { toNest.v = rs.u32Mul(n as rs.u32, 3); toPair.v = rs.u32Mul(n as rs.u32, 5); const kept: rs.Cell = new rs.Cell(nest.v.v); - const pointer7 = nest; - const value12 = new rs.Cell(rs.u32Add(n as rs.u32, 7)); - if (pointer7.v === undefined) { - pointer7.v = value12; + const pointer9 = nest; + const value18 = new rs.Cell(rs.u32Add(n as rs.u32, 7)); + if (pointer9.v === undefined) { + pointer9.v = value18; } else { - pointer7.v.v = value12.v; + pointer9.v.v = value18.v; } undefined; rs.byteVecPush(out, kept.v & 0xff); @@ -3913,26 +3913,54 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _255: [rs.Place>, rs.Place>] = [rs.place(() => slot, (value8) => slot = value8), rs.place(() => pairSlot, (value9) => pairSlot = value9)]; const toSlot: rs.Place> = _255[0]; const toPairSlot: rs.Place> = _255[1]; - const _262: [rs.Place, [rs.u8, rs.u8]] = [rs.rawUnsize(toSlot, (p: rs.Place>) => p.v), rs.rawUnsize(toPairSlot, (p2: rs.Place>) => p2.v.v)]; + const _262: [rs.Place, [rs.u8, rs.u8]] = [rs.rawUnsize(toSlot, (p: rs.Place>) => rs.place(() => p.v.v, (value10) => p.v.v = value10)), rs.rawUnsize(toPairSlot, (p2: rs.Place>) => p2.v.v)]; const early: rs.Place = _262[0]; const earlyPair: [rs.u8, rs.u8] = _262[1]; const pointer5 = toSlot; - const value10 = new rs.Cell(((n as rs.u32) ^ 17) >>> 0); + const value11 = new rs.Cell(((n as rs.u32) ^ 17) >>> 0); if (pointer5.v === undefined) { - pointer5.v = value10; + pointer5.v = value11; } else { - pointer5.v.v = value10.v; + pointer5.v.v = value11.v; } undefined; const pointer6 = toPairSlot; - const value11 = new rs.Cell([n, ~n & 0xff] as [rs.u8, rs.u8]); + const value12 = new rs.Cell([n, ~n & 0xff] as [rs.u8, rs.u8]); if (pointer6.v === undefined) { - pointer6.v = value11; + pointer6.v = value12; } else { - rs.ptrWrite(pointer6.v.v, value11.v); + rs.ptrWrite(pointer6.v.v, value12.v); } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(early.v & 0xff, earlyPair[1])), undefined, true); + const room: rs.Vec> = rs.vecWithCapacity(2, 4); + const toRoom: rs.Place> = rs.vecAsPtr(room, 4); + const _285: [rs.Place, rs.Place] = [rs.rawUnsize(toRoom, (p3: rs.Place>) => rs.place(() => p3.v.v, (value13) => p3.v.v = value13)), rs.rawUnsize(rs.ptrAdd(toRoom, 2), (p4: rs.Place>) => rs.place(() => p4.v.v, (value14) => p4.v.v = value14))]; + const first: rs.Place = _285[0]; + const past: rs.Place = _285[1]; + const pointer7 = toRoom; + const value15 = new rs.Cell(((n as rs.u32) ^ 18) >>> 0); + if (pointer7.v === undefined) { + pointer7.v = value15; + } else { + pointer7.v.v = value15.v; + } + undefined; + rs.vecSetLen(room, 1, 4, (): rs.Cell => { + return (new rs.Cell(undefined) as unknown) as rs.Cell; + }); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(first.v & 0xff, past === rs.NULL ? 1 : 0, room.items[rs.idx(room.items, 0)].v & 0xff)), undefined, true); + let uninit: rs.Cell = new rs.Cell(n as rs.u32); + const readOut: rs.Cell = new rs.Cell(uninit.v); + const pointer8 = rs.place(() => uninit, (value16) => uninit = value16); + const value17 = new rs.Cell(((n as rs.u32) ^ 19) >>> 0); + if (pointer8.v === undefined) { + pointer8.v = value17; + } else { + pointer8.v.v = value17.v; + } + undefined; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(readOut.v & 0xff, uninit.v & 0xff)), undefined, true); const src: rs.Cell<[rs.Cell, [rs.u32, rs.u32]]> = new rs.Cell([new rs.Cell(n as rs.u32), [2, 3] as [rs.u32, rs.u32]] as [rs.Cell, [rs.u32, rs.u32]]); const object2 = src.v; const copy2 = rs.objectCopy(object2); @@ -3942,21 +3970,21 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { src.v[1][0] = 9; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy[1][0] & 0xff, src.v[1][0] & 0xff)), undefined, true); } else { - const _299: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); - const _302: rs.Place> = nest; - const value = _299; - if (_302.v === undefined) { - _302.v = value; + const _347: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); + const _350: rs.Place> = nest; + const value = _347; + if (_350.v === undefined) { + _350.v = value; } else { - _302.v.v = value.v; + _350.v.v = value.v; } - const _304: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); - const _308: rs.Place>> = deep; - const value2 = _304; - if (_308.v === undefined) { - _308.v = value2; + const _352: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); + const _356: rs.Place>> = deep; + const value2 = _352; + if (_356.v === undefined) { + _356.v = value2; } else { - _308.v.v.v = value2.v.v; + _356.v.v.v = value2.v.v; } } rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toNest.v & 0xff, toDeep.v & 0xff, toPair.v & 0xff, toStrukt.v & 0xff)), undefined, true); diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index b96c2c9d5..4643d428d 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2931,6 +2931,16 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { ptr::write(to_slot, UnsafeCell::new(u32::from(n) ^ 17)); ptr::write(to_pair_slot, UnsafeCell::new((n, !n))); out.extend([*early as u8, (*early_pair).1]); + let mut room = Vec::>::with_capacity(2); + let to_room = room.as_mut_ptr(); + let (first, past) = (UnsafeCell::raw_get(to_room), UnsafeCell::raw_get(to_room.add(2))); + ptr::write(to_room, UnsafeCell::new(u32::from(n) ^ 18)); + room.set_len(1); + out.extend([*first as u8, past.is_null() as u8, room[0].get().read() as u8]); + let mut uninit = MaybeUninit::new(UnsafeCell::new(u32::from(n))); + let read_out = uninit.assume_init_read(); + ptr::write(uninit.as_mut_ptr(), UnsafeCell::new(u32::from(n) ^ 19)); + out.extend([read_out.into_inner() as u8, *uninit.assume_init_ref().get() as u8]); let src = UnsafeCell::new((UnsafeCell::new(u32::from(n)), (2u32, 3u32))); let copy = ptr::read(src.get()); (*src.get()).1.0 = 9; diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index 0b885bfbd..26f13f4f1 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -871,6 +871,7 @@ pub fn call<'tcx>( } } // A shared reference to a value JS copies is the value. + ("MaybeUninit", "assume_init_read") if b.t.keeps_cells(pointee()) => copy_kept(b, arg(), pointee(), span, out), ("MaybeUninit", "assume_init_read") => b.t.snapshot(arg(), pointee(), span), ("MaybeUninit", "assume_init_drop") | ("ManuallyDrop", "drop") => { let p = arg(); @@ -887,7 +888,10 @@ pub fn call<'tcx>( Some(Pointee::Place) => p.member("v"), _ => p, }; - b.t.snapshot(value, pointee(), span) + match b.t.keeps_cells(pointee()) { + true => copy_kept(b, value, pointee(), span, out), + false => b.t.snapshot(value, pointee(), span), + } } // An `rs.Cell`, a `Cell`'s too, which is the place a pointer to a value JS copies is, // and holds the object a pointer to any other is. @@ -897,7 +901,8 @@ pub fn call<'tcx>( cell_pointer(b, cell, pointee()) } // `get` of the cell where the pointer points, an `rs.Place` of where it is kept, or - // the pointer itself if it points at no memory, as a cast of it would. + // the pointer itself if it points at no memory, as a cast of it would. A cell of an + // object or array must be there, as the pointer is what it holds. ("UnsafeCell", "raw_get") => { let tcx = b.t.tcx; let cell = tcx @@ -910,7 +915,24 @@ pub fn call<'tcx>( } let name = b.fresh("p"); let p = Expr::ident(&name); - let made = cell_pointer(b, if object { p } else { p.member("v") }, pointee()); + let made = match (object, b.t.pointee(pointee())) { + // The place of the value in whatever cell is there as it is read, so the + // memory may hold none yet, as a `Vec`'s room past its length doesn't. + (false, Pointee::Place) => { + let value = b.fresh("value"); + let at = || p.clone().member("v").member("v"); + let get = Expr::Lambda(Vec::new(), Box::new(at())); + let set = Expr::Lambda( + vec![crate::ts::Param { + name: value.clone(), + ty: None, + }], + Box::new(at().assign(Expr::ident(&value))), + ); + Expr::rs("place", vec![get, set]) + } + _ => cell_pointer(b, if object { p } else { p.member("v") }, pointee()), + }; // Typed, as a null pointer's own type is none. let ty = Some(b.t.ty(Ty::new_imm_ptr(tcx, cell), span)); let make = Expr::Lambda(vec![crate::ts::Param { name, ty }], Box::new(made)); From 3d8d2cfe84cedf2c95fad470e618de436e734bb6 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 11:15:58 -0800 Subject: [PATCH 14/17] test(ts): refresh reject spans after formatting Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/reject/census.txt | 2 +- ts/fixtures/reject/expected.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index 2e23c3554..5bf7ff024 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -11,7 +11,7 @@ fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:cell_of_arrays+1:10: `UnsafeCell::from_mut` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it reads a copy of, so a write through its pointer would land in the copy -fixtures/reject/src/alloc.rs:cell_of_arrays+4:4: `Cell::swap` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it may read a copy of +fixtures/reject/src/alloc.rs:cell_of_arrays+7:4: `Cell::swap` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it may read a copy of fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 45fa10867..c3ddc68f4 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -15,7 +15,7 @@ fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:cell_of_arrays+1:10: `UnsafeCell::from_mut` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it reads a copy of, so a write through its pointer would land in the copy -fixtures/reject/src/alloc.rs:cell_of_arrays+4:4: `Cell::swap` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it may read a copy of +fixtures/reject/src/alloc.rs:cell_of_arrays+7:4: `Cell::swap` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it may read a copy of fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address From e0338a85fc293da8941eb39156a8486d8b9637e7 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 11:43:51 -0800 Subject: [PATCH 15/17] fix(ts): follow shared raw_get origins, take's Default first, compare Cell copies, and refuse Cell::swap of arrays raw_origins unwinds its cycle check, so two branches through one base pointer both contribute origins. Cell::take makes its Default before it copies out the value replaced. A Cell compares copies of its values, as get gives them, so a comparison writing the cell sees what it held. Cell::swap of an array is refused, as cells of arrays may partially overlap, which Rust panics on. Co-Authored-By: Claude Opus 5.5 --- .../lang/collections/expected/collections.ts | 56 +++--- ts/fixtures/lang/pointers/expected/lib.ts | 5 + .../pointers/expected/pointers.Reentrant.ts | 26 +++ .../lang/pointers/expected/pointers.ts | 185 ++++++++++-------- ts/fixtures/lang/pointers/src/pointers.rs | 42 +++- ts/fixtures/reject/census.txt | 3 +- ts/fixtures/reject/expected.txt | 3 +- ts/fixtures/reject/src/alloc.rs | 8 +- ts/runtime/rs/task.ts | 6 +- ts/src/lower/collections.rs | 15 +- ts/src/lower/copy.rs | 5 +- ts/src/lower/mir/call.rs | 3 + ts/src/lower/task.rs | 11 +- 13 files changed, 232 insertions(+), 136 deletions(-) create mode 100644 ts/fixtures/lang/pointers/expected/pointers.Reentrant.ts diff --git a/ts/fixtures/lang/collections/expected/collections.ts b/ts/fixtures/lang/collections/expected/collections.ts index 49f4eae5d..65efd5ce8 100644 --- a/ts/fixtures/lang/collections/expected/collections.ts +++ b/ts/fixtures/lang/collections/expected/collections.ts @@ -4340,14 +4340,14 @@ export function cellEqs(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void const _23: [rs.Cell, rs.Cell] = [new rs.Cell(copy$collections_Quirky(x)), new rs.Cell(copy$collections_Quirky(y))]; const ca: rs.Cell = _23[0]; const cb: rs.Cell = _23[1]; - putEq(rs.cellBy(Quirky.eq$cmp_PartialEq)(ca, cb), log, out); - putEq(!rs.cellBy(Quirky.eq$cmp_PartialEq)(ca, cb), log, out); - putEq(rs.cellBy(Quirky.lt$cmp_PartialOrd)(ca, cb), log, out); - putEq(rs.cellBy(Quirky.le$cmp_PartialOrd)(ca, cb), log, out); - putEq(rs.cellBy(Quirky.gt$cmp_PartialOrd)(ca, cb), log, out); - putEq(rs.cellBy(Quirky.ge$cmp_PartialOrd)(ca, cb), log, out); - putOrder(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd)(ca, cb), log, out); - putCmp(rs.cellBy(Quirky.cmp$cmp_Ord)(ca, cb), log, out); + putEq(rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky)(ca, cb), log, out); + putEq(!rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky)(ca, cb), log, out); + putEq(rs.cellBy(Quirky.lt$cmp_PartialOrd, copy$collections_Quirky)(ca, cb), log, out); + putEq(rs.cellBy(Quirky.le$cmp_PartialOrd, copy$collections_Quirky)(ca, cb), log, out); + putEq(rs.cellBy(Quirky.gt$cmp_PartialOrd, copy$collections_Quirky)(ca, cb), log, out); + putEq(rs.cellBy(Quirky.ge$cmp_PartialOrd, copy$collections_Quirky)(ca, cb), log, out); + putOrder(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd, copy$collections_Quirky)(ca, cb), log, out); + putCmp(rs.cellBy(Quirky.cmp$cmp_Ord, copy$collections_Quirky)(ca, cb), log, out); const _68: [rs.RefCell, rs.RefCell] = [new rs.RefCell(copy$collections_Quirky(x)), new rs.RefCell(copy$collections_Quirky(y))]; const ra: rs.RefCell = _68[0]; const rb: rs.RefCell = _68[1]; @@ -4434,40 +4434,40 @@ export function cellEqs(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void const _404: [rs.RefCell>, rs.RefCell>] = [new rs.RefCell(new rs.Cell(copy$collections_Quirky(x))), new rs.RefCell(new rs.Cell(copy$collections_Quirky(y)))]; const rca: rs.RefCell> = _404[0]; const rcb: rs.RefCell> = _404[1]; - putEq(rs.refCellBy(rs.cellBy(Quirky.eq$cmp_PartialEq))(rca, rcb), log, out); - putEq(!rs.refCellBy(rs.cellBy(Quirky.eq$cmp_PartialEq))(rca, rcb), log, out); - putEq(rs.refCellBy(rs.cellBy(Quirky.lt$cmp_PartialOrd))(rca, rcb), log, out); - putEq(rs.refCellBy(rs.cellBy(Quirky.le$cmp_PartialOrd))(rca, rcb), log, out); - putEq(rs.refCellBy(rs.cellBy(Quirky.gt$cmp_PartialOrd))(rca, rcb), log, out); - putEq(rs.refCellBy(rs.cellBy(Quirky.ge$cmp_PartialOrd))(rca, rcb), log, out); - putOrder(rs.refCellBy(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd))(rca, rcb), log, out); - putCmp(rs.refCellBy(rs.cellBy(Quirky.cmp$cmp_Ord))(rca, rcb), log, out); + putEq(rs.refCellBy(rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky))(rca, rcb), log, out); + putEq(!rs.refCellBy(rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky))(rca, rcb), log, out); + putEq(rs.refCellBy(rs.cellBy(Quirky.lt$cmp_PartialOrd, copy$collections_Quirky))(rca, rcb), log, out); + putEq(rs.refCellBy(rs.cellBy(Quirky.le$cmp_PartialOrd, copy$collections_Quirky))(rca, rcb), log, out); + putEq(rs.refCellBy(rs.cellBy(Quirky.gt$cmp_PartialOrd, copy$collections_Quirky))(rca, rcb), log, out); + putEq(rs.refCellBy(rs.cellBy(Quirky.ge$cmp_PartialOrd, copy$collections_Quirky))(rca, rcb), log, out); + putOrder(rs.refCellBy(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd, copy$collections_Quirky))(rca, rcb), log, out); + putCmp(rs.refCellBy(rs.cellBy(Quirky.cmp$cmp_Ord, copy$collections_Quirky))(rca, rcb), log, out); const _452: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _455: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - putEq(rs.eqOption(rs.cellBy(Quirky.eq$cmp_PartialEq))(_452, _455), log, out); + putEq(rs.eqOption(rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky))(_452, _455), log, out); const _461: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _464: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - putEq(!rs.eqOption(rs.cellBy(Quirky.eq$cmp_PartialEq))(_461, _464), log, out); + putEq(!rs.eqOption(rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky))(_461, _464), log, out); const _470: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _473: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - putEq(rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd))(_470, _473) === -1, log, out); + putEq(rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd, copy$collections_Quirky))(_470, _473) === -1, log, out); const _479: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _482: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - const order7 = rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd))(_479, _482); + const order7 = rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd, copy$collections_Quirky))(_479, _482); putEq(order7 !== undefined && order7 <= 0, log, out); const _488: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _491: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - putEq(rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd))(_488, _491) === 1, log, out); + putEq(rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd, copy$collections_Quirky))(_488, _491) === 1, log, out); const _497: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _500: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - const order8 = rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd))(_497, _500); + const order8 = rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd, copy$collections_Quirky))(_497, _500); putEq(order8 !== undefined && order8 >= 0, log, out); const _506: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _509: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - putOrder(rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd))(_506, _509), log, out); + putOrder(rs.cmpOption(rs.cellBy(Quirky.partialCmp$cmp_PartialOrd, copy$collections_Quirky))(_506, _509), log, out); const _515: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(x)); const _518: rs.Cell | undefined = new rs.Cell(copy$collections_Quirky(y)); - putCmp(rs.cmpOption(rs.cellBy(Quirky.cmp$cmp_Ord))(_515, _518), log, out); + putCmp(rs.cmpOption(rs.cellBy(Quirky.cmp$cmp_Ord, copy$collections_Quirky))(_515, _518), log, out); const _524: [rs.RefCell, Quirky.Quirky] = [new rs.RefCell(copy$collections_Quirky(x)), x]; const _527: [rs.RefCell, Quirky.Quirky] = [new rs.RefCell(copy$collections_Quirky(y)), y]; putEq(rs.eqTuple([rs.refCellBy(Quirky.eq$cmp_PartialEq), Quirky.eq$cmp_PartialEq])(_524, _527), log, out); @@ -4513,7 +4513,7 @@ export function cellEqs(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void const _629: rs.Vec> = each$collections_Quirky_Cell_collections_Quirky_collections_cell_eqs_closure3(rs.sliceFrom(vb.items), (_22: Quirky.Quirky): rs.Cell => { return new rs.Cell(copy$collections_Quirky(copy$collections_Quirky(_22))); }); - putEq(rs.eqSlice(rs.cellBy(Quirky.eq$cmp_PartialEq))(_625, _629), log, out); + putEq(rs.eqSlice(rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky))(_625, _629), log, out); const _636: rs.Vec> = each$collections_Quirky_cell_RefCell_collections_Quirky_collections_cell_eqs_closure4(rs.sliceFrom(va.items), (_26: Quirky.Quirky): rs.RefCell => { return new rs.RefCell(copy$collections_Quirky(copy$collections_Quirky(_26))); }); @@ -4523,13 +4523,13 @@ export function cellEqs(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void putEq(!rs.eqSlice(rs.refCellBy(Quirky.eq$cmp_PartialEq))(_636, _640), log, out); const _647: [rs.Cell, rs.RefCell] = [new rs.Cell(copy$collections_Quirky(x)), new rs.RefCell(copy$collections_Quirky(y))]; const _651: [rs.Cell, rs.RefCell] = [new rs.Cell(copy$collections_Quirky(y)), new rs.RefCell(copy$collections_Quirky(x))]; - putEq(!rs.eqTuple([rs.cellBy(Quirky.eq$cmp_PartialEq), rs.refCellBy(Quirky.eq$cmp_PartialEq)])(_647, _651), log, out); + putEq(!rs.eqTuple([rs.cellBy(Quirky.eq$cmp_PartialEq, copy$collections_Quirky), rs.refCellBy(Quirky.eq$cmp_PartialEq)])(_647, _651), log, out); const _658: rs.Arc> = new rs.Arc(new rs.RefCell(copy$collections_Quirky(x))); const _661: rs.Arc> = new rs.Arc(new rs.RefCell(copy$collections_Quirky(y))); putEq(!rs.eqArc(rs.refCellBy(Quirky.eq$cmp_PartialEq), true)(_658, _661), log, out); _661[Symbol.dispose](); _658[Symbol.dispose](); - putEq(copy$collections_Quirky(rs.ordMax(new rs.Cell(copy$collections_Quirky(x)), new rs.Cell(copy$collections_Quirky(y)), rs.cellBy(Quirky.lt$cmp_PartialOrd)).v)._0 === x._0, log, out); + putEq(copy$collections_Quirky(rs.ordMax(new rs.Cell(copy$collections_Quirky(x)), new rs.Cell(copy$collections_Quirky(y)), rs.cellBy(Quirky.lt$cmp_PartialOrd, copy$collections_Quirky)).v)._0 === x._0, log, out); const _679: rs.RefCell = rs.refCellBorrow(rs.ordMin(new rs.RefCell(copy$collections_Quirky(x)), new rs.RefCell(copy$collections_Quirky(y)), rs.refCellBy(Quirky.lt$cmp_PartialOrd))); putEq(_679.value._0 === x._0, log, out); rs.refRelease(_679); @@ -4540,7 +4540,7 @@ export function cellEqs(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void } const lo: Quirky.Quirky = _688[0]; const hi: Quirky.Quirky = _688[1]; - rs.byteVecPush(out, copy$collections_Quirky(rs.ordClamp(new rs.Cell({ _0: at(2), _1: log }), new rs.Cell(copy$collections_Quirky(lo)), new rs.Cell(copy$collections_Quirky(hi)), rs.cellBy(Quirky.le$cmp_PartialOrd), rs.cellBy(Quirky.lt$cmp_PartialOrd), rs.cellBy(Quirky.gt$cmp_PartialOrd)).v)._0 & 0xff); + rs.byteVecPush(out, copy$collections_Quirky(rs.ordClamp(new rs.Cell({ _0: at(2), _1: log }), new rs.Cell(copy$collections_Quirky(lo)), new rs.Cell(copy$collections_Quirky(hi)), rs.cellBy(Quirky.le$cmp_PartialOrd, copy$collections_Quirky), rs.cellBy(Quirky.lt$cmp_PartialOrd, copy$collections_Quirky), rs.cellBy(Quirky.gt$cmp_PartialOrd, copy$collections_Quirky)).v)._0 & 0xff); rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(rs.cellReplace(log, 0)).reverse()), undefined, true); const once: (a0: rs.usize, a1: Quirky.Quirky) => rs.OnceCell = (i3: rs.usize, v: Quirky.Quirky): rs.OnceCell => { const cell: rs.OnceCell = new rs.OnceCell(undefined); diff --git a/ts/fixtures/lang/pointers/expected/lib.ts b/ts/fixtures/lang/pointers/expected/lib.ts index 7c4f38c67..d654e3dda 100644 --- a/ts/fixtures/lang/pointers/expected/lib.ts +++ b/ts/fixtures/lang/pointers/expected/lib.ts @@ -17,6 +17,7 @@ import * as List from "./pointers.List.ts"; import type * as Outer from "./pointers.Outer.ts"; import type * as Pair from "./pointers.Pair.ts"; import type * as Paired from "./pointers.Paired.ts"; +import type * as Reentrant from "./pointers.Reentrant.ts"; import type * as Spot from "./pointers.Spot.ts"; import type * as Twin from "./pointers.Twin.ts"; @@ -141,6 +142,10 @@ export function copy$pointers_Pair(v: Pair.Pair): Pair.Pair { return { a: v.a, b: v.b }; } +export function copy$pointers_Reentrant(v: Reentrant.Reentrant): Reentrant.Reentrant { + return { _0: v._0, _1: v._1 }; +} + export function copy$pointers_Spot(v: Spot.Spot): Spot.Spot { return { at: copy$tuple2_u16_u8(v.at), tag: v.tag }; } diff --git a/ts/fixtures/lang/pointers/expected/pointers.Reentrant.ts b/ts/fixtures/lang/pointers/expected/pointers.Reentrant.ts new file mode 100644 index 000000000..b1fe9e0dc --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.Reentrant.ts @@ -0,0 +1,26 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; +import { REENTERED } from "./pointers.ts"; + +/** A struct Rust copies whose `default` and `eq` write the cell holding it. */ +export interface Reentrant { + _0: rs.u8; + _1: rs.u8; +} + +export function default$Default(): Reentrant { + ((c: rs.Cell): undefined => { + rs.ptrWrite(c.v, { _0: 7, _1: 7 }); + })(REENTERED.get()); + return { _0: 0, _1: 0 }; +} + +export function eq$cmp_PartialEq(self: Reentrant, other: Reentrant): boolean { + ((c: rs.Cell): undefined => { + rs.ptrWrite(c.v, { _0: 9, _1: 9 }); + })(REENTERED.get()); + return self._0 === other._0; +} + +//# sourceMappingURL=pointers.Reentrant.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index fec87f7b9..7413590fa 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_tuple2_u8_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_tuple2_u8_u8, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Either_u32, copy$pointers_Either_u8, copy$pointers_Halves, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u16, copy$tuple2_u8_u8, drop$pointers_List_String, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; +import { copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_tuple2_u8_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_tuple2_u8_u8, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Either_u32, copy$pointers_Either_u8, copy$pointers_Halves, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Reentrant, copy$pointers_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u16, copy$tuple2_u8_u8, drop$pointers_List_String, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; import { then$0bgt67i, then$1a5xgoh, then$0jw3xip } from "./core/bool.ts"; import { isSomeAnd$1s2u5lz, isSomeAnd$1nosv3e, isSomeAnd$07atiew, unwrapOr$0mkoact as option_unwrapOr$0mkoact, unwrapOr$179xo1w as option_unwrapOr$179xo1w, unwrapOr$1fr498u as option_unwrapOr$1fr498u, map$1cfrse5 as option_map$1cfrse5, mapOr$078vnqn as option_mapOr$078vnqn, mapOr$00zno7m as option_mapOr$00zno7m, mapOr$173j2gr as option_mapOr$173j2gr, filter$0wvwd70 } from "./core/option.ts"; import type * as Aimed from "./pointers.Aimed.ts"; @@ -33,6 +33,7 @@ import type * as Pair from "./pointers.Pair.ts"; import * as Paired from "./pointers.Paired.ts"; import * as Pointing from "./pointers.Pointing.ts"; import type * as Reattached from "./pointers.Reattached.ts"; +import * as Reentrant from "./pointers.Reentrant.ts"; import type * as Refilled from "./pointers.Refilled.ts"; import type * as Run from "./pointers.Run.ts"; import * as Segment from "./pointers.Segment.ts"; @@ -3155,8 +3156,8 @@ export function genericUnions(data: rs.ByteSlice, out: rs.ByteVec): void { * `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, * an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a * pointer taken before sees, giving back what it held undropped; what `as_ref_unchecked` - * and `as_mut_unchecked` lend; and the allocations of boxes and pinned boxes replaced and swapped through pointers, `UnsafeCell`s, one - * `from_mut` makes, and `Cell`s. + * and `as_mut_unchecked` lend; and the allocations of boxes and pinned boxes replaced and + * swapped through pointers, `UnsafeCell`s, one `from_mut` makes, and `Cell`s. */ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const count: rs.Arc = new rs.Arc(undefined); @@ -3435,11 +3436,18 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { count[Symbol.dispose](); } +export const REENTERED: rs.LocalKey> = /* @__PURE__ */ new rs.LocalKey(REENTERED$init, false); + +export function REENTERED$init(): rs.Cell { + return new rs.Cell({ _0: 1, _1: 1 }); +} + /** * What `Cell`s of a tuple, a struct Rust copies, one that owns a count, and arrays of bytes * and of numbers, and cells `Cell::from_mut` makes of a tuple and an array, hold after - * `replace`, `set`, `swap`, `take`, and `update`, each read back through a pointer - * `as_ptr` gave before, and what each gives back and drops. + * `replace`, `set`, `swap` (but of the arrays), `take`, and `update`, each read back + * through a pointer `as_ptr` gave before, and what each gives back and drops; and `take` + * and `==` of a cell its value's `default` and `eq` write. */ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { const count: rs.Arc = new rs.Arc(undefined); @@ -3465,40 +3473,40 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { const _46: rs.u8 = rs.u8Rem(op, 8); if (_46 === 0) { const _50: [rs.u8, rs.u16] = [n, ((n as rs.u16) << 8) & 0xffff]; - const pointer14 = pair.v; - const old17 = rs.objectCopy(pointer14); - rs.ptrWrite(pointer14, _50); - const _57: rs.u8 = old17[0]; + const pointer15 = pair.v; + const old16 = rs.objectCopy(pointer15); + rs.ptrWrite(pointer15, _50); + const _57: rs.u8 = old16[0]; const _61: Duo.Duo = { _0: n, _1: ~n & 0xff }; - const pointer15 = duo.v; - const old18 = rs.objectCopy(pointer15); - rs.ptrWrite(pointer15, _61); - const _58: rs.u8 = old18._1; + const pointer16 = duo.v; + const old17 = rs.objectCopy(pointer16); + rs.ptrWrite(pointer16, _61); + const _58: rs.u8 = old17._1; const _66: Held.Held = held(n); - const pointer16 = owner.v; - const old19 = rs.objectCopy(pointer16); - rs.ptrWrite(pointer16, _66); - const _64: Held.Held = old19; + const pointer17 = owner.v; + const old18 = rs.objectCopy(pointer17); + rs.ptrWrite(pointer17, _66); + const _64: Held.Held = old18; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_57, _58, _64.n)), undefined, true); _64[Symbol.dispose](); const _72: Uint8Array = Uint8Array.of(n, 1); - const pointer17 = rs.byteSliceFrom(bytes.v); - const old20 = rs.byteSliceToArray(pointer17); - rs.byteSliceSet(pointer17, _72); - rs.iterExtendBytes(out, rs.iterSlice(old20), undefined, true); + const pointer18 = rs.byteSliceFrom(bytes.v); + const old19 = rs.byteSliceToArray(pointer18); + rs.byteSliceSet(pointer18, _72); + rs.iterExtendBytes(out, rs.iterSlice(old19), undefined, true); const _78: rs.u16[] = [n as rs.u16, 2]; - const pointer18 = rs.sliceFrom(wide.v); - const old21 = rs.objectCopy(rs.sliceView(pointer18)); - rs.sliceSet(pointer18, _78); - rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old21[1]).reverse()), undefined, true); + const pointer19 = rs.sliceFrom(wide.v); + const old20 = rs.objectCopy(rs.sliceView(pointer19)); + rs.sliceSet(pointer19, _78); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old20[1]).reverse()), undefined, true); } else if (_46 === 1) { rs.ptrWrite(pair.v, [n ^ 1, 3] as [rs.u8, rs.u16]); rs.ptrWrite(duo.v, { _0: n ^ 2, _1: 4 }); const _92: Held.Held = held(n ^ 3); - const pointer13 = owner.v; - const old16 = rs.objectCopy(pointer13); - rs.ptrWrite(pointer13, _92); - old16[Symbol.dispose](); + const pointer14 = owner.v; + const old15 = rs.objectCopy(pointer14); + rs.ptrWrite(pointer14, _92); + old15[Symbol.dispose](); const _98: Uint8Array = Uint8Array.of(n ^ 4, 5); rs.byteSliceSet(rs.byteSliceFrom(bytes.v), _98); const _102: rs.u16[] = [6, ((n as rs.u16) << 4) & 0xffff]; @@ -3507,87 +3515,91 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { const _110: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([n, 7] as [rs.u8, rs.u16]); const x2 = pair.v; const y2 = _110.v; - const old11 = rs.objectCopy(x2); + const old12 = rs.objectCopy(x2); rs.ptrWrite(x2, rs.objectCopy(y2)); - rs.ptrWrite(y2, old11); + rs.ptrWrite(y2, old12); const other: rs.Cell = new rs.Cell({ _0: 8, _1: n }); const x3 = duo.v; const y3 = other.v; - const old12 = rs.objectCopy(x3); + const old13 = rs.objectCopy(x3); rs.ptrWrite(x3, rs.objectCopy(y3)); - rs.ptrWrite(y3, old12); + rs.ptrWrite(y3, old13); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy$pointers_Duo(other.v)._0, copy$pointers_Duo(other.v)._1)), undefined, true); const other2: rs.Cell = new rs.Cell(held(n ^ 5)); const x4 = owner.v; const y4 = other2.v; - const old13 = rs.objectCopy(x4); + const old14 = rs.objectCopy(x4); rs.ptrWrite(x4, rs.objectCopy(y4)); - rs.ptrWrite(y4, old13); + rs.ptrWrite(y4, old14); const _135: Held.Held = other2.v; rs.byteVecPush(out, _135.n); _135[Symbol.dispose](); - const x5 = rs.byteSliceFrom(bytes.v); - const y5 = rs.byteSliceFrom(bytes.v); - const old14 = rs.byteSliceToArray(x5); - rs.byteSliceSet(x5, rs.byteSliceToArray(y5)); - rs.byteSliceSet(y5, old14); - const _143: rs.Cell = new rs.Cell([n as rs.u16, 9]); - const x6 = rs.sliceFrom(wide.v); - const y6 = rs.sliceFrom(_143.v); - const old15 = rs.objectCopy(rs.sliceView(x6)); - rs.sliceSet(x6, rs.objectCopy(rs.sliceView(y6))); - rs.sliceSet(y6, old15); } else if (_46 === 3) { - const pointer10 = pair.v; - const old8 = rs.objectCopy(pointer10); - rs.ptrWrite(pointer10, [0, 0]); - rs.byteVecPush(out, old8[0]); - const pointer11 = rs.byteSliceFrom(bytes.v); - const old9 = rs.byteSliceToArray(pointer11); - rs.byteSliceSet(pointer11, rs.bytes(2)); - rs.iterExtendBytes(out, rs.iterSlice(old9), undefined, true); - const pointer12 = rs.sliceFrom(wide.v); - const old10 = rs.objectCopy(rs.sliceView(pointer12)); - rs.sliceSet(pointer12, Array.from({ length: 2 }, (): rs.u16 => { + const value3 = [0, 0]; + const pointer11 = pair.v; + const old9 = rs.objectCopy(pointer11); + rs.ptrWrite(pointer11, value3); + rs.byteVecPush(out, old9[0]); + const value4 = rs.bytes(2); + const pointer12 = rs.byteSliceFrom(bytes.v); + const old10 = rs.byteSliceToArray(pointer12); + rs.byteSliceSet(pointer12, value4); + rs.iterExtendBytes(out, rs.iterSlice(old10), undefined, true); + const value5 = Array.from({ length: 2 }, (): rs.u16 => { return 0; - })); - rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old10[0]).reverse()), undefined, true); + }); + const pointer13 = rs.sliceFrom(wide.v); + const old11 = rs.objectCopy(rs.sliceView(pointer13)); + rs.sliceSet(pointer13, value5); + rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(old11[0]).reverse()), undefined, true); } else if (_46 === 4) { - const _162: (a0: [rs.u8, rs.u16]) => [rs.u8, rs.u16] = (_25: [rs.u8, rs.u16]): [rs.u8, rs.u16] => { + const _153: (a0: [rs.u8, rs.u16]) => [rs.u8, rs.u16] = (_25: [rs.u8, rs.u16]): [rs.u8, rs.u16] => { const a3: rs.u8 = _25[0]; const b3: rs.u16 = _25[1]; return [rs.u8WrappingAdd(a3, n), b3 ^ 1] as [rs.u8, rs.u16]; }; - const pointer6 = pair.v; - rs.ptrWrite(pointer6, _162(copy$tuple2_u8_u16(pointer6))); - const _166: (a0: Duo.Duo) => Duo.Duo = (_27: Duo.Duo): Duo.Duo => { + const pointer7 = pair.v; + rs.ptrWrite(pointer7, _153(copy$tuple2_u8_u16(pointer7))); + const _157: (a0: Duo.Duo) => Duo.Duo = (_27: Duo.Duo): Duo.Duo => { return { _0: _27._1, _1: _27._0 ^ n }; }; - const pointer7 = duo.v; - rs.ptrWrite(pointer7, _166(copy$pointers_Duo(pointer7))); - const _170: (a0: Uint8Array) => Uint8Array = (_28: Uint8Array): Uint8Array => { + const pointer8 = duo.v; + rs.ptrWrite(pointer8, _157(copy$pointers_Duo(pointer8))); + const _161: (a0: Uint8Array) => Uint8Array = (_28: Uint8Array): Uint8Array => { return Uint8Array.of(_28[1], rs.u8WrappingMul(_28[0], n)); }; - const pointer8 = rs.byteSliceFrom(bytes.v); - rs.byteSliceSet(pointer8, _170(rs.byteSliceToArray(pointer8))); - const _174: (a0: rs.u16[]) => rs.u16[] = (_29: rs.u16[]): rs.u16[] => { + const pointer9 = rs.byteSliceFrom(bytes.v); + rs.byteSliceSet(pointer9, _161(rs.byteSliceToArray(pointer9))); + const _165: (a0: rs.u16[]) => rs.u16[] = (_29: rs.u16[]): rs.u16[] => { const a6: rs.u16 = _29[0]; return [rs.intRotateLeft(_29[1], n as rs.u32, 16, false), a6]; }; - const pointer9 = rs.sliceFrom(wide.v); - rs.sliceSet(pointer9, _174(rs.sliceToArray(pointer9))); + const pointer10 = rs.sliceFrom(wide.v); + rs.sliceSet(pointer10, _165(rs.sliceToArray(pointer10))); + } else if (_46 === 6) { + ((c3: rs.Cell): undefined => { + rs.ptrWrite(c3.v, { _0: n, _1: 1 }); + const value2 = Reentrant.default$Default(); + const pointer6 = c3.v; + const old8 = rs.objectCopy(pointer6); + rs.ptrWrite(pointer6, value2); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old8._0, copy$pointers_Reentrant(c3.v)._0)), undefined, true); + rs.ptrWrite(c3.v, { _0: n, _1: 1 }); + const _172: rs.Cell = new rs.Cell({ _0: n, _1: 2 }); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(rs.cellBy(Reentrant.eq$cmp_PartialEq, copy$pointers_Reentrant)(c3, _172) ? 1 : 0, copy$pointers_Reentrant(c3.v)._0)), undefined, true); + })(REENTERED.get()); } else if (_46 === 5) { const c: rs.Cell<[rs.u8, rs.u16]> = rs.cellOfPlace(rs.partPlace(lent)); const p: [rs.u8, rs.u16] = c.v; rs.ptrWrite(c.v, [n, 1] as [rs.u8, rs.u16]); - const _182: [rs.u8, rs.u16] = [n ^ 1, 2]; + const _176: [rs.u8, rs.u16] = [n ^ 1, 2]; const pointer = c.v; const old4 = rs.objectCopy(pointer); - rs.ptrWrite(pointer, _182); + rs.ptrWrite(pointer, _176); const old2: [rs.u8, rs.u16] = old4; - const _186: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([n ^ 2, 3] as [rs.u8, rs.u16]); + const _180: rs.Cell<[rs.u8, rs.u16]> = new rs.Cell([n ^ 2, 3] as [rs.u8, rs.u16]); const x = c.v; - const y = _186.v; + const y = _180.v; const old5 = rs.objectCopy(x); rs.ptrWrite(x, rs.objectCopy(y)); rs.ptrWrite(y, old5); @@ -3595,18 +3607,19 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { rs.ptrWrite(pointer2, ((_2: [rs.u8, rs.u16]): [rs.u8, rs.u16] => { return [_2[0] ^ 4, _2[1]] as [rs.u8, rs.u16]; })(copy$tuple2_u8_u16(pointer2))); - const _192: rs.u8 = old2[0]; - const _193: rs.u8 = p[0]; + const _186: rs.u8 = old2[0]; + const _187: rs.u8 = p[0]; + const value = [0, 0]; const pointer3 = c.v; const old6 = rs.objectCopy(pointer3); - rs.ptrWrite(pointer3, [0, 0]); - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_192, _193, old6[0])), undefined, true); + rs.ptrWrite(pointer3, value); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_186, _187, old6[0])), undefined, true); const c2: rs.Cell = rs.cellOfPlace(rs.partPlace(rs.byteSliceView(rs.byteSliceFrom(lentBytes)))); const p2: rs.ByteSlice = rs.byteSliceFrom(c2.v); - const _200: Uint8Array = Uint8Array.of(n, 5); + const _194: Uint8Array = Uint8Array.of(n, 5); const pointer4 = rs.byteSliceFrom(c2.v); const old7 = rs.byteSliceToArray(pointer4); - rs.byteSliceSet(pointer4, _200); + rs.byteSliceSet(pointer4, _194); const old3: Uint8Array = old7; const pointer5 = rs.byteSliceFrom(c2.v); rs.byteSliceSet(pointer5, ((_23: Uint8Array): Uint8Array => { @@ -3624,11 +3637,11 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { rs.iterExtendBytes(out, rs.iterSlice(rs.u16ToBeBytes(toWide.data[toWide.start + 1]).reverse()), undefined, true); rs.byteVecPush(out, count.strong & 0xff); } - const _233: rs.u8 = copy$tuple2_u8_u16(pair.v)[0]; - const _236: rs.u8 = copy$pointers_Duo(duo.v)._1; - const _240: Held.Held = owner.v; - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_233, _236, _240.n, copy$array_u8(bytes.v)[1])), undefined, true); - _240[Symbol.dispose](); + const _227: rs.u8 = copy$tuple2_u8_u16(pair.v)[0]; + const _230: rs.u8 = copy$pointers_Duo(duo.v)._1; + const _234: Held.Held = owner.v; + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_227, _230, _234.n, copy$array_u8(bytes.v)[1])), undefined, true); + _234[Symbol.dispose](); rs.byteVecPush(out, count.strong & 0xff); count[Symbol.dispose](); } diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index 79e7dc8bd..1705c9f96 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2542,8 +2542,8 @@ struct Slots { /// `MaybeUninit::as_ptr`; cells `UnsafeCell::from_mut` makes of a variable, an object, /// an element, and an array, written through `get`; `UnsafeCell::replace` of each, which a /// pointer taken before sees, giving back what it held undropped; what `as_ref_unchecked` -/// and `as_mut_unchecked` lend; and the allocations of boxes and pinned boxes replaced and swapped through pointers, `UnsafeCell`s, one -/// `from_mut` makes, and `Cell`s. +/// and `as_mut_unchecked` lend; and the allocations of boxes and pinned boxes replaced and +/// swapped through pointers, `UnsafeCell`s, one `from_mut` makes, and `Cell`s. pub fn raw_cells(data: &[u8], out: &mut Vec) { let count = Rc::new(()); let num = UnsafeCell::new(7u32); @@ -2721,10 +2721,34 @@ pub fn raw_cells(data: &[u8], out: &mut Vec) { out.extend(elems.iter().flat_map(|e| e.to_le_bytes())); } +thread_local! { + /// The cell `Reentrant`'s `default` and `eq` write. + static REENTERED: Cell = const { Cell::new(Reentrant(1, 1)) }; +} + +/// A struct Rust copies whose `default` and `eq` write the cell holding it. +#[derive(Clone, Copy)] +struct Reentrant(u8, u8); + +impl Default for Reentrant { + fn default() -> Self { + REENTERED.with(|c| c.set(Reentrant(7, 7))); + Reentrant(0, 0) + } +} + +impl PartialEq for Reentrant { + fn eq(&self, other: &Self) -> bool { + REENTERED.with(|c| c.set(Reentrant(9, 9))); + self.0 == other.0 + } +} + /// What `Cell`s of a tuple, a struct Rust copies, one that owns a count, and arrays of bytes /// and of numbers, and cells `Cell::from_mut` makes of a tuple and an array, hold after -/// `replace`, `set`, `swap`, `take`, and `update`, each read back through a pointer -/// `as_ptr` gave before, and what each gives back and drops. +/// `replace`, `set`, `swap` (but of the arrays), `take`, and `update`, each read back +/// through a pointer `as_ptr` gave before, and what each gives back and drops; and `take` +/// and `==` of a cell its value's `default` and `eq` write. pub fn cell_writes(data: &[u8], out: &mut Vec) { let count = Rc::new(()); let held = |n: u8| Held { @@ -2764,8 +2788,6 @@ pub fn cell_writes(data: &[u8], out: &mut Vec) { let other = Cell::new(held(n ^ 5)); owner.swap(&other); out.push(other.into_inner().n); - bytes.swap(&bytes); - wide.swap(&Cell::new([u16::from(n), 9])); } 3 => { out.push(pair.take().0); @@ -2778,6 +2800,14 @@ pub fn cell_writes(data: &[u8], out: &mut Vec) { bytes.update(|[a, b]| [b, a.wrapping_mul(n)]); wide.update(|[a, b]| [b.rotate_left(u32::from(n)), a]); } + 6 => REENTERED.with(|c| { + c.set(Reentrant(n, 1)); + let taken = c.take(); + out.extend([taken.0, c.get().0]); + c.set(Reentrant(n, 1)); + let same = *c == Cell::new(Reentrant(n, 2)); + out.extend([u8::from(same), c.get().0]); + }), 5 => unsafe { let c = Cell::from_mut(&mut lent); let p = c.as_ptr(); diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index 5bf7ff024..3efd454d2 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -11,7 +11,8 @@ fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:cell_of_arrays+1:10: `UnsafeCell::from_mut` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it reads a copy of, so a write through its pointer would land in the copy -fixtures/reject/src/alloc.rs:cell_of_arrays+7:4: `Cell::swap` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it may read a copy of +fixtures/reject/src/alloc.rs:cell_of_arrays+7:4: `Cell::swap` of `[[u16; 2]; 2]`, an array, whose cells may partially overlap, which Rust panics on +fixtures/reject/src/alloc.rs:cell_of_arrays+9:4: `Cell::swap` of `[u32; 2]`, an array, whose cells may partially overlap, which Rust panics on fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index c3ddc68f4..881e38072 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -15,7 +15,8 @@ fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+7:15: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:cell_of_arrays+1:10: `UnsafeCell::from_mut` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it reads a copy of, so a write through its pointer would land in the copy -fixtures/reject/src/alloc.rs:cell_of_arrays+7:4: `Cell::swap` of `[[u16; 2]; 2]`, an array of arrays, which a cell of it may read a copy of +fixtures/reject/src/alloc.rs:cell_of_arrays+7:4: `Cell::swap` of `[[u16; 2]; 2]`, an array, whose cells may partially overlap, which Rust panics on +fixtures/reject/src/alloc.rs:cell_of_arrays+9:4: `Cell::swap` of `[u32; 2]`, an array, whose cells may partially overlap, which Rust panics on fixtures/reject/src/alloc.rs:cell_of_objects+1:10: `UnsafeCell::from_mut` of `std::vec::Vec`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `std::string::String`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index acf20fd1f..3be3c0a57 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -113,8 +113,8 @@ pub fn uninit_cell_lent(n: u32) -> u32 { unsafe { *slot.assume_init_mut().get_mut() + 1 } } -/// An `UnsafeCell::from_mut` and a `Cell::swap` of arrays of arrays, which a cell may read -/// a copy of. +/// An `UnsafeCell::from_mut` of arrays of arrays, which a cell may read a copy of, and +/// `Cell::swap`s of arrays, whose cells may partially overlap. pub fn cell_of_arrays(a: &mut [[u16; 2]; 2]) -> u16 { let c = std::cell::UnsafeCell::from_mut(a); let first = unsafe { (*c.get())[0][0] }; @@ -123,7 +123,9 @@ pub fn cell_of_arrays(a: &mut [[u16; 2]; 2]) -> u16 { std::cell::Cell::new([[3u16, 4]; 2]), ); x.swap(&y); - first ^ x.into_inner()[0][0] + let (v, w) = (std::cell::Cell::new([5u32, 6]), std::cell::Cell::new([7u32, 8])); + v.swap(&w); + first ^ x.into_inner()[0][0] ^ v.into_inner()[0] as u16 } /// A `get_mut` and an `as_mut_unchecked` of a cell of a pinned `Box`, whose old value diff --git a/ts/runtime/rs/task.ts b/ts/runtime/rs/task.ts index c5911b573..019616f4e 100644 --- a/ts/runtime/rs/task.ts +++ b/ts/runtime/rs/task.ts @@ -931,10 +931,10 @@ export function cellIn(p: T): Cell { /** * `PartialEq`, `PartialOrd`, or `Ord` of `Cell`s or places, or `PartialEq` of `OnceCell`s - * or `OnceLock`s, by `f` of what each holds. + * or `OnceLock`s, by `f` of what each holds, or of its `copy`, as a `Cell`'s `get` gives. */ -export function cellBy(f: (a: T, b: T) => R): (a: { v: T }, b: { v: T }) => R { - return (a, b) => f(a.v, b.v); +export function cellBy(f: (a: T, b: T) => R, copy?: (v: T) => T): (a: { v: T }, b: { v: T }) => R { + return copy ? (a, b) => f(copy(a.v), copy(b.v)) : (a, b) => f(a.v, b.v); } /** `Cell::replace`: the value it held, `v` in its place. */ diff --git a/ts/src/lower/collections.rs b/ts/src/lower/collections.rs index 5a1b759e0..ad10ad4ea 100644 --- a/ts/src/lower/collections.rs +++ b/ts/src/lower/collections.rs @@ -1235,7 +1235,7 @@ pub fn test_fn<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, test: &str, span: } ty::Adt(..) if let Some((inner, by)) = cell_of(b, ty) => { let f = test_fn(b, inner, test, span); - cell_by(b, by, f, test) + cell_by(b, ty, by, f, test, span) } _ => { let f = trait_fn(tcx, "PartialOrd", test); @@ -3047,11 +3047,18 @@ fn cell_of<'tcx>(b: &Body<'_, '_, 'tcx>, ty: Ty<'tcx>) -> Option<(Ty<'tcx>, &'st /// The runtime's `by` of `f`, as [`cell_of`] says a cell compares, for its `method`: given /// where a `RefCell`'s borrows, in a program with a panic hook. -fn cell_by(b: &Body<'_, '_, '_>, by: &str, f: Expr, method: &str) -> Expr { +/// A `Cell`'s values are compared as copies, as its `get` gives them, since a comparison +/// may write the cell, and a write lands in an object it holds. +fn cell_by<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, by: &str, f: Expr, method: &str, span: Span) -> Expr { let mut values = vec![f]; if by == "refCellBy" && b.t.hooked { values.extend(super::caller::cell_sites(b.t.tcx, method)); } + if let ty::Adt(_, args) = ty.kind() + && b.t.std_of(ty) == Some(Std::Cell) + { + values.extend(b.t.copy_fn(args.type_at(0), span)); + } Expr::rs(by, values) } @@ -3091,7 +3098,7 @@ pub fn cmp_fn<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, partial: bool, spa ty::Adt(..) if let Some(inner) = arc_of(b, ty) => Expr::rs("cmpArc", vec![cmp_fn(b, inner, partial, span)]), ty::Adt(..) if let Some((inner, by)) = cell_of(b, ty) => { let cmp = cmp_fn(b, inner, partial, span); - cell_by(b, by, cmp, if partial { "partial_cmp" } else { "cmp" }) + cell_by(b, ty, by, cmp, if partial { "partial_cmp" } else { "cmp" }, span) } // An `rs.Place` compares by its value, as std's impl on `&mut` does by what it points at. ty::Ref(..) if let Some(inner) = b.t.place_pointee(ty) => { @@ -3189,7 +3196,7 @@ pub fn eq_fn<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, span: Span) -> Expr // Whose `ne` is `!eq`, which compares what it holds by `eq`. ty::Adt(..) if let Some((inner, by)) = cell_of(b, ty) => { let eq = eq_fn(b, inner, span); - cell_by(b, by, eq, "eq") + cell_by(b, ty, by, eq, "eq", span) } ty::Ref(..) if let Some(inner) = b.t.place_pointee(ty) => Expr::rs("cellBy", vec![eq_fn(b, inner, span)]), _ if by_own(b, ty) || derived_std(b, ty) || b.t.std_of(ty).is_some_and(Std::record) => { diff --git a/ts/src/lower/copy.rs b/ts/src/lower/copy.rs index 444a619ba..05253cecd 100644 --- a/ts/src/lower/copy.rs +++ b/ts/src/lower/copy.rs @@ -685,10 +685,13 @@ fn replaces<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, this: Ty<'tcx>) -> Option(tcx: TyCtxt<'tcx>, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> Option> { let (owner, name) = super::library::key(tcx, def_id)?; let writes = match owner.as_str() { + // Refused of an array. + "Cell" if name == "swap" && args.type_at(0).is_array() => false, "Cell" if super::array_slice::of_arrays(args.type_at(0)) => false, "Cell" => matches!(name.as_str(), "replace" | "set" | "swap" | "take" | "update"), "UnsafeCell" => name == "replace", diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 80e11c6b4..ac23a39ef 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -1504,6 +1504,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { ) -> Option>> { use rustc_middle::mir::{Rvalue, StatementKind, TerminatorKind}; let tcx = self.tcx; + // A cycle, whose places the path to it already has. if seen.contains(&local) { return Some(Vec::new()); } @@ -1566,6 +1567,8 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { }; origins.extend(self.place_origins(*made, seen)?); } + // Off the path, as another branch may reach it again. + seen.pop(); (found == self.defs[local]).then_some(origins) } diff --git a/ts/src/lower/task.rs b/ts/src/lower/task.rs index 2c1bfa120..31b5e03e1 100644 --- a/ts/src/lower/task.rs +++ b/ts/src/lower/task.rs @@ -624,8 +624,12 @@ fn cell<'tcx>( } "replace" | "take" if in_place => { let [cell, value] = two(args); + // Made before the value it replaces is read, as `Default` may write the cell. let value = match name { - "take" => default(b, out), + "take" => { + let value = default(b, out); + b.spill(value, "value", out) + } _ => value, }; let p = pointer::cell_pointer(b, cell, ty); @@ -637,9 +641,10 @@ fn cell<'tcx>( let new = f.call(vec![pointer::read(b, p.clone(), ty, span, out)]); pointer::write(b, p, new, ty, span, out) } - "swap" if arrays => b.err( + // Rust panics on cells that partially overlap, which only arrays' can. + "swap" if b.t.pointee(ty) == Pointee::Array => b.err( span, - format!("`Cell::swap` of `{ty}`, an array of arrays, which a cell of it may read a copy of"), + format!("`Cell::swap` of `{ty}`, an array, whose cells may partially overlap, which Rust panics on"), ), "swap" => { let [x, y] = two(args); From 5c82333cd9fdbed3d27f13519845e7e03f4f24ef Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 12:03:16 -0800 Subject: [PATCH 16/17] fix(ts): keep nested cells through a MaybeUninit or ManuallyDrop kept_cells looks through the MaybeUninits and ManuallyDrops JS keeps as what they hold, so a whole write of one holding a cell writes into the cell in place, and a pointer saved into it sees the write. Co-Authored-By: Claude Opus 5.5 --- .../lang/pointers/expected/pointers.ts | 184 ++++++++++-------- ts/fixtures/lang/pointers/src/pointers.rs | 14 +- ts/src/lower/pointer.rs | 4 +- 3 files changed, 122 insertions(+), 80 deletions(-) diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 7413590fa..75234b70f 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -3647,8 +3647,9 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { } /** - * Whole writes of values holding cells into a cell, a tuple, and a struct, each read back - * through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, + * Whole writes of values holding cells into a cell, a tuple, a struct, a `MaybeUninit`, and + * a `ManuallyDrop`, each read back through a pointer saved into an inner cell before: + * `UnsafeCell::replace`, `ptr::replace`, * `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a * value `ptr::read` copies out before one, and before a write into a part holding no * cell; `ptr::write`s into memory not written yet; and pointers `UnsafeCell::raw_get` @@ -3662,7 +3663,11 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const obj: rs.Cell> = new rs.Cell(new rs.Cell([7, 8] as [rs.u8, rs.u8])); const cell: rs.Cell<[rs.Cell, rs.u8]> = new rs.Cell([new rs.Cell(9), 10] as [rs.Cell, rs.u8]); const wrapped: rs.Cell> = new rs.Cell(new rs.Cell(11)); + const uninit: rs.Cell> = new rs.Cell(new rs.Cell(12)); + const manual: rs.Cell> = new rs.Cell(new rs.Cell(13)); const toWrapped: rs.Place = wrapped.v; + const toUninit: rs.Place = uninit.v; + const toManual: rs.Place = manual.v; const toNest: rs.Place = nest.v; const toDeep: rs.Place = deep.v.v; const toPair: rs.Place = pair.v[0]; @@ -3671,40 +3676,40 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const toCell: rs.Place = cell.v[0]; for (const op of rs.byteSliceView(data)) { const n: rs.u8 = rs.u8Div(op, 8); - const _60: rs.u8 = rs.u8Rem(op, 8); - if (_60 === 0) { - const _64: rs.Cell = new rs.Cell(n as rs.u32); + const _76: rs.u8 = rs.u8Rem(op, 8); + if (_76 === 0) { + const _80: rs.Cell = new rs.Cell(n as rs.u32); const pointer16 = nest; - const old16 = new rs.Cell(pointer16.v.v); + const old18 = new rs.Cell(pointer16.v.v); const pointer17 = pointer16; - const value25 = _64; + const value25 = _80; if (pointer17.v === undefined) { pointer17.v = value25; } else { pointer17.v.v = value25.v; } undefined; - rs.byteVecPush(out, old16.v & 0xff); - const _71: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); + rs.byteVecPush(out, old18.v & 0xff); + const _87: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); const pointer18 = deep; - const old17 = new rs.Cell(new rs.Cell(pointer18.v.v.v)); + const old19 = new rs.Cell(new rs.Cell(pointer18.v.v.v)); const pointer19 = pointer18; - const value26 = _71; + const value26 = _87; if (pointer19.v === undefined) { pointer19.v = value26; } else { pointer19.v.v.v = value26.v.v; } undefined; - rs.byteVecPush(out, old17.v.v & 0xff); - const _82: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; + rs.byteVecPush(out, old19.v.v & 0xff); + const _98: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; const pointer20 = pair.v; const object13 = pointer20; const copy7 = rs.objectCopy(object13); copy7[0] = new rs.Cell(object13[0].v); - const old18 = copy7; + const old20 = copy7; const object14 = pointer20; - const value27 = _82; + const value27 = _98; const cell9 = object14[0]; rs.ptrWrite(object14, value27); if (cell9 !== undefined) { @@ -3712,16 +3717,16 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell9.v = value27[0].v; } undefined; - const old3: [rs.Cell, rs.u8] = old18; + const old3: [rs.Cell, rs.u8] = old20; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old3[0].v & 0xff, old3[1])), undefined, true); - const _95: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; + const _111: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; const pointer21 = strukt.v; const object15 = pointer21; const copy8 = rs.objectCopy(object15); copy8.c = new rs.Cell(object15.c.v); - const old19 = copy8; + const old21 = copy8; const object16 = pointer21; - const value28 = _95; + const value28 = _111; const cell10 = object16.c; rs.ptrWrite(object16, value28); if (cell10 !== undefined) { @@ -3729,43 +3734,67 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell10.v = value28.c.v; } undefined; - const old4: Celled.Celled = old19; + const old4: Celled.Celled = old21; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old4.c.v & 0xff, old4.n)), undefined, true); - const _108: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); + const _124: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); const pointer22 = obj; - const old20 = new rs.Cell(copy$tuple2_u8_u8(pointer22.v.v)); + const old22 = new rs.Cell(copy$tuple2_u8_u8(pointer22.v.v)); const pointer23 = pointer22; - const value29 = _108; + const value29 = _124; if (pointer23.v === undefined) { pointer23.v = value29; } else { rs.ptrWrite(pointer23.v.v, value29.v); } undefined; - rs.byteVecPush(out, old20.v[1]); - } else if (_60 === 1) { - const _115: rs.Place> = nest; - const _117: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); - const pointer13 = _115; - const old14 = new rs.Cell(pointer13.v.v); + rs.byteVecPush(out, old22.v[1]); + const _132: rs.Cell = new rs.Cell(((n as rs.u32) ^ 20) >>> 0); + const pointer24 = uninit; + const old23 = new rs.Cell(pointer24.v.v); + const pointer25 = pointer24; + const value30 = _132; + if (pointer25.v === undefined) { + pointer25.v = value30; + } else { + pointer25.v.v = value30.v; + } + undefined; + rs.byteVecPush(out, old23.v & 0xff); + const _142: rs.Cell = new rs.Cell(((n as rs.u32) ^ 21) >>> 0); + const pointer26 = manual; + const old24 = new rs.Cell(pointer26.v.v); + const pointer27 = pointer26; + const value31 = _142; + if (pointer27.v === undefined) { + pointer27.v = value31; + } else { + pointer27.v.v = value31.v; + } + undefined; + rs.byteVecPush(out, old24.v & 0xff); + } else if (_76 === 1) { + const _151: rs.Place> = nest; + const _153: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); + const pointer13 = _151; + const old16 = new rs.Cell(pointer13.v.v); const pointer14 = pointer13; - const value23 = _117; + const value23 = _153; if (pointer14.v === undefined) { pointer14.v = value23; } else { pointer14.v.v = value23.v; } undefined; - rs.byteVecPush(out, old14.v & 0xff); - const _124: [rs.Cell, rs.u8] = pair.v; - const _126: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; - const pointer15 = _124; + rs.byteVecPush(out, old16.v & 0xff); + const _160: [rs.Cell, rs.u8] = pair.v; + const _162: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; + const pointer15 = _160; const object11 = pointer15; const copy6 = rs.objectCopy(object11); copy6[0] = new rs.Cell(object11[0].v); - const old15 = copy6; + const old17 = copy6; const object12 = pointer15; - const value24 = _126; + const value24 = _162; const cell8 = object12[0]; rs.ptrWrite(object12, value24); if (cell8 !== undefined) { @@ -3773,8 +3802,8 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell8.v = value24[0].v; } undefined; - rs.byteVecPush(out, old15[0].v & 0xff); - } else if (_60 === 2) { + rs.byteVecPush(out, old17[0].v & 0xff); + } else if (_76 === 2) { const pointer12 = nest; const value21 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); if (pointer12.v === undefined) { @@ -3792,11 +3821,11 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell7.v = value22.c.v; } undefined; - } else if (_60 === 3) { + } else if (_76 === 3) { const other: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 5) >>> 0)); const x = nest; const y = other; - const old12 = new rs.Cell(x.v.v); + const old14 = new rs.Cell(x.v.v); const pointer10 = x; const value17 = new rs.Cell(y.v.v); if (pointer10.v === undefined) { @@ -3806,7 +3835,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; const pointer11 = y; - const value18 = old12; + const value18 = old14; if (pointer11.v === undefined) { pointer11.v = value18; } else { @@ -3820,7 +3849,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const object6 = x2; const copy4 = rs.objectCopy(object6); copy4.c = new rs.Cell(object6.c.v); - const old13 = copy4; + const old15 = copy4; const object7 = y2; const copy5 = rs.objectCopy(object7); copy5.c = new rs.Cell(object7.c.v); @@ -3834,7 +3863,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; const object9 = y2; - const value20 = old13; + const value20 = old15; const cell6 = object9.c; rs.ptrWrite(object9, value20); if (cell6 !== undefined) { @@ -3843,15 +3872,15 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(other2.c.v & 0xff, other2.n)), undefined, true); - } else if (_60 === 4) { - const _178: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; + } else if (_76 === 4) { + const _214: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; const pointer9 = cell.v; const object3 = pointer9; const copy3 = rs.objectCopy(object3); copy3[0] = new rs.Cell(object3[0].v); - const old11 = copy3; + const old13 = copy3; const object4 = pointer9; - const value15 = _178; + const value15 = _214; const cell3 = object4[0]; rs.ptrWrite(object4, value15); if (cell3 !== undefined) { @@ -3859,10 +3888,10 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell3.v = value15[0].v; } undefined; - rs.byteVecPush(out, old11[0].v & 0xff); - const _188: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; + rs.byteVecPush(out, old13[0].v & 0xff); + const _224: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; const object5 = cell.v; - const value16 = _188; + const value16 = _224; const cell4 = object5[0]; rs.ptrWrite(object5, value16); if (cell4 !== undefined) { @@ -3870,7 +3899,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell4.v = value16[0].v; } undefined; - } else if (_60 === 5) { + } else if (_76 === 5) { toNest.v = rs.u32Mul(n as rs.u32, 3); toPair.v = rs.u32Mul(n as rs.u32, 5); const kept: rs.Cell = new rs.Cell(nest.v.v); @@ -3883,22 +3912,22 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; rs.byteVecPush(out, kept.v & 0xff); - } else if (_60 === 6) { - const _212: rs.Cell = new rs.Cell(((n as rs.u32) ^ 8) >>> 0); + } else if (_76 === 6) { + const _248: rs.Cell = new rs.Cell(((n as rs.u32) ^ 8) >>> 0); const pointer = wrapped; - const old10 = new rs.Cell(pointer.v.v); + const old12 = new rs.Cell(pointer.v.v); const pointer2 = pointer; - const value3 = _212; + const value3 = _248; if (pointer2.v === undefined) { pointer2.v = value3; } else { pointer2.v.v = value3.v; } undefined; - rs.byteVecPush(out, old10.v & 0xff); - const _220: rs.Cell = new rs.Cell(((n as rs.u32) ^ 9) >>> 0); + rs.byteVecPush(out, old12.v & 0xff); + const _256: rs.Cell = new rs.Cell(((n as rs.u32) ^ 9) >>> 0); const pointer3 = wrapped; - const value4 = _220; + const value4 = _256; if (pointer3.v === undefined) { pointer3.v = value4; } else { @@ -3929,9 +3958,9 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { let pairSlot: rs.Cell<[rs.u8, rs.u8]> = (new rs.Cell([undefined, undefined]) as unknown) as rs.Cell<[rs.u8, rs.u8]>; const toSlot: rs.Place> = rs.place(() => slot, (value8) => slot = value8); const toPairSlot: rs.Place> = rs.place(() => pairSlot, (value9) => pairSlot = value9); - const _259: [rs.Place, [rs.u8, rs.u8]] = [toSlot.v, toPairSlot.v.v]; - const early: rs.Place = _259[0]; - const earlyPair: [rs.u8, rs.u8] = _259[1]; + const _295: [rs.Place, [rs.u8, rs.u8]] = [toSlot.v, toPairSlot.v.v]; + const early: rs.Place = _295[0]; + const earlyPair: [rs.u8, rs.u8] = _295[1]; const pointer5 = toSlot; const value10 = new rs.Cell(((n as rs.u32) ^ 17) >>> 0); if (pointer5.v === undefined) { @@ -3949,9 +3978,9 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(early.v & 0xff, earlyPair[1])), undefined, true); - let uninit: rs.Cell = new rs.Cell(n as rs.u32); - const readOut: rs.Cell = new rs.Cell(uninit.v); - const pointer7 = rs.place(() => uninit, (value12) => uninit = value12); + let uninit2: rs.Cell = new rs.Cell(n as rs.u32); + const readOut: rs.Cell = new rs.Cell(uninit2.v); + const pointer7 = rs.place(() => uninit2, (value12) => uninit2 = value12); const value13 = new rs.Cell(((n as rs.u32) ^ 19) >>> 0); if (pointer7.v === undefined) { pointer7.v = value13; @@ -3959,7 +3988,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { pointer7.v.v = value13.v; } undefined; - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(readOut.v & 0xff, uninit.v & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(readOut.v & 0xff, uninit2.v & 0xff)), undefined, true); const src: rs.Cell<[rs.Cell, [rs.u32, rs.u32]]> = new rs.Cell([new rs.Cell(n as rs.u32), [2, 3] as [rs.u32, rs.u32]] as [rs.Cell, [rs.u32, rs.u32]]); const object2 = src.v; const copy2 = rs.objectCopy(object2); @@ -3969,25 +3998,26 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { src.v[1][0] = 9; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy[1][0] & 0xff, src.v[1][0] & 0xff)), undefined, true); } else { - const _316: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); - const _319: rs.Place> = nest; - const value = _316; - if (_319.v === undefined) { - _319.v = value; + const _352: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); + const _355: rs.Place> = nest; + const value = _352; + if (_355.v === undefined) { + _355.v = value; } else { - _319.v.v = value.v; + _355.v.v = value.v; } - const _321: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); - const _325: rs.Place>> = deep; - const value2 = _321; - if (_325.v === undefined) { - _325.v = value2; + const _357: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); + const _361: rs.Place>> = deep; + const value2 = _357; + if (_361.v === undefined) { + _361.v = value2; } else { - _325.v.v.v = value2.v.v; + _361.v.v.v = value2.v.v; } } rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toNest.v & 0xff, toDeep.v & 0xff, toPair.v & 0xff, toStrukt.v & 0xff)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toObj[0], toObj[1], toCell.v & 0xff, toWrapped.v & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toUninit.v & 0xff, toManual.v & 0xff)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(nest.v.v & 0xff, pair.v[1], strukt.v.n)), undefined, true); } } diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index 1705c9f96..c69852e44 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2844,8 +2844,9 @@ struct Celled { n: u8, } -/// Whole writes of values holding cells into a cell, a tuple, and a struct, each read back -/// through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, +/// Whole writes of values holding cells into a cell, a tuple, a struct, a `MaybeUninit`, and +/// a `ManuallyDrop`, each read back through a pointer saved into an inner cell before: +/// `UnsafeCell::replace`, `ptr::replace`, /// `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a /// value `ptr::read` copies out before one, and before a write into a part holding no /// cell; `ptr::write`s into memory not written yet; and pointers `UnsafeCell::raw_get` @@ -2861,8 +2862,12 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { let obj = UnsafeCell::new(UnsafeCell::new((7u8, 8u8))); let cell = Cell::new((UnsafeCell::new(9u32), 10u8)); let wrapped = Cell::new(UnsafeCell::new(11u32)); + let uninit = UnsafeCell::new(MaybeUninit::new(UnsafeCell::new(12u32))); + let manual = UnsafeCell::new(mem::ManuallyDrop::new(UnsafeCell::new(13u32))); unsafe { let to_wrapped = (*wrapped.as_ptr()).get(); + let to_uninit = (*uninit.get()).assume_init_ref().get(); + let to_manual = (*manual.get()).get(); let to_nest = (*nest.get()).get(); let to_deep = (*(*deep.get()).get()).get(); let to_pair = (*pair.get()).0.get(); @@ -2886,6 +2891,10 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { out.extend([old.c.into_inner() as u8, old.n]); let old = obj.replace(UnsafeCell::new((n, n ^ 1))); out.push(old.into_inner().1); + let old = uninit.replace(MaybeUninit::new(UnsafeCell::new(u32::from(n) ^ 20))); + out.push(old.assume_init().into_inner() as u8); + let old = manual.replace(mem::ManuallyDrop::new(UnsafeCell::new(u32::from(n) ^ 21))); + out.push(mem::ManuallyDrop::into_inner(old).into_inner() as u8); } 1 => { let old = ptr::replace(nest.get(), UnsafeCell::new(u32::from(n) ^ 1)); @@ -2962,6 +2971,7 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { } out.extend([*to_nest as u8, *to_deep as u8, *to_pair as u8, *to_strukt as u8]); out.extend([(*to_obj).0, (*to_obj).1, *to_cell as u8, *to_wrapped as u8]); + out.extend([*to_uninit as u8, *to_manual as u8]); out.extend([(*nest.get()).get().read() as u8, (*pair.get()).1, (*strukt.get()).n]); } } diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index 7ae5e504f..39bf6149d 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -173,8 +173,10 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { } /// What a `ty` that [keeps cells](Self::keeps_cells) holds: a cell's value, or each - /// field of a tuple or struct, and whether it keeps cells too. + /// field of a tuple or struct, and whether it keeps cells too. A `MaybeUninit` or a + /// `ManuallyDrop` holds what JS keeps it as. pub fn kept_cells(&self, ty: Ty<'tcx>) -> Option> { + let ty = self.uninit_inner(ty); let fields: Vec> = match ty.kind() { ty::Adt(adt, args) if Std::of(self.tcx, adt.did()) == Some(Std::Cell) => { return Some(KeptCells::Cell(args.type_at(0))); From 3cb74e2284204c8e6b8b2a917614127b3c1dd5cb Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sat, 7 Nov 2026 12:17:59 -0800 Subject: [PATCH 17/17] fix(ts): refuse whole writes of a MaybeUninit or ManuallyDrop holding cells Reverts 5c82333cd's look-through in kept_cells, which panicked projecting fields of a wrapped tuple or struct. A whole write through the pointer helpers, Cell's writes, or an assignment through a raw pointer of a value holding a cell in a MaybeUninit or ManuallyDrop is refused with a span (wraps_cells), as it would put another cell in place of one a pointer may point into. The two wrapper cases leave pointers::nested_cells for the reject case alloc::wrapped_cells. Co-Authored-By: Claude Opus 5.5 --- .../lang/pointers/expected/pointers.ts | 184 ++++++++---------- ts/fixtures/lang/pointers/src/pointers.rs | 14 +- ts/fixtures/reject/census.txt | 5 + ts/fixtures/reject/expected.txt | 5 + ts/fixtures/reject/src/alloc.rs | 21 ++ ts/fixtures/reject/ts.toml | 1 + ts/src/census.rs | 2 + ts/src/lower/mir/place.rs | 18 +- ts/src/lower/pointer.rs | 57 ++++-- ts/src/lower/task.rs | 1 + 10 files changed, 171 insertions(+), 137 deletions(-) diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 75234b70f..7413590fa 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -3647,9 +3647,8 @@ export function cellWrites(data: rs.ByteSlice, out: rs.ByteVec): void { } /** - * Whole writes of values holding cells into a cell, a tuple, a struct, a `MaybeUninit`, and - * a `ManuallyDrop`, each read back through a pointer saved into an inner cell before: - * `UnsafeCell::replace`, `ptr::replace`, + * Whole writes of values holding cells into a cell, a tuple, and a struct, each read back + * through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, * `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a * value `ptr::read` copies out before one, and before a write into a part holding no * cell; `ptr::write`s into memory not written yet; and pointers `UnsafeCell::raw_get` @@ -3663,11 +3662,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const obj: rs.Cell> = new rs.Cell(new rs.Cell([7, 8] as [rs.u8, rs.u8])); const cell: rs.Cell<[rs.Cell, rs.u8]> = new rs.Cell([new rs.Cell(9), 10] as [rs.Cell, rs.u8]); const wrapped: rs.Cell> = new rs.Cell(new rs.Cell(11)); - const uninit: rs.Cell> = new rs.Cell(new rs.Cell(12)); - const manual: rs.Cell> = new rs.Cell(new rs.Cell(13)); const toWrapped: rs.Place = wrapped.v; - const toUninit: rs.Place = uninit.v; - const toManual: rs.Place = manual.v; const toNest: rs.Place = nest.v; const toDeep: rs.Place = deep.v.v; const toPair: rs.Place = pair.v[0]; @@ -3676,40 +3671,40 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const toCell: rs.Place = cell.v[0]; for (const op of rs.byteSliceView(data)) { const n: rs.u8 = rs.u8Div(op, 8); - const _76: rs.u8 = rs.u8Rem(op, 8); - if (_76 === 0) { - const _80: rs.Cell = new rs.Cell(n as rs.u32); + const _60: rs.u8 = rs.u8Rem(op, 8); + if (_60 === 0) { + const _64: rs.Cell = new rs.Cell(n as rs.u32); const pointer16 = nest; - const old18 = new rs.Cell(pointer16.v.v); + const old16 = new rs.Cell(pointer16.v.v); const pointer17 = pointer16; - const value25 = _80; + const value25 = _64; if (pointer17.v === undefined) { pointer17.v = value25; } else { pointer17.v.v = value25.v; } undefined; - rs.byteVecPush(out, old18.v & 0xff); - const _87: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); + rs.byteVecPush(out, old16.v & 0xff); + const _71: rs.Cell> = new rs.Cell(new rs.Cell(rs.u32Add(n as rs.u32, 1))); const pointer18 = deep; - const old19 = new rs.Cell(new rs.Cell(pointer18.v.v.v)); + const old17 = new rs.Cell(new rs.Cell(pointer18.v.v.v)); const pointer19 = pointer18; - const value26 = _87; + const value26 = _71; if (pointer19.v === undefined) { pointer19.v = value26; } else { pointer19.v.v.v = value26.v.v; } undefined; - rs.byteVecPush(out, old19.v.v & 0xff); - const _98: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; + rs.byteVecPush(out, old17.v.v & 0xff); + const _82: [rs.Cell, rs.u8] = [new rs.Cell(rs.u32Add(n as rs.u32, 2)), n]; const pointer20 = pair.v; const object13 = pointer20; const copy7 = rs.objectCopy(object13); copy7[0] = new rs.Cell(object13[0].v); - const old20 = copy7; + const old18 = copy7; const object14 = pointer20; - const value27 = _98; + const value27 = _82; const cell9 = object14[0]; rs.ptrWrite(object14, value27); if (cell9 !== undefined) { @@ -3717,16 +3712,16 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell9.v = value27[0].v; } undefined; - const old3: [rs.Cell, rs.u8] = old20; + const old3: [rs.Cell, rs.u8] = old18; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old3[0].v & 0xff, old3[1])), undefined, true); - const _111: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; + const _95: Celled.Celled = { c: new rs.Cell(rs.u32Add(n as rs.u32, 3)), n }; const pointer21 = strukt.v; const object15 = pointer21; const copy8 = rs.objectCopy(object15); copy8.c = new rs.Cell(object15.c.v); - const old21 = copy8; + const old19 = copy8; const object16 = pointer21; - const value28 = _111; + const value28 = _95; const cell10 = object16.c; rs.ptrWrite(object16, value28); if (cell10 !== undefined) { @@ -3734,67 +3729,43 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell10.v = value28.c.v; } undefined; - const old4: Celled.Celled = old21; + const old4: Celled.Celled = old19; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old4.c.v & 0xff, old4.n)), undefined, true); - const _124: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); + const _108: rs.Cell<[rs.u8, rs.u8]> = new rs.Cell([n, n ^ 1] as [rs.u8, rs.u8]); const pointer22 = obj; - const old22 = new rs.Cell(copy$tuple2_u8_u8(pointer22.v.v)); + const old20 = new rs.Cell(copy$tuple2_u8_u8(pointer22.v.v)); const pointer23 = pointer22; - const value29 = _124; + const value29 = _108; if (pointer23.v === undefined) { pointer23.v = value29; } else { rs.ptrWrite(pointer23.v.v, value29.v); } undefined; - rs.byteVecPush(out, old22.v[1]); - const _132: rs.Cell = new rs.Cell(((n as rs.u32) ^ 20) >>> 0); - const pointer24 = uninit; - const old23 = new rs.Cell(pointer24.v.v); - const pointer25 = pointer24; - const value30 = _132; - if (pointer25.v === undefined) { - pointer25.v = value30; - } else { - pointer25.v.v = value30.v; - } - undefined; - rs.byteVecPush(out, old23.v & 0xff); - const _142: rs.Cell = new rs.Cell(((n as rs.u32) ^ 21) >>> 0); - const pointer26 = manual; - const old24 = new rs.Cell(pointer26.v.v); - const pointer27 = pointer26; - const value31 = _142; - if (pointer27.v === undefined) { - pointer27.v = value31; - } else { - pointer27.v.v = value31.v; - } - undefined; - rs.byteVecPush(out, old24.v & 0xff); - } else if (_76 === 1) { - const _151: rs.Place> = nest; - const _153: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); - const pointer13 = _151; - const old16 = new rs.Cell(pointer13.v.v); + rs.byteVecPush(out, old20.v[1]); + } else if (_60 === 1) { + const _115: rs.Place> = nest; + const _117: rs.Cell = new rs.Cell(((n as rs.u32) ^ 1) >>> 0); + const pointer13 = _115; + const old14 = new rs.Cell(pointer13.v.v); const pointer14 = pointer13; - const value23 = _153; + const value23 = _117; if (pointer14.v === undefined) { pointer14.v = value23; } else { pointer14.v.v = value23.v; } undefined; - rs.byteVecPush(out, old16.v & 0xff); - const _160: [rs.Cell, rs.u8] = pair.v; - const _162: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; - const pointer15 = _160; + rs.byteVecPush(out, old14.v & 0xff); + const _124: [rs.Cell, rs.u8] = pair.v; + const _126: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 2) >>> 0), n]; + const pointer15 = _124; const object11 = pointer15; const copy6 = rs.objectCopy(object11); copy6[0] = new rs.Cell(object11[0].v); - const old17 = copy6; + const old15 = copy6; const object12 = pointer15; - const value24 = _162; + const value24 = _126; const cell8 = object12[0]; rs.ptrWrite(object12, value24); if (cell8 !== undefined) { @@ -3802,8 +3773,8 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell8.v = value24[0].v; } undefined; - rs.byteVecPush(out, old17[0].v & 0xff); - } else if (_76 === 2) { + rs.byteVecPush(out, old15[0].v & 0xff); + } else if (_60 === 2) { const pointer12 = nest; const value21 = new rs.Cell(((n as rs.u32) ^ 3) >>> 0); if (pointer12.v === undefined) { @@ -3821,11 +3792,11 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell7.v = value22.c.v; } undefined; - } else if (_76 === 3) { + } else if (_60 === 3) { const other: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 5) >>> 0)); const x = nest; const y = other; - const old14 = new rs.Cell(x.v.v); + const old12 = new rs.Cell(x.v.v); const pointer10 = x; const value17 = new rs.Cell(y.v.v); if (pointer10.v === undefined) { @@ -3835,7 +3806,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; const pointer11 = y; - const value18 = old14; + const value18 = old12; if (pointer11.v === undefined) { pointer11.v = value18; } else { @@ -3849,7 +3820,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { const object6 = x2; const copy4 = rs.objectCopy(object6); copy4.c = new rs.Cell(object6.c.v); - const old15 = copy4; + const old13 = copy4; const object7 = y2; const copy5 = rs.objectCopy(object7); copy5.c = new rs.Cell(object7.c.v); @@ -3863,7 +3834,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; const object9 = y2; - const value20 = old15; + const value20 = old13; const cell6 = object9.c; rs.ptrWrite(object9, value20); if (cell6 !== undefined) { @@ -3872,15 +3843,15 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(other2.c.v & 0xff, other2.n)), undefined, true); - } else if (_76 === 4) { - const _214: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; + } else if (_60 === 4) { + const _178: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 6) >>> 0), n]; const pointer9 = cell.v; const object3 = pointer9; const copy3 = rs.objectCopy(object3); copy3[0] = new rs.Cell(object3[0].v); - const old13 = copy3; + const old11 = copy3; const object4 = pointer9; - const value15 = _214; + const value15 = _178; const cell3 = object4[0]; rs.ptrWrite(object4, value15); if (cell3 !== undefined) { @@ -3888,10 +3859,10 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell3.v = value15[0].v; } undefined; - rs.byteVecPush(out, old13[0].v & 0xff); - const _224: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; + rs.byteVecPush(out, old11[0].v & 0xff); + const _188: [rs.Cell, rs.u8] = [new rs.Cell(((n as rs.u32) ^ 7) >>> 0), n]; const object5 = cell.v; - const value16 = _224; + const value16 = _188; const cell4 = object5[0]; rs.ptrWrite(object5, value16); if (cell4 !== undefined) { @@ -3899,7 +3870,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { cell4.v = value16[0].v; } undefined; - } else if (_76 === 5) { + } else if (_60 === 5) { toNest.v = rs.u32Mul(n as rs.u32, 3); toPair.v = rs.u32Mul(n as rs.u32, 5); const kept: rs.Cell = new rs.Cell(nest.v.v); @@ -3912,22 +3883,22 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; rs.byteVecPush(out, kept.v & 0xff); - } else if (_76 === 6) { - const _248: rs.Cell = new rs.Cell(((n as rs.u32) ^ 8) >>> 0); + } else if (_60 === 6) { + const _212: rs.Cell = new rs.Cell(((n as rs.u32) ^ 8) >>> 0); const pointer = wrapped; - const old12 = new rs.Cell(pointer.v.v); + const old10 = new rs.Cell(pointer.v.v); const pointer2 = pointer; - const value3 = _248; + const value3 = _212; if (pointer2.v === undefined) { pointer2.v = value3; } else { pointer2.v.v = value3.v; } undefined; - rs.byteVecPush(out, old12.v & 0xff); - const _256: rs.Cell = new rs.Cell(((n as rs.u32) ^ 9) >>> 0); + rs.byteVecPush(out, old10.v & 0xff); + const _220: rs.Cell = new rs.Cell(((n as rs.u32) ^ 9) >>> 0); const pointer3 = wrapped; - const value4 = _256; + const value4 = _220; if (pointer3.v === undefined) { pointer3.v = value4; } else { @@ -3958,9 +3929,9 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { let pairSlot: rs.Cell<[rs.u8, rs.u8]> = (new rs.Cell([undefined, undefined]) as unknown) as rs.Cell<[rs.u8, rs.u8]>; const toSlot: rs.Place> = rs.place(() => slot, (value8) => slot = value8); const toPairSlot: rs.Place> = rs.place(() => pairSlot, (value9) => pairSlot = value9); - const _295: [rs.Place, [rs.u8, rs.u8]] = [toSlot.v, toPairSlot.v.v]; - const early: rs.Place = _295[0]; - const earlyPair: [rs.u8, rs.u8] = _295[1]; + const _259: [rs.Place, [rs.u8, rs.u8]] = [toSlot.v, toPairSlot.v.v]; + const early: rs.Place = _259[0]; + const earlyPair: [rs.u8, rs.u8] = _259[1]; const pointer5 = toSlot; const value10 = new rs.Cell(((n as rs.u32) ^ 17) >>> 0); if (pointer5.v === undefined) { @@ -3978,9 +3949,9 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { } undefined; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(early.v & 0xff, earlyPair[1])), undefined, true); - let uninit2: rs.Cell = new rs.Cell(n as rs.u32); - const readOut: rs.Cell = new rs.Cell(uninit2.v); - const pointer7 = rs.place(() => uninit2, (value12) => uninit2 = value12); + let uninit: rs.Cell = new rs.Cell(n as rs.u32); + const readOut: rs.Cell = new rs.Cell(uninit.v); + const pointer7 = rs.place(() => uninit, (value12) => uninit = value12); const value13 = new rs.Cell(((n as rs.u32) ^ 19) >>> 0); if (pointer7.v === undefined) { pointer7.v = value13; @@ -3988,7 +3959,7 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { pointer7.v.v = value13.v; } undefined; - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(readOut.v & 0xff, uninit2.v & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(readOut.v & 0xff, uninit.v & 0xff)), undefined, true); const src: rs.Cell<[rs.Cell, [rs.u32, rs.u32]]> = new rs.Cell([new rs.Cell(n as rs.u32), [2, 3] as [rs.u32, rs.u32]] as [rs.Cell, [rs.u32, rs.u32]]); const object2 = src.v; const copy2 = rs.objectCopy(object2); @@ -3998,26 +3969,25 @@ export function nestedCells(data: rs.ByteSlice, out: rs.ByteVec): void { src.v[1][0] = 9; rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(copy[1][0] & 0xff, src.v[1][0] & 0xff)), undefined, true); } else { - const _352: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); - const _355: rs.Place> = nest; - const value = _352; - if (_355.v === undefined) { - _355.v = value; + const _316: rs.Cell = new rs.Cell(((n as rs.u32) ^ 13) >>> 0); + const _319: rs.Place> = nest; + const value = _316; + if (_319.v === undefined) { + _319.v = value; } else { - _355.v.v = value.v; + _319.v.v = value.v; } - const _357: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); - const _361: rs.Place>> = deep; - const value2 = _357; - if (_361.v === undefined) { - _361.v = value2; + const _321: rs.Cell> = new rs.Cell(new rs.Cell(((n as rs.u32) ^ 14) >>> 0)); + const _325: rs.Place>> = deep; + const value2 = _321; + if (_325.v === undefined) { + _325.v = value2; } else { - _361.v.v.v = value2.v.v; + _325.v.v.v = value2.v.v; } } rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toNest.v & 0xff, toDeep.v & 0xff, toPair.v & 0xff, toStrukt.v & 0xff)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toObj[0], toObj[1], toCell.v & 0xff, toWrapped.v & 0xff)), undefined, true); - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(toUninit.v & 0xff, toManual.v & 0xff)), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(nest.v.v & 0xff, pair.v[1], strukt.v.n)), undefined, true); } } diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index c69852e44..1705c9f96 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -2844,9 +2844,8 @@ struct Celled { n: u8, } -/// Whole writes of values holding cells into a cell, a tuple, a struct, a `MaybeUninit`, and -/// a `ManuallyDrop`, each read back through a pointer saved into an inner cell before: -/// `UnsafeCell::replace`, `ptr::replace`, +/// Whole writes of values holding cells into a cell, a tuple, and a struct, each read back +/// through a pointer saved into an inner cell before: `UnsafeCell::replace`, `ptr::replace`, /// `ptr::write`, `ptr::swap`, `Cell`'s writes, and assignments through a raw pointer; a /// value `ptr::read` copies out before one, and before a write into a part holding no /// cell; `ptr::write`s into memory not written yet; and pointers `UnsafeCell::raw_get` @@ -2862,12 +2861,8 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { let obj = UnsafeCell::new(UnsafeCell::new((7u8, 8u8))); let cell = Cell::new((UnsafeCell::new(9u32), 10u8)); let wrapped = Cell::new(UnsafeCell::new(11u32)); - let uninit = UnsafeCell::new(MaybeUninit::new(UnsafeCell::new(12u32))); - let manual = UnsafeCell::new(mem::ManuallyDrop::new(UnsafeCell::new(13u32))); unsafe { let to_wrapped = (*wrapped.as_ptr()).get(); - let to_uninit = (*uninit.get()).assume_init_ref().get(); - let to_manual = (*manual.get()).get(); let to_nest = (*nest.get()).get(); let to_deep = (*(*deep.get()).get()).get(); let to_pair = (*pair.get()).0.get(); @@ -2891,10 +2886,6 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { out.extend([old.c.into_inner() as u8, old.n]); let old = obj.replace(UnsafeCell::new((n, n ^ 1))); out.push(old.into_inner().1); - let old = uninit.replace(MaybeUninit::new(UnsafeCell::new(u32::from(n) ^ 20))); - out.push(old.assume_init().into_inner() as u8); - let old = manual.replace(mem::ManuallyDrop::new(UnsafeCell::new(u32::from(n) ^ 21))); - out.push(mem::ManuallyDrop::into_inner(old).into_inner() as u8); } 1 => { let old = ptr::replace(nest.get(), UnsafeCell::new(u32::from(n) ^ 1)); @@ -2971,7 +2962,6 @@ pub fn nested_cells(data: &[u8], out: &mut Vec) { } out.extend([*to_nest as u8, *to_deep as u8, *to_pair as u8, *to_strukt as u8]); out.extend([(*to_obj).0, (*to_obj).1, *to_cell as u8, *to_wrapped as u8]); - out.extend([*to_uninit as u8, *to_manual as u8]); out.extend([(*nest.get()).get().read() as u8, (*pair.get()).1, (*strukt.get()).n]); } } diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index 3efd454d2..f7530611f 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -35,6 +35,11 @@ fixtures/reject/src/alloc.rs:pinned_lent+2:43: `UnsafeCell::as_mut_unchecked` of fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:uninit_cell_lent+2:17: `MaybeUninit::assume_init_mut` of `std::cell::UnsafeCell`, whose mutable reference is the object, but the uninitialized value's is the place it is kept in fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept +fixtures/reject/src/alloc.rs:wrapped_cells+8:19: a whole write of `std::mem::MaybeUninit<(std::cell::UnsafeCell, u32)>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+9:22: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+10:3: a whole write of `(std::mem::MaybeUninit>, u32)`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+11:8: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+12:3: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:zeroed+1:13: unsupported call to `std::alloc::alloc_zeroed` fixtures/reject/src/buffers.rs:blank_bytes+1:2: `Buf::copy_to_bytes` of `buffers::Blank`, a value JS copies, which is not supported diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 881e38072..6c7b82dbc 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -39,6 +39,11 @@ fixtures/reject/src/alloc.rs:pinned_lent+2:43: `UnsafeCell::as_mut_unchecked` of fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:uninit_cell_lent+2:17: `MaybeUninit::assume_init_mut` of `std::cell::UnsafeCell`, whose mutable reference is the object, but the uninitialized value's is the place it is kept in fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept +fixtures/reject/src/alloc.rs:wrapped_cells+8:19: a whole write of `std::mem::MaybeUninit<(std::cell::UnsafeCell, u32)>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+9:22: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+10:3: a whole write of `(std::mem::MaybeUninit>, u32)`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+11:8: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds +fixtures/reject/src/alloc.rs:wrapped_cells+12:3: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds fixtures/reject/src/alloc.rs:zeroed+1:12: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:zeroed+1:13: unsupported call to `std::alloc::alloc_zeroed` fixtures/reject/src/buffers.rs:blank_bytes+1:2: `Buf::copy_to_bytes` of `buffers::Blank`, a value JS copies, which is not supported diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index 3be3c0a57..d3ec2b5ea 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -113,6 +113,27 @@ pub fn uninit_cell_lent(n: u32) -> u32 { unsafe { *slot.assume_init_mut().get_mut() + 1 } } +/// Whole writes of values holding a cell in a `MaybeUninit` or `ManuallyDrop`, which would +/// put another cell in place of one a pointer `get` gave points into: `UnsafeCell::replace` +/// of a wrapped tuple and of a wrapped cell, `ptr::write` of a tuple holding one, +/// `Cell::set`, and an assignment through a raw pointer. +pub fn wrapped_cells(n: u32) -> u32 { + use std::cell::{Cell, UnsafeCell}; + use std::mem::{ManuallyDrop, MaybeUninit}; + let tuple = UnsafeCell::new(MaybeUninit::new((UnsafeCell::new(n), n))); + let manual = UnsafeCell::new(ManuallyDrop::new(UnsafeCell::new(n))); + let mut pair = (MaybeUninit::new(UnsafeCell::new(n)), n); + let cell = Cell::new(ManuallyDrop::new(UnsafeCell::new(n))); + unsafe { + let old = tuple.replace(MaybeUninit::new((UnsafeCell::new(1), 1))); + let older = manual.replace(ManuallyDrop::new(UnsafeCell::new(2))); + std::ptr::write(&raw mut pair, (MaybeUninit::new(UnsafeCell::new(3)), 3)); + cell.set(ManuallyDrop::new(UnsafeCell::new(4))); + *manual.get() = ManuallyDrop::new(UnsafeCell::new(5)); + old.assume_init().1 ^ ManuallyDrop::into_inner(older).into_inner() ^ pair.1 + } +} + /// An `UnsafeCell::from_mut` of arrays of arrays, which a cell may read a copy of, and /// `Cell::swap`s of arrays, whose cells may partially overlap. pub fn cell_of_arrays(a: &mut [[u16; 2]; 2]) -> u16 { diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index ab488ddc9..ae0d56fcd 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -14,6 +14,7 @@ roots = [ "alloc::reinterpreted", "alloc::uninit_cell_lent", "alloc::vec_pointer", + "alloc::wrapped_cells", "alloc::zeroed", "bound_ranges", "buffers::blank_bytes", diff --git a/ts/src/census.rs b/ts/src/census.rs index 3217ed642..47e3fda6d 100644 --- a/ts/src/census.rs +++ b/ts/src/census.rs @@ -159,6 +159,8 @@ const VERBATIM: &[&str] = &[ "Location", "LowerExp", "MAY_HAVE_SIDE_EFFECT", + "ManuallyDrop", + "MaybeUninit", "MaybeUninit::assume_init_mut", "None", "Octal", diff --git a/ts/src/lower/mir/place.rs b/ts/src/lower/mir/place.rs index 87833604c..8ba93d11e 100644 --- a/ts/src/lower/mir/place.rs +++ b/ts/src/lower/mir/place.rs @@ -857,16 +857,22 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { return out; } // A whole write through a raw pointer keeps the cells a value keeps, as `ptr::write` - // does. + // does, and is refused where it would not. if let [ProjectionElem::Deref] = place.projection.as_slice() && matches!(self.mir.local_decls[place.local].ty.kind(), ty::RawPtr(..)) && let pointee = place.ty(&self.mir.local_decls, self.tcx).ty - && self.b.t.keeps_cells(pointee) { - let at = self.place_expr(place, span, &mut out); - let at = self.path(at, "kept", &mut out); - crate::lower::pointer::write_kept_at(self.b, at, value, pointee, span, &mut out); - return out; + if self.b.t.wraps_cells(pointee) { + let err = crate::lower::pointer::wrapped_write(self.b, pointee, span); + out.push(StmtKind::Expr(err).at(self.b.t.loc(span))); + return out; + } + if self.b.t.keeps_cells(pointee) { + let at = self.place_expr(place, span, &mut out); + let at = self.path(at, "kept", &mut out); + crate::lower::pointer::write_kept_at(self.b, at, value, pointee, span, &mut out); + return out; + } } // `*r = v` replaces the object `r` points at in place, which other references and // captures share. An alias of a borrowed place assigns the place, and an `rs.Place` diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index 39bf6149d..4c3fc6dbd 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -173,23 +173,43 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { } /// What a `ty` that [keeps cells](Self::keeps_cells) holds: a cell's value, or each - /// field of a tuple or struct, and whether it keeps cells too. A `MaybeUninit` or a - /// `ManuallyDrop` holds what JS keeps it as. + /// field of a tuple or struct, and whether it keeps cells too. pub fn kept_cells(&self, ty: Ty<'tcx>) -> Option> { - let ty = self.uninit_inner(ty); - let fields: Vec> = match ty.kind() { + let (fields, cell) = self.kept_parts(ty)?; + if cell { + return Some(KeptCells::Cell(fields[0])); + } + let fields: Vec<_> = fields.into_iter().map(|f| (f, self.keeps_cells(f))).collect(); + (self.pointee(ty) == Pointee::Object && fields.iter().any(|&(_, kept)| kept)) + .then_some(KeptCells::Fields(fields)) + } + + /// What a write keeping cells reaches in `ty`, and whether `ty` is a cell: a cell's + /// value, or each field of a tuple or a crate's struct. + fn kept_parts(&self, ty: Ty<'tcx>) -> Option<(Vec>, bool)> { + match ty.kind() { ty::Adt(adt, args) if Std::of(self.tcx, adt.did()) == Some(Std::Cell) => { - return Some(KeptCells::Cell(args.type_at(0))); + Some((vec![args.type_at(0)], true)) } - ty::Tuple(tys) => tys.to_vec(), + ty::Tuple(tys) => Some((tys.to_vec(), false)), ty::Adt(adt, args) if adt.is_struct() && Std::of(self.tcx, adt.did()).is_none() && self.own(adt.did()) => { - adt.all_fields().map(|f| self.field_ty(f.did, args)).collect() + Some((adt.all_fields().map(|f| self.field_ty(f.did, args)).collect(), false)) } - _ => return None, - }; - let fields: Vec<_> = fields.into_iter().map(|f| (f, self.keeps_cells(f))).collect(); - (self.pointee(ty) == Pointee::Object && fields.iter().any(|&(_, kept)| kept)) - .then_some(KeptCells::Fields(fields)) + _ => None, + } + } + + /// Whether a whole write of a `ty` would put other cells in place of those a + /// `MaybeUninit` or `ManuallyDrop` in it holds, which a write keeps nowhere, so a pointer + /// `get` gave into one would not see it: one of them holding a value that + /// [keeps cells](Self::keeps_cells), or a part such a write reaches holding one. + pub fn wraps_cells(&self, ty: Ty<'tcx>) -> bool { + let inner = self.uninit_inner(ty); + if inner != ty { + return self.keeps_cells(inner) || self.wraps_cells(inner); + } + self.kept_parts(ty) + .is_some_and(|(parts, _)| parts.into_iter().any(|part| self.wraps_cells(part))) } /// Whether only an instance decides what a pointer or reference to `ty` is, as in a @@ -1379,6 +1399,9 @@ pub(super) fn write<'tcx>( if b.t.zero_sized(ty) { return Expr::undefined(); } + if b.t.wraps_cells(ty) { + return wrapped_write(b, ty, span); + } if b.t.keeps_cells(ty) { let at = match b.t.pointee(ty) { Pointee::Object => p, @@ -1395,6 +1418,16 @@ pub(super) fn write<'tcx>( } } +/// The refusal of a whole write of a `ty` that [wraps cells](Translator::wraps_cells). +pub(super) fn wrapped_write<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, span: Span) -> Expr { + b.err( + span, + format!( + "a whole write of `{ty}`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds" + ), + ) +} + /// The JS value of the `ty` that `p` points at: a cell's `rs.Cell`, which a pointer to /// it is the place of, or the object a pointer to it is. fn pointed<'tcx>(b: &Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>) -> Expr { diff --git a/ts/src/lower/task.rs b/ts/src/lower/task.rs index 31b5e03e1..8b6197e5c 100644 --- a/ts/src/lower/task.rs +++ b/ts/src/lower/task.rs @@ -605,6 +605,7 @@ fn cell<'tcx>( b.call_def(f, tcx.mk_args(&[ty.into()]), Vec::new(), span, out) }; let e = match name { + "set" | "replace" | "take" | "update" | "swap" if b.t.wraps_cells(ty) => pointer::wrapped_write(b, ty, span), "new" => Expr::New(Box::new(Expr::Rs("Cell".into())), args), "get" => b.t.copied(first(args).member("v"), ty, span), "into_inner" => first(args).member("v"),