diff --git a/quest/m3/README.md b/quest/m3/README.md index c79b26afe..ac8e45039 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -31,7 +31,6 @@ generic std gap is planned here even when moq never calls it. - [std's sorts call their tests in std's order](/quest/m3/ts-sort-order.md) - `slice::sort` and a B-tree's `collect` call `lt` or `cmp` as std's driftsort does past 16 elements - [A B-tree's searches call Ord as std's do](/quest/m3/ts-btree-search-order.md) - a B-tree's lookups and inserts call a key's own `Ord` in std's order and number - [Rc and Arc of a slice](/quest/m3/ts-shared-slices.md) - `Rc<[T]>` and `Arc<[T]>` from a `Vec`, a slice, or an iterator translate -- [The rest of a boxed slice's impls](/quest/m3/ts-boxed-slice-rest.md) - `clone_from`, `AsRef`, `AsMut`, `TryFrom` into a boxed array, and `Box` into bytes translate for boxed slices - [The rest of a vector's methods](/quest/m3/ts-vec-rest.md) - `append`, `resize`, `split_off`, `try_reserve`, and `Vec::from(VecDeque)` translate - [A reversed zip's per-pair cost](/quest/m3/ts-zip-rev-overhead.md) - a reversed by-index zip costs per pair what a forward one does, or the gap is explained - [Whole writes into a lazy cell or thread-local key](/quest/m3/ts-lazy-writes.md) - a whole write through a `&mut` to a `LazyCell`, `LazyLock`, or `LocalKey` keeps its state diff --git a/quest/m3/ts-boxed-slice-rest.md b/quest/m3/ts-boxed-slice-rest.md deleted file mode 100644 index c9bf70a19..000000000 --- a/quest/m3/ts-boxed-slice-rest.md +++ /dev/null @@ -1,18 +0,0 @@ -# [S] The rest of a boxed slice's impls - -## Goal - -[rust2-ts](/ts/README.md) translates the `Box<[T]>` and `Box` impls still refused, -with std's semantics, proven by differential tests against native Rust. - -## Plan - -Found translating boxed slices: each of these refuses with its span as "unsupported call": - -- `Clone::clone_from` of a `Box<[T]>`, which std clones into in place when the lengths - match, by each element's `clone_from`, and otherwise replaces whole. -- `AsRef` and `AsMut` of a `Box`, for any `T`. -- `TryFrom>` and `TryFrom>` for `Box<[T; N]>`. -- `From>` for `Box<[u8]>`, and `str::into_boxed_bytes`. - -No consumer calls them yet; they are planned here as part of std. diff --git a/ts/README.md b/ts/README.md index 81f2efbca..cadf317f4 100644 --- a/ts/README.md +++ b/ts/README.md @@ -886,7 +886,7 @@ built natively for wasm32 and run beside its translation on the same inputs by - `lang` covers every integer width's arithmetic, casts, and inherent methods, with `NonZero`'s, `Wrapping`'s, and `Saturating`'s, at each width's edges, the ASCII methods of every byte, of chars either side of ASCII, Latin-1, and the surrogates, and of slices and strings, `char`s next to each - encoding's edges and the surrogates, `Cow` and `Cow<[T]>` borrowed and owned, `Box<[T]>` and `Box` made each way and turned back, `Wrapping` and `Saturating` at every width, `Duration`s whose + encoding's edges and the surrogates, `Cow` and `Cow<[T]>` borrowed and owned, `Box<[T]>` and `Box` made each way, cloned into, lent, and turned back, into a boxed array or bytes included, `Wrapping` and `Saturating` at every width, `Duration`s whose seconds pass 2^53 or reach `u64::MAX`, control flow, patterns, `Copy` aggregates, those a container stores included and those written into only in another module, behind a type parameter, or through a box, formatting, methods diff --git a/ts/fixtures/lang/expected/boxes.ts b/ts/fixtures/lang/expected/boxes.ts index 481c9015d..b5de1b696 100644 --- a/ts/fixtures/lang/expected/boxes.ts +++ b/ts/fixtures/lang/expected/boxes.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { copy$array_tuple2_u8_u16, copy$array_u16, copy$array_u8, copy$tuple2_u8_u16 } from "./lib.ts"; +import { copy$array_tuple2_u8_u16, copy$array_u16, copy$array_u8, copy$tuple2_u8_u16, drop$Result_boxed_Box_array1_boxes_Logged_boxed_Box_slice_boxes_Logged } from "./lib.ts"; import * as boxes_Held from "./boxes.Held.ts"; import * as boxes_Logged from "./boxes.Logged.ts"; import type * as Owner from "./boxes.Owner.ts"; @@ -714,4 +714,327 @@ export function places(_1: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void { } } +/** Clones `from` into `slot` by its `clone_from`. */ +export function refill$boxed_Box_slice_boxes_Logged(slot: rt.Place, from: boxes_Logged.Logged[]): void { + slot.v = rt.boxSliceCloneFrom(slot.v, from, boxes_Logged.clone$Clone, rt.drop); +} + +/** Clones `from` into `slot` by its `clone_from`. */ +export function refill$boxed_Box_slice_u8(slot: rt.Place, from: rt.ByteSlice): void { + slot.v = rt.byteBoxSliceCloneFrom(slot.v, from); +} + +/** The sum of the bytes `x` lends by its `AsRef`. */ +export function sum$boxed_Box_slice_u8(x: rt.ByteSlice): rt.u8 { + return rt.iterFold(rt.iterSlice(rt.byteSliceView(x)), 0, (a: rt.u8, _32: rt.u8): rt.u8 => { + return rt.u8WrappingAdd(a, _32); + }); +} + +/** Writes through what `x` lends by its `AsMut`. */ +export function flip$boxed_Box_slice_u8(x: rt.Place): void { + const iter: rt.Iter> = rt.iterSliceMut(rt.byteSliceView(x.v)); + while (true) { + const _5: rt.Place | rt.Done = iter.next(); + if (_5 === rt.DONE) { + break; + } + _5.v = _5.v ^ 15; + } +} + +/** Puts what boxing `r` into an array gave: its bytes, or a marker and what it gave back. */ +export function boxed$0_boxed_Box_slice_u8(r: rt.Result, out: rt.ByteVec): void { + if (r.ok) { + const a: Uint8Array = r.value; + const _5: rt.Place | undefined = rt.sliceFirstMut(rt.byteSliceView(rt.byteSliceFrom(a))); + if (_5 !== undefined) { + _5.v = _5.v ^ 255; + } + bytes(rt.byteSliceFrom(a), out); + } else { + const e: rt.ByteSlice = r.error; + rt.byteVecPush(out, 238); + bytes(e, out); + } +} + +/** Puts what boxing `r` into an array gave: its bytes, or a marker and what it gave back. */ +export function boxed$3_boxed_Box_slice_u8(r: rt.Result, out: rt.ByteVec): void { + if (r.ok) { + const a: Uint8Array = r.value; + const _5: rt.Place | undefined = rt.sliceFirstMut(rt.byteSliceView(rt.byteSliceFrom(a))); + if (_5 !== undefined) { + _5.v = _5.v ^ 255; + } + bytes(rt.byteSliceFrom(a), out); + } else { + const e: rt.ByteSlice = r.error; + rt.byteVecPush(out, 238); + bytes(e, out); + } +} + +/** Puts what boxing `r` into an array gave: its bytes, or a marker and what it gave back. */ +export function boxed$4_Vec_u8(r: rt.Result, out: rt.ByteVec): void { + if (r.ok) { + const a: Uint8Array = r.value; + const _5: rt.Place | undefined = rt.sliceFirstMut(rt.byteSliceView(rt.byteSliceFrom(a))); + if (_5 !== undefined) { + _5.v = _5.v ^ 255; + } + bytes(rt.byteSliceFrom(a), out); + } else { + const e: rt.ByteVec = r.error; + rt.byteVecPush(out, 238); + bytes(rt.byteVecSlice(e), out); + } +} + +/** + * The rest of a box's impls: `clone_from` of a boxed slice, into one of its length by + * each item's `clone_from` and into one of another replaced whole; `AsRef` and `AsMut` of + * a box of any value; `TryFrom` of a boxed slice and a `Vec` into a boxed array, of its + * length or not; and a boxed text's bytes. Each of bytes and of other values, empty or + * not, with what drops dropped in std's order. + */ +export function impls(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void { + const data2: rt.ByteSlice = rt.byteSliceRange(data, 0, rt.usizeRem(kind >> 3 as rt.usize, rt.usizeAdd(data.length, 1))); + const half: rt.usize = rt.usizeDiv(data2.length, 2); + const log: rt.Arc> = new rt.Arc(new rt.RefCell(new rt.ByteVec())); + const logged: (a0: rt.u8) => boxes_Logged.Logged = (id: rt.u8): boxes_Logged.Logged => { + return new boxes_Logged.Logged(id, rt.arcClone(log)); + }; + const _23: rt.u8 = rt.u8Rem(kind, 4); + if (_23 === 0) { + let a: rt.ByteSlice = rt.byteSliceFrom(rt.byteSliceToArray(rt.byteSliceRange(data2, 0, half))); + const b: rt.ByteSlice = rt.byteSliceFrom(rt.byteSliceToArray(rt.byteSliceRange(data2, half, rt.usizeMul(half, 2)))); + a = rt.byteBoxSliceCloneFrom(a, b); + const _36: rt.Place | undefined = rt.sliceFirstMut(rt.byteSliceView(a)); + if (_36 !== undefined) { + _36.v = _36.v ^ 255; + } + bytes(a, out); + bytes(b, out); + const _49: rt.ByteSlice = rt.byteSliceFrom(rt.byteSliceToArray(data2)); + refill$boxed_Box_slice_u8(rt.place(() => a, (value2) => a = value2), _49); + rt.byteSliceView(a).reverse(); + bytes(a, out); + let w: rt.u16[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, half))), (_216: rt.u8): rt.u16 => { + return rt.u16Mul(_216 as rt.u16, 257); + }), undefined, 2, true); + const v: rt.u16[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, 0, rt.usizeSub(data2.length, half)))), (_218: rt.u8): rt.u16 => { + return (((_218 as rt.u16)) << 3) & 0xffff; + }), undefined, 2, true); + w = rt.boxSliceCloneFrom(w, v); + w.reverse(); + wides(w, out); + wides(v, out); + const _82: rt.u16[] = [1, 2, 3]; + w = rt.boxSliceCloneFrom(w, _82); + wides(w, out); + let p: [rt.u8, rt.u16][] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, 0, half))), (_220: rt.u8): [rt.u8, rt.u16] => { + return [_220, 7] as [rt.u8, rt.u16]; + }), undefined, 4, true); + const q: [rt.u8, rt.u16][] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, half, rt.usizeMul(half, 2)))), (_221: rt.u8): [rt.u8, rt.u16] => { + return [_221 ^ 1, 9] as [rt.u8, rt.u16]; + }), undefined, 4, true); + p = rt.boxSliceCloneFrom(p, q, copy$tuple2_u8_u16); + const _102: [rt.u8, rt.u16] | undefined = rt.sliceFirst(p); + if (_102 !== undefined) { + _102[0] = 51; + _102[1] = 68; + } + const iter: rt.Iter<[rt.u8, rt.u16]> = rt.iterChain(rt.iterSlice(p), rt.iterSlice(q)); + while (true) { + const _113: [rt.u8, rt.u16] | rt.Done = iter.next(); + if (_113 === rt.DONE) { + break; + } + rt.byteVecPush(out, _113[0]); + rt.iterExtendBytes(out, rt.iterSlice(rt.u16ToBeBytes(_113[1]).reverse()), undefined, true); + } + let s: string[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, 0, half))), (_222: rt.u8): string => { + return text(rt.byteSliceFrom(Uint8Array.of(_222))); + }), undefined, 12, true); + const t: string[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, half, rt.usizeMul(half, 2)))), (_223: rt.u8): string => { + const x11: rt.u8 = _223; + return text(rt.byteSliceFrom(Uint8Array.of(x11, x11))); + }), undefined, 12, true); + s = rt.boxSliceCloneFrom(s, t); + const _138: rt.Place | undefined = rt.sliceFirstMut(s); + if (_138 !== undefined) { + _138.v = _138.v + "!"; + } + const iter2: rt.Iter = rt.iterChain(rt.iterSlice(s), rt.iterSlice(t)); + while (true) { + const _150: string | rt.Done = iter2.next(); + if (_150 === rt.DONE) { + break; + } + fmt_put(out, _150); + } + let l: boxes_Logged.Logged[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, 0, half))), (_224: rt.u8): boxes_Logged.Logged => { + return logged(_224 & 126); + }), undefined, 8, true); + const m: boxes_Logged.Logged[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, half, rt.usizeMul(half, 2)))), (_226: rt.u8): boxes_Logged.Logged => { + return logged(_226 | 1); + }), undefined, 8, true); + flush(log.value, out); + l = rt.boxSliceCloneFrom(l, m, boxes_Logged.clone$Clone, rt.drop); + flush(log.value, out); + ids(l, out); + const _187: boxes_Logged.Logged[] = [logged(16), logged(18), logged(20)]; + refill$boxed_Box_slice_boxes_Logged(rt.place(() => l, (value3) => l = value3), _187); + rt.dropAll(_187); + flush(log.value, out); + ids(l, out); + const held: boxes_Held.Held = new boxes_Held.Held(rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(rt.byteSliceRange(data2, 0, rt.usizeSub(data2.length, half)))), (_227: rt.u8): boxes_Logged.Logged => { + return logged(_227 & 126); + }), undefined, 8, true), logged(80)); + flush(log.value, out); + held.items = rt.boxSliceCloneFrom(held.items, m, boxes_Logged.clone$Clone, rt.drop); + flush(log.value, out); + ids(held.items, out); + held[Symbol.dispose](); + flush(log.value, out); + const v2: rt.ByteSlice[] = [rt.byteSliceFrom(rt.byteSliceToArray(rt.byteSliceRange(data2, 0, half))), rt.byteSliceFrom(rt.byteSliceToArray(data2))]; + const at = rt.idx(v2, 1); + v2[at] = rt.byteBoxSliceCloneFrom(v2[at], b); + const at2 = rt.idx(v2, 0); + v2[at2] = rt.byteBoxSliceCloneFrom(v2[at2], a); + for (const _252 of v2) { + bytes(_252, out); + } + rt.dropAll(l); + rt.dropAll(m); + flush(log.value, out); + let t2: string = "ab"; + t2 = text(data2); + fmt_put(out, t2); + } else if (_23 === 1) { + let a2: rt.ByteSlice = rt.byteSliceFrom(rt.byteSliceToArray(data2)); + bytes(a2, out); + rt.byteSliceView(a2).reverse(); + flip$boxed_Box_slice_u8(rt.place(() => a2, (value) => a2 = value)); + rt.byteVecPush(out, sum$boxed_Box_slice_u8(a2)); + bytes(a2, out); + let n: rt.u32 = data2.length as rt.u32; + n = rt.u32Add(n, 7); + rt.iterExtendBytes(out, rt.iterSlice(rt.u32ToBeBytes(n).reverse()), undefined, true); + const pair: [rt.u8, rt.u16] = [option_unwrapOr$0r2bmpr(rt.byteSliceFirst(data2), 1), 2]; + const _306: [rt.u8, rt.u16] = pair; + _306[1] = _306[1] ^ 257; + const copy: [rt.u8, rt.u16] = copy$tuple2_u8_u16(pair); + pair[0] = 9; + rt.byteVecPush(out, copy[0]); + rt.iterExtendBytes(out, rt.iterSlice(rt.u16ToBeBytes(copy[1]).reverse()), undefined, true); + rt.byteVecPush(out, pair[0]); + const array: Uint8Array = Uint8Array.of(1, 2, 3, 4); + const _322: rt.ByteSlice = rt.byteSliceFrom(array); + const _324: rt.usize = rt.usizeRem(kind as rt.usize, 4); + if (!(_324 < 4)) { + rt.panic("index out of bounds: the len is " + 4 + " but the index is " + _324); + } + _322.data[_322.start + _324] = 85; + rt.byteSliceView(rt.byteSliceFrom(array)).reverse(); + bytes(rt.byteSliceFrom(array), out); + const v3: rt.ByteVec = rt.byteSliceToByteVec(data2); + rt.byteVecPush(v3, 102); + bytes(rt.byteVecSlice(v3), out); + let s2: string = text(data2); + s2 = s2 + "#"; + fmt_put(out, s2); + const w2: rt.u16[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(data2)), (_28: rt.u8): rt.u16 => { + return rt.u16Add(_28 as rt.u16, 1); + }), undefined, 2, true); + rt.sliceSort(w2, rt.lt); + wides(w2, out); + const l2: boxes_Logged.Logged[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(data2)), (_214: rt.u8): boxes_Logged.Logged => { + return logged(_214); + }), undefined, 8, true); + const _368: boxes_Logged.Logged[] = l2; + if (_368.length >= 2) { + const swapped = _368[0]; + _368[0] = _368[_368.length - 1]; + _368[_368.length - 1] = swapped; + } + ids(l2, out); + let o: rt.u8 | undefined = rt.byteSliceFirst(data2); + if (o !== undefined) { + o = rt.u8WrappingAdd(o, 1); + } + rt.byteVecPush(out, option_unwrapOr$0r2bmpr(o, 238)); + rt.dropAll(l2); + } else if (_23 === 2) { + const _395: rt.ByteSlice = rt.byteSliceFrom(rt.byteSliceToArray(data2)); + boxed$3_boxed_Box_slice_u8(_395.length === 3 ? rt.Ok(rt.byteSliceView(_395)) : rt.Err(_395), out); + const _398: rt.ByteSlice = rt.byteSliceFrom(rt.byteSliceToArray(data2)); + boxed$0_boxed_Box_slice_u8(_398.length === 0 ? rt.Ok(rt.byteSliceView(_398)) : rt.Err(_398), out); + const _400: rt.ByteVec = rt.byteSliceToByteVec(data2); + const r: rt.Result = _400.length === 2 ? rt.Ok(rt.byteSliceView(rt.byteVecSlice(_400))) : rt.Err(_400); + if (r.ok) { + bytes(rt.byteSliceFrom(r.value), out); + } else { + const v4: rt.ByteVec = r.error; + rt.byteVecPush(v4, 119); + bytes(rt.byteVecSlice(v4), out); + } + const spare: rt.ByteVec = rt.byteVecWithCapacity(32); + rt.byteVecExtendFromSlice(spare, data2); + const _417: rt.ByteVec = spare; + boxed$4_Vec_u8(_417.length === 4 ? rt.Ok(rt.byteSliceView(rt.byteVecSlice(_417))) : rt.Err(_417), out); + const w3: rt.u16[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(data2)), (_2: rt.u8): rt.u16 => { + return rt.u16Mul(_2 as rt.u16, 3); + }), undefined, 2, true); + const _422: rt.Result = w3.length === 2 ? rt.Ok(w3) : rt.Err(w3); + if (_422.ok) { + const a4: rt.u16[] = _422.value; + a4[1] = 4660; + wides(a4, out); + } else { + wides(_422.error, out); + } + const l3: boxes_Logged.Logged[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(data2)), (_22: rt.u8): boxes_Logged.Logged => { + return logged(_22 & 126); + }), undefined, 8, true); + const _438: rt.Result = l3.length === 2 ? rt.Ok(l3) : rt.Err(l3); + if (_438.ok) { + const a5: boxes_Logged.Logged[] = _438.value; + ids(a5, out); + flush(log.value, out); + rt.dropAll(a5); + } else { + const l4: boxes_Logged.Logged[] = _438.error; + ids(l4, out); + flush(log.value, out); + rt.dropAll(l4); + } + flush(log.value, out); + const l5: boxes_Logged.Logged[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(data2)), (_25: rt.u8): boxes_Logged.Logged => { + return logged(_25 | 1); + }), undefined, 8, true); + const r2: rt.Result = l5.length === 1 ? rt.Ok(l5) : rt.Err(l5); + flush(log.value, out); + drop$Result_boxed_Box_array1_boxes_Logged_boxed_Box_slice_boxes_Logged(r2); + flush(log.value, out); + } else { + const s3: string = text(data2); + const a6: rt.ByteSlice = rt.stringAsBytes(s3); + rt.byteSliceView(a6).reverse(); + bytes(a6, out); + const b2: rt.ByteSlice = rt.stringAsBytes(s3); + const _485: rt.Place | undefined = rt.sliceLastMut(rt.byteSliceView(b2)); + if (_485 !== undefined) { + _485.v = 33; + } + bytes(b2, out); + const v5: rt.ByteVec = rt.byteSliceToByteVec(rt.stringAsBytes(s3)); + rt.byteVecPush(v5, 46); + bytes(rt.byteVecSlice(v5), out); + bytes(rt.stringAsBytes(""), out); + } + log[Symbol.dispose](); +} + //# sourceMappingURL=boxes.ts.map diff --git a/ts/fixtures/lang/expected/harness.ts b/ts/fixtures/lang/expected/harness.ts index 8c9c5641c..43520bfdf 100644 --- a/ts/fixtures/lang/expected/harness.ts +++ b/ts/fixtures/lang/expected/harness.ts @@ -7,7 +7,7 @@ import { bytes as ascii_bytes, chars as ascii_chars, slices as ascii_slices, par import { atomics } from "./atomics.ts"; import { borrows as borrows_borrows, guards, maps as borrows_maps, tries, leaks } from "./borrows.ts"; import { bounds as bounds_bounds } from "./bounds.ts"; -import { boxes, places as boxes_places } from "./boxes.ts"; +import { boxes, places as boxes_places, impls as boxes_impls } from "./boxes.ts"; import { vecs as capacity_vecs, deques as capacity_deques, heaps as capacity_heaps, collects, maps as capacity_maps } from "./capacity.ts"; import { captures } from "./captures.ts"; import { cells as cells_cells } from "./cells.ts"; @@ -61,7 +61,7 @@ import { tuples as tuples_tuples } from "./tuples.ts"; import { decodes } from "./utf8.ts"; import { wrappers } from "./wrappers.ts"; import { u8s as wraps_u8s, u16s as wraps_u16s, u32s as wraps_u32s, u64s as wraps_u64s, u128s as wraps_u128s, usizes as wraps_usizes, i8s as wraps_i8s, i16s as wraps_i16s, i32s as wraps_i32s, i64s as wraps_i64s, i128s as wraps_i128s, isizes as wraps_isizes } from "./wraps.ts"; -import { zeros, impls, keys as zeros_keys, unread, writes as zeros_writes } from "./zeros.ts"; +import { zeros, impls as zeros_impls, keys as zeros_keys, unread, writes as zeros_writes } from "./zeros.ts"; /** * Each case's name and what it runs. The first byte picks one modulo their count, so a @@ -218,7 +218,7 @@ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) arrays_lent(data70, out71); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["floats::mul_adds", floats_mulAdds as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["singles::mul_adds", singles_mulAdds as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fmt::collections", (_2171: rt.u8, data71: rt.ByteSlice, out72: rt.ByteVec): undefined => { collections(data71, out72); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["collections::unlike_eqs", unlikeEqs as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["zeros::impls", impls as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["zeros::keys", zeros_keys as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["keys::arrays", keys_arrays as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["iters::zips", zips as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["iters::zips_back", zipsBack as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["collections::own_nes", ownNes as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fmt::iters", (_2172: rt.u8, data72: rt.ByteSlice, out73: rt.ByteVec): undefined => { + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["collections::unlike_eqs", unlikeEqs as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["zeros::impls", zeros_impls as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["zeros::keys", zeros_keys as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["keys::arrays", keys_arrays as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["iters::zips", zips as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["iters::zips_back", zipsBack as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["collections::own_nes", ownNes as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fmt::iters", (_2172: rt.u8, data72: rt.ByteSlice, out73: rt.ByteVec): undefined => { fmt_iters(data72, out73); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["zeros::unread", unread as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["iters::range_froms", rangeFroms as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["iters::range_methods", rangeMethods as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["clones::iters", clones_iters as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["collections::cell_eqs", cellEqs as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["iters::compares", compares as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["collections::unlike_compares", unlikeCompares as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["collections::ref_orders", refOrders as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["cycles::cycles", cycles as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["pointers::lapsed", (_2173: rt.u8, data73: rt.ByteSlice, out74: rt.ByteVec): undefined => { lapsed(data73, out74); @@ -262,18 +262,18 @@ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) borrows_maps(data91, out92); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locks::mapped", (_2192: rt.u8, data92: rt.ByteSlice, out93: rt.ByteVec): undefined => { mapped(data92, out93); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["conversions::conversions", conversions as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::tries", (_2193: rt.u8, data93: rt.ByteSlice, out94: rt.ByteVec): undefined => { + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["boxes::impls", boxes_impls as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["conversions::conversions", conversions as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::tries", (_2193: rt.u8, data93: rt.ByteSlice, out94: rt.ByteVec): undefined => { tries(data93, out94); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locks::mutex_mapped", (_2194: rt.u8, data94: rt.ByteSlice, out95: rt.ByteVec): undefined => { mutexMapped(data94, out95); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locks::printed", (_2195: rt.u8, data95: rt.ByteSlice, out96: rt.ByteVec): undefined => { locks_printed(data95, out96); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::vecs", (a0153: rt.u8, a1153: rt.ByteSlice, a2153: rt.ByteVec): undefined => { - capacity_vecs(a0153, a1153.data, a1153.start, a1153.length, a2153); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::deques", (a0154: rt.u8, a1154: rt.ByteSlice, a2154: rt.ByteVec): undefined => { - capacity_deques(a0154, a1154.data, a1154.start, a1154.length, a2154); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::heaps", (a0155: rt.u8, a1155: rt.ByteSlice, a2155: rt.ByteVec): undefined => { - capacity_heaps(a0155, a1155.data, a1155.start, a1155.length, a2155); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::vecs", (a0154: rt.u8, a1154: rt.ByteSlice, a2154: rt.ByteVec): undefined => { + capacity_vecs(a0154, a1154.data, a1154.start, a1154.length, a2154); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::deques", (a0155: rt.u8, a1155: rt.ByteSlice, a2155: rt.ByteVec): undefined => { + capacity_deques(a0155, a1155.data, a1155.start, a1155.length, a2155); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::heaps", (a0156: rt.u8, a1156: rt.ByteSlice, a2156: rt.ByteVec): undefined => { + capacity_heaps(a0156, a1156.data, a1156.start, a1156.length, a2156); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::collects", collects as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::maps", capacity_maps as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locks::downgraded", (_2196: rt.u8, data96: rt.ByteSlice, out97: rt.ByteVec): undefined => { downgraded(data96, out97); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::leaks", (_2197: rt.u8, data97: rt.ByteSlice, out98: rt.ByteVec): undefined => { diff --git a/ts/fixtures/lang/expected/lib.ts b/ts/fixtures/lang/expected/lib.ts index 7abf908c7..b5a559ea3 100644 --- a/ts/fixtures/lang/expected/lib.ts +++ b/ts/fixtures/lang/expected/lib.ts @@ -3,6 +3,7 @@ import * as rt from "./rt.ts"; import type * as borrows_Parts from "./borrows.Parts.ts"; import type * as borrows_Tagged from "./borrows.Tagged.ts"; +import type * as boxes_Logged from "./boxes.Logged.ts"; import type * as clones_Logged from "./clones.Logged.ts"; import type * as collections_Picky from "./collections.Picky.ts"; import type * as Quirky from "./collections.Quirky.ts"; @@ -860,6 +861,14 @@ export function drop$Result_Result_sync_MappedRwLockWriteGuard_u64_sync_MappedRw } } +export function drop$Result_boxed_Box_array1_boxes_Logged_boxed_Box_slice_boxes_Logged(_1: rt.Result): void { + if (_1.ok) { + rt.dropAll(_1.value); + } else { + rt.dropAll(_1.error); + } +} + export function drop$Result_cell_RefMut_Vec_u8_cell_BorrowMutError(_1: rt.Result, rt.BorrowMutError>): void { if (_1.ok) { rt.refMutRelease(_1.value); diff --git a/ts/fixtures/lang/src/boxes.rs b/ts/fixtures/lang/src/boxes.rs index dfae1e516..a6678ba6c 100644 --- a/ts/fixtures/lang/src/boxes.rs +++ b/ts/fixtures/lang/src/boxes.rs @@ -8,6 +8,7 @@ use std::borrow::Cow; use std::cell::RefCell; use std::hash::{DefaultHasher, Hash, Hasher}; +use std::ops::Deref; use std::rc::Rc; use crate::fmt::put; @@ -558,3 +559,225 @@ pub fn places(_: u8, data: &[u8], out: &mut Vec) { out.extend(boxed_opt.unwrap_or(0xee).to_le_bytes()); } } + +/// Clones `from` into `slot` by its `clone_from`. +fn refill(slot: &mut T, from: &T) { + slot.clone_from(from); +} + +/// The sum of the bytes `x` lends by its `AsRef`. +fn sum + ?Sized>(x: &T) -> u8 { + x.as_ref().iter().fold(0, |a, &b| a.wrapping_add(b)) +} + +/// Writes through what `x` lends by its `AsMut`. +fn flip + ?Sized>(x: &mut T) { + for b in x.as_mut() { + *b ^= 0x0f; + } +} + +/// Puts what boxing `r` into an array gave: its bytes, or a marker and what it gave back. +fn boxed>(r: Result, E>, out: &mut Vec) { + match r { + Ok(mut a) => { + if let Some(first) = a.first_mut() { + *first ^= 0xff; + } + bytes(&*a, out); + } + Err(e) => { + out.push(0xee); + bytes(&e, out); + } + } +} + +/// The rest of a box's impls: `clone_from` of a boxed slice, into one of its length by +/// each item's `clone_from` and into one of another replaced whole; `AsRef` and `AsMut` of +/// a box of any value; `TryFrom` of a boxed slice and a `Vec` into a boxed array, of its +/// length or not; and a boxed text's bytes. Each of bytes and of other values, empty or +/// not, with what drops dropped in std's order. +pub fn impls(kind: u8, data: &[u8], out: &mut Vec) { + let data = &data[..usize::from(kind >> 3) % (data.len() + 1)]; + let half = data.len() / 2; + let log = Rc::new(RefCell::new(Vec::new())); + let logged = |id: u8| Logged { id, log: log.clone() }; + match kind % 4 { + 0 => { + let mut a: Box<[u8]> = Box::from(&data[..half]); + let b: Box<[u8]> = Box::from(&data[half..half * 2]); + a.clone_from(&b); + if let Some(x) = a.first_mut() { + *x ^= 0xff; + } + bytes(&a, out); + bytes(&b, out); + refill(&mut a, &Box::from(data)); + a.reverse(); + bytes(&a, out); + let mut w: Box<[u16]> = data[half..].iter().map(|&x| u16::from(x) * 257).collect(); + let v: Box<[u16]> = data[..data.len() - half].iter().map(|&x| u16::from(x) << 3).collect(); + w.clone_from(&v); + w.reverse(); + wides(&w, out); + wides(&v, out); + w.clone_from(&Box::from([1, 2, 3])); + wides(&w, out); + let mut p: Box<[(u8, u16)]> = data[..half].iter().map(|&x| (x, 7)).collect(); + let q: Box<[(u8, u16)]> = data[half..half * 2].iter().map(|&x| (x ^ 1, 9)).collect(); + p.clone_from(&q); + if let Some(x) = p.first_mut() { + x.0 = 0x33; + x.1 = 0x44; + } + for (a, b) in p.iter().chain(q.iter()) { + out.push(*a); + out.extend(b.to_le_bytes()); + } + let mut s: Box<[String]> = data[..half].iter().map(|&x| text(&[x])).collect(); + let t: Box<[String]> = data[half..half * 2].iter().map(|&x| text(&[x, x])).collect(); + s.clone_from(&t); + if let Some(x) = s.first_mut() { + x.push('!'); + } + for x in s.iter().chain(t.iter()) { + put(out, x); + } + // Each item by its `clone_from`, which clones and drops what it writes over, and + // of another length, all cloned before the old drop. + let mut l: Box<[Logged]> = data[..half].iter().map(|&x| logged(x & 0x7e)).collect(); + let m: Box<[Logged]> = data[half..half * 2].iter().map(|&x| logged(x | 1)).collect(); + flush(&log, out); + l.clone_from(&m); + flush(&log, out); + ids(&l, out); + refill(&mut l, &Box::from([logged(0x10), logged(0x12), logged(0x14)])); + flush(&log, out); + ids(&l, out); + // Into a field, and into a `Vec`'s element. + let mut held = Held { + items: data[..data.len() - half].iter().map(|&x| logged(x & 0x7e)).collect(), + last: logged(0x50), + }; + flush(&log, out); + held.items.clone_from(&m); + flush(&log, out); + ids(&held.items, out); + drop(held); + flush(&log, out); + let mut v: Vec> = vec![Box::from(&data[..half]), Box::from(data)]; + v[1].clone_from(&b); + v[0].clone_from(&a); + for x in &v { + bytes(x, out); + } + drop(l); + drop(m); + flush(&log, out); + let mut t: Box = Box::from("ab"); + t.clone_from(&Box::from(text(data).as_str())); + put(out, &t); + } + 1 => { + let mut a: Box<[u8]> = Box::from(data); + bytes(a.as_ref(), out); + a.as_mut().reverse(); + flip(&mut a); + out.push(sum(&a)); + bytes(&a, out); + let mut n = Box::new(data.len() as u32); + *n.as_mut() += 7; + out.extend(n.as_ref().to_le_bytes()); + let mut pair = Box::new((data.first().copied().unwrap_or(1), 2u16)); + pair.as_mut().1 ^= 0x0101; + let copy = *pair.as_ref(); + pair.as_mut().0 = 9; + out.push(copy.0); + out.extend(copy.1.to_le_bytes()); + out.push(pair.0); + let mut array = Box::new([1u8, 2, 3, 4]); + array.as_mut()[usize::from(kind) % 4] = 0x55; + array.as_mut().reverse(); + bytes(array.as_ref(), out); + let mut v = Box::new(data.to_vec()); + v.as_mut().push(0x66); + bytes(v.as_ref(), out); + let mut s = Box::new(text(data)); + s.as_mut().push('#'); + put(out, s.as_ref()); + let mut w: Box<[u16]> = data.iter().map(|&x| u16::from(x) + 1).collect(); + w.as_mut().sort_unstable(); + wides(w.as_ref(), out); + let mut l: Box<[Logged]> = data.iter().map(|&x| logged(x)).collect(); + if let [first, .., last] = l.as_mut() { + std::mem::swap(first, last); + } + ids(l.as_ref(), out); + let mut o: Box> = Box::new(data.first().copied()); + if let Some(x) = o.as_mut() { + *x = x.wrapping_add(1); + } + out.push(o.as_ref().unwrap_or(0xee)); + } + 2 => { + boxed(>::try_from(Box::<[u8]>::from(data)), out); + boxed(>::try_from(Box::<[u8]>::from(data)), out); + let r: Result, Vec> = data.to_vec().try_into(); + match r { + Ok(a) => bytes(&*a, out), + Err(mut v) => { + v.push(0x77); + bytes(&v, out); + } + } + let mut spare = Vec::with_capacity(32); + spare.extend_from_slice(data); + boxed(>::try_from(spare), out); + let w: Box<[u16]> = data.iter().map(|&x| u16::from(x) * 3).collect(); + match >::try_from(w) { + Ok(mut a) => { + a[1] = 0x1234; + wides(&*a, out); + } + Err(w) => wides(&w, out), + } + let l: Vec = data.iter().map(|&x| logged(x & 0x7e)).collect(); + match >::try_from(l) { + Ok(a) => { + ids(&*a, out); + flush(&log, out); + drop(a); + } + Err(l) => { + ids(&l, out); + flush(&log, out); + drop(l); + } + } + flush(&log, out); + let l: Box<[Logged]> = data.iter().map(|&x| logged(x | 1)).collect(); + let r: Result, _> = l.try_into(); + flush(&log, out); + drop(r); + flush(&log, out); + } + _ => { + let s: Box = text(data).into_boxed_str(); + let mut a: Box<[u8]> = s.clone().into_boxed_bytes(); + a.reverse(); + bytes(&a, out); + let mut b: Box<[u8]> = Box::from(s.clone()); + if let Some(x) = b.last_mut() { + *x = 0x21; + } + bytes(&b, out); + let c: Box<[u8]> = s.into(); + let mut v = c.into_vec(); + v.push(0x2e); + bytes(&v, out); + let empty: Box<[u8]> = Box::::default().into_boxed_bytes(); + bytes(&empty, out); + } + } +} diff --git a/ts/fixtures/lang/src/harness.rs b/ts/fixtures/lang/src/harness.rs index 1fa621a15..cee0fe0ab 100644 --- a/ts/fixtures/lang/src/harness.rs +++ b/ts/fixtures/lang/src/harness.rs @@ -275,6 +275,7 @@ fn cases() -> Vec<(&'static str, Case)> { ("strs::gets", strs::gets), ("borrows::maps", |_, data, out| borrows::maps(data, out)), ("locks::mapped", |_, data, out| locks::mapped(data, out)), + ("boxes::impls", boxes::impls), ("conversions::conversions", conversions::conversions), ("borrows::tries", |_, data, out| borrows::tries(data, out)), ("locks::mutex_mapped", |_, data, out| locks::mutex_mapped(data, out)), diff --git a/ts/runtime/rt.ts b/ts/runtime/rt.ts index 438eb76d6..cdf6afc19 100644 --- a/ts/runtime/rt.ts +++ b/ts/runtime/rt.ts @@ -637,6 +637,24 @@ export function sliceCloneFromSlice(dst: T[], src: ArrayLike, clone: ((val } } +/** `Clone::clone_from` of a `Box<[T]>`, `dst`: itself, by `clone_from_slice`, where the lengths match, or else a clone of `src`, made before `dst`'s elements drop in order. */ +export function boxSliceCloneFrom(dst: T[], src: T[], clone?: (value: T) => T, drop?: (item: T) => void): T[] { + if (dst.length === src.length) { + sliceCloneFromSlice(dst, src, clone, drop); + return dst; + } + const v = clone === undefined ? src.slice() : src.map(clone); + if (drop !== undefined) for (const item of dst) drop(item); + return v; +} + +/** `Clone::clone_from` of a `Box<[u8]>`, `dst`: itself, `src` copied in, where the lengths match, or else a copy of `src`. */ +export function byteBoxSliceCloneFrom(dst: ByteSlice, src: ByteSlice): ByteSlice { + if (dst.length !== src.length) return byteSliceFrom(byteSliceToArray(src)); + byteSliceCopyFromSlice(dst, src); + return dst; +} + function copyLengthFail(dst: number, src: number): never { panic(`copy_from_slice: source slice length (${src}) does not match destination slice length (${dst})`); } diff --git a/ts/src/lower/library.rs b/ts/src/lower/library.rs index 31abba0ee..7bb8a1e28 100644 --- a/ts/src/lower/library.rs +++ b/ts/src/lower/library.rs @@ -897,6 +897,8 @@ fn discards(owner: &str, name: &str) -> Option<&'static [usize]> { | ("hint", "must_use") | ("From", "from") | ("Into", "into") + | ("TryFrom", "try_from") + | ("TryInto", "try_into") // What a panic would drop, which aborts. | ("Result", "unwrap" | "expect" | "unwrap_err") | ("Vec", "insert") @@ -1378,8 +1380,8 @@ fn mapping<'tcx>( { return Some(cow_cloned(b, "IntoOwned", value, elem, span)); } - // A boxed slice clones what it borrows, moves an array's or a `Vec`'s elements, and - // owns the `Vec` a `Cow` gives up. + // A boxed slice clones what it borrows, moves an array's or a `Vec`'s elements, owns + // the `Vec` a `Cow` gives up, and takes a boxed text's bytes. if let Some(elem) = box_slice(tcx, to) { return match targ(gargs, 1).kind() { ty::Ref(_, s, _) if s.is_slice() => slice_boxed(b, value, elem, span), @@ -1388,6 +1390,8 @@ fn mapping<'tcx>( _ if cow_slice(tcx, from).is_some() => { Some(vec_boxed(cow_cloned(b, "IntoOwned", value, elem, span), elem)) } + // A `Box`'s bytes. + _ if string(tcx, from) => Some(Expr::rt("stringAsBytes", vec![value])), _ => None, }; } @@ -1435,6 +1439,37 @@ fn mapping<'tcx>( let value = b.convert(values.pop()?, from, to, span, out); return Some(Expr::rt("Ok", vec![value])); } + // A boxed slice or a `Vec` of `N` elements is a boxed array of them, and else + // itself again. + if let Some(ty::Array(elem, n)) = to.boxed_ty().map(|t| t.kind()) + && (box_slice(tcx, from) == Some(*elem) || b.t.std_of(from) == Some(Std::Vec)) + { + let n = n.try_to_target_usize(tcx)?; + let mut values = args; + let from_value = match values.pop()? { + v if v.is_path() => v, + v => b.spill(v, "boxed", out), + }; + let boxed = match b.t.std_of(from) { + Some(Std::Vec) => vec_boxed(from_value.clone(), *elem), + _ => from_value.clone(), + }; + // The bytes keep their buffer, as std keeps the allocation. + let array = match is_u8(*elem) { + true => Expr::rt("byteSliceView", vec![boxed]), + false => boxed, + }; + return Some(Expr::Cond( + Box::new( + from_value + .clone() + .member("length") + .binary("===", Expr::lit(n.to_string())), + ), + Box::new(Expr::rt("Ok", vec![array])), + Box::new(Expr::rt("Err", vec![from_value])), + )); + } let (Some(int), Some(_)) = (Int::of(to), Int::of(from)) else { return None; }; @@ -1873,7 +1908,7 @@ fn mapping<'tcx>( if is_u8(elem) { return Some(rt2(args, "byteSlice", "fill")); } - let clone = clone_into(b, elem, &name, span); + let clone = clone_into(b, elem, "slice::fill", span); let mut values = args; // A value its own clone, which nothing drops, is JS's `fill`. if matches!(&clone, Expr::Lit(l) if l == "undefined") && !b.t.drops(elem) { @@ -1901,7 +1936,7 @@ fn mapping<'tcx>( } ("slice", "clone_from_slice") => { let mut values = args; - values.push(clone_into(b, targ(gargs, 0), &name, span)); + values.push(clone_into(b, targ(gargs, 0), "slice::clone_from_slice", span)); Expr::rt("sliceCloneFromSlice", values) } ("PartialEq", "eq") if byte_slice(targ(gargs, 0)) && byte_slice(targ(gargs, 1)) => { @@ -2148,6 +2183,8 @@ fn mapping<'tcx>( let v = args.pop()?; vec_view(v, targ(gargs, 0)) } + // A box is what it holds. + ("AsRef", "as_ref") | ("AsMut", "as_mut") if b.t.std_of(targ(gargs, 0)) == Some(Std::Box) => args.pop()?, ("Deref", "deref") | ("DerefMut", "deref_mut") => { let ty = targ(gargs, 0); let v = args.pop()?; @@ -2175,7 +2212,8 @@ fn mapping<'tcx>( } // Strings. - ("String" | "str", "as_bytes") => Expr::rt("stringAsBytes", args), + // A `Box`'s bytes are their own, as `as_bytes` gives them. + ("String" | "str", "as_bytes") | ("str", "into_boxed_bytes") => Expr::rt("stringAsBytes", args), ("String", "from_utf8") => Expr::rt("stringFromUtf8", args), // `str::from_utf8` is `core::str::converts::from_utf8`, or `str`'s own. ("converts" | "str", "from_utf8") => Expr::rt("strFromUtf8", args), @@ -2507,17 +2545,36 @@ fn owned<'tcx>(b: &mut Body<'_, '_, 'tcx>, value: Expr, ty: Ty<'tcx>, span: Span } } +/// The `Box<[elem]>` that `clone_from` of `source` leaves in place of `slot`: `slot`, each +/// element written by its `clone_from` as `clone_from_slice` writes them, where the lengths +/// match, and else a clone of `source`, made before `slot`'s elements drop. +pub(super) fn box_slice_clone_from<'tcx>( + b: &mut Body<'_, '_, 'tcx>, + slot: Expr, + source: Expr, + elem: Ty<'tcx>, + span: Span, +) -> Option { + if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) { + return Some(Expr::rt("byteBoxSliceCloneFrom", vec![slot, source])); + } + let clone = clone_into(b, elem, "Box::clone_from", span); + let drop = b.t.droppers([elem], span)?; + let values = super::iter::tail(vec![slot, source], [vec![clone], drop].concat()); + Some(Expr::rt("boxSliceCloneFrom", values)) +} + /// What clones an `elem` into a slot of a slice, as `fill` and `clone_from_slice` do by /// `clone_from`: a copy for a `TrivialClone`, or else `clone_fn`, where `clone_from` does -/// as `*self = source.clone()` does. Refused for any other `clone_from`. -fn clone_into<'tcx>(b: &mut Body<'_, '_, 'tcx>, elem: Ty<'tcx>, name: &str, span: Span) -> Expr { +/// as `*self = source.clone()` does. Refused for any other `clone_from`, naming `path`. +fn clone_into<'tcx>(b: &mut Body<'_, '_, 'tcx>, elem: Ty<'tcx>, path: &str, span: Span) -> Expr { if super::iter::implements(b.t, elem, "TrivialClone") { return b.t.copy_fn(elem, span).unwrap_or_else(Expr::undefined); } if !clone_from_clones(b.t, elem) { return b.err( span, - format!("`slice::{name}` of `{elem}`, whose `clone_from` is its own, which is not supported yet"), + format!("`{path}` of `{elem}`, whose `clone_from` is its own, which is not supported yet"), ); } super::collections::clone_fn(b, elem, span) diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 4065f0dc2..699bb3f64 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -516,6 +516,21 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { self.set_borrowed(&c, old, span, out); done(Expr::undefined(), false) } + // What `clone_from` leaves where the `&mut` borrows a boxed slice or text. + ("Clone", "clone_from") + if targ(0).is_some_and(|t| { + library::box_slice(tcx, t).is_some() || t.boxed_ty().is_some_and(|t| t.is_str()) + }) => + { + let place = self.borrowed_place(&args[0].node, span, out)?; + let source = self.operand(&args[1].node, span, out); + let value = match library::box_slice(tcx, targ(0)?) { + Some(elem) => library::box_slice_clone_from(self.b, place.0.clone(), source, elem, span)?, + None => source, + }; + self.set_borrowed(&place, value, span, out); + done(Expr::undefined(), false) + } // `*self = source.clone()`, as std's `Clone` for a `T: Clone` has `clone_from` and // its `ToOwned` has `clone_into`, cloning before it drops what it writes over. A // list's maps, as it clones each element into one of its own nodes.