diff --git a/quest/m3/README.md b/quest/m3/README.md index bd7ae0e67..313c2d143 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -30,7 +30,6 @@ generic std gap is planned here even when moq never calls it. - [A vector's exact capacity as it shrinks](/quest/m3/ts-vec-exact-capacity.md) - a deque made from a `Vec` splits its slices where std's does - [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 - [A slice's chunks as mutable arrays](/quest/m3/ts-slice-conversions.md) - `as_chunks_mut` and `as_rchunks_mut` translate, writes landing in the slice -- [Repeated values that drop](/quest/m3/ts-vec-from-elem-drops.md) - `vec![x; n]` translates where `x` drops, cloned and dropped as std's `from_elem` does - [A vector's capacity where clone_from writes it in a container](/quest/m3/ts-clone-from-capacity.md) - a `Vec` an array, slice, `Option`, or list clones over keeps its capacity as std's does - [Comparing pointers to arrays of other values than bytes](/quest/m3/ts-array-ref-identity.md) - `ptr::eq` of pointers to a `[T; N]` of non-bytes translates, as a slice lends them too - [An Arc's pointer](/quest/m3/ts-arc-pointers.md) - `Arc::as_ptr` and `Rc::as_ptr` translate, comparable by `ptr::addr_eq` diff --git a/quest/m3/ts-vec-from-elem-drops.md b/quest/m3/ts-vec-from-elem-drops.md deleted file mode 100644 index 60bfedac6..000000000 --- a/quest/m3/ts-vec-from-elem-drops.md +++ /dev/null @@ -1,20 +0,0 @@ -# [S] Repeated values that drop - -## Goal - -[rust2-ts](/ts/README.md) translates `vec![x; n]` where `x` drops, as std's `from_elem` -makes it: `n - 1` clones of `x` in order, then `x` itself, or `x` dropped where `n` is 0, -proven by differential tests against native Rust. - -## Plan - -Found writing `lang/clones`' `clone_froms` case: `vec![row; 4]` of a `Vec` -and of a `[Logged; 1]` refuse as "passing `T`, which drops, to `vec::from_elem`". -`Vec::resize`'s runtime already clones `n - 1` times and moves the value into the last -slot, which may serve here. No consumer calls it yet; it is planned as part of std. - -Public API: the translator's output. - -## Related - -- [Fresh writes through a zip, a copy, and a repeat](/quest/m3/ts-fresh-shapes.md) - `vec![x; n]` over a `Copy` type diff --git a/ts/fixtures/lang/clones/expected/clones.ts b/ts/fixtures/lang/clones/expected/clones.ts index be9046b1f..6b01e3656 100644 --- a/ts/fixtures/lang/clones/expected/clones.ts +++ b/ts/fixtures/lang/clones/expected/clones.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { drop$Vec_clones_Logged, drop$array1_clones_Logged, drop$array2_clones_Kept, drop$array2_clones_Logged, drop$clones_Slot, drop$tuple2_Option_Vec_clones_Logged_Option_Vec_clones_Logged, drop$tuple2_Vec_array2_clones_Logged_array2_array2_clones_Logged, drop$tuple2_array2_array2_clones_Logged_array2_array2_clones_Logged, drop$tuple2_tuple3_array2_clones_Logged_String_clones_Logged_clones_Pair, drop$tuple3_Option_array2_clones_Logged_Option_array2_clones_Logged_Option_array2_clones_Logged, drop$tuple3_Vec_Vec_clones_Logged_Vec_Vec_clones_Logged_Vec_Vec_clones_Logged, drop$tuple3_Vec_array2_clones_Logged_Vec_array2_clones_Logged_array2_clones_Logged, drop$tuple3_Vec_clones_Logged_Vec_clones_Logged_Vec_clones_Logged, drop$tuple3_array0_clones_Logged_array0_clones_Logged_array3_clones_Logged, drop$tuple3_array2_clones_Logged_String_clones_Logged, drop$tuple3_array3_clones_Logged_array3_clones_Logged_array3_clones_Logged, drop$tuple4_boxed_Box_slice_clones_Logged_boxed_Box_slice_clones_Logged_boxed_Box_slice_array1_clones_Logged_boxed_Box_slice_array1_clones_Logged, drop$tuple7_Vec_clones_Kept_Vec_clones_Kept_array1_array2_clones_Kept_Option_clones_Kept_array2_clones_Slot_collections_LinkedList_clones_Kept_collections_LinkedList_clones_Kept } from "./lib.ts"; +import { drop$Vec_clones_Logged, drop$array1_clones_Logged, drop$array2_clones_Kept, drop$array2_clones_Logged, drop$clones_Slot, drop$tuple2_Option_Vec_clones_Logged_Option_Vec_clones_Logged, drop$tuple2_Vec_array2_clones_Logged_array2_array2_clones_Logged, drop$tuple2_Vec_clones_Logged_Vec_Vec_clones_Logged, drop$tuple2_Vec_clones_Logged_Vec_clones_Logged, drop$tuple2_array2_array2_clones_Logged_array2_array2_clones_Logged, drop$tuple2_clones_Logged_String, drop$tuple2_tuple3_array2_clones_Logged_String_clones_Logged_clones_Pair, drop$tuple3_Option_array2_clones_Logged_Option_array2_clones_Logged_Option_array2_clones_Logged, drop$tuple3_Vec_Option_clones_Logged_Vec_Option_clones_Logged_Vec_tuple2_clones_Logged_String, drop$tuple3_Vec_Vec_clones_Logged_Vec_Vec_clones_Logged_Vec_Vec_clones_Logged, drop$tuple3_Vec_array2_clones_Logged_Vec_array2_clones_Logged_array2_clones_Logged, drop$tuple3_Vec_clones_Logged_Vec_clones_Logged_Vec_clones_Logged, drop$tuple3_array0_clones_Logged_array0_clones_Logged_array3_clones_Logged, drop$tuple3_array2_clones_Logged_String_clones_Logged, drop$tuple3_array3_clones_Logged_array3_clones_Logged_array3_clones_Logged, drop$tuple4_boxed_Box_slice_clones_Logged_boxed_Box_slice_clones_Logged_boxed_Box_slice_array1_clones_Logged_boxed_Box_slice_array1_clones_Logged, drop$tuple7_Vec_clones_Kept_Vec_clones_Kept_array1_array2_clones_Kept_Option_clones_Kept_array2_clones_Slot_collections_LinkedList_clones_Kept_collections_LinkedList_clones_Kept } from "./lib.ts"; import * as Countdown from "./clones.Countdown.ts"; import * as Fussy from "./clones.Fussy.ts"; import * as Kept from "./clones.Kept.ts"; @@ -10,7 +10,7 @@ import * as Pair from "./clones.Pair.ts"; import * as Plan from "./clones.Plan.ts"; import * as Slot from "./clones.Slot.ts"; import { then$1uqvqbl } from "./core/bool.ts"; -import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$0y3liwu as option_map$0y3liwu, mapOr$1yabfom as option_mapOr$1yabfom, mapOr$1surhce as option_mapOr$1surhce, mapOr$02k4ito as option_mapOr$02k4ito, clone$0bhdqd8 as option_clone$0bhdqd8 } from "./core/option.ts"; +import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$0y3liwu as option_map$0y3liwu, mapOr$1yabfom as option_mapOr$1yabfom, mapOr$1surhce as option_mapOr$1surhce, mapOr$02k4ito as option_mapOr$02k4ito, clone$0bhdqd8 as option_clone$0bhdqd8, clone$1jx0028 as option_clone$1jx0028 } from "./core/option.ts"; /** Puts what was logged since the last flush, after a marker. */ export function flush(log: rt.RefCell, out: rt.ByteVec): void { @@ -8086,4 +8086,163 @@ export function cloneFroms(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): vo log[Symbol.dispose](); } +/** + * `vec![x; n]` of values that clone and drop themselves, as std's `from_elem` makes it: + * `n - 1` clones of `x` in order, then `x` itself, or `x` dropped where `n` is 0, of a + * `Logged`, its own `Clone`, an array, an `Option`, a tuple, nested `Vec`s, and through a + * generic function, with `n` from the input. + */ +export function repeats(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void { + let slot!: Slot.Slot; + const _11: (a0: Logged.Logged[], a1: rt.ByteVec) => undefined = (a: Logged.Logged[], out2: rt.ByteVec): undefined => { + rt.iterExtendBytes(out2, rt.iterMap(rt.iterSlice(a), Logged.id), undefined, true); + }; + const log: rt.Arc> = new rt.Arc(new rt.RefCell(new rt.ByteVec())); + const logged: (a0: rt.u8) => Logged.Logged = (id: rt.u8): Logged.Logged => { + return new Logged.Logged(id, rt.arcClone(log)); + }; + const at: (a0: rt.usize) => rt.u8 = (i: rt.usize): rt.u8 => { + return option_unwrapOr$0r2bmpr(rt.byteSliceGet(data, i), rt.u8WrappingMul(i & 0xff, 48)) & 126; + }; + const n: (a0: rt.usize) => rt.usize = (i2: rt.usize): rt.usize => { + return rt.u8Rem(option_unwrapOr$0r2bmpr(rt.byteSliceGet(data, i2), i2 & 0xff), 5) as rt.usize; + }; + const _12: rt.u8 = rt.u8Rem(kind, 8); + if (_12 === 0) { + const part17 = at(0); + const v: Logged.Logged[] = rt.vecFromElem(logged(part17), n(1), Logged.clone$Clone, rt.drop); + flush(log.value, out); + _11(v, out); + rt.byteVecPush(out, rt.vecCapacity(v, 8) & 0xff); + rt.dropAll(v); + flush(log.value, out); + } else if (_12 === 1) { + const row: Logged.Logged[] = rt.vecWithCapacity(6, 8); + const _43: rt.Iter = rt.iterMap(new rt.Range(0, n(1)), (i3: rt.usize): Logged.Logged => { + return logged(at(rt.usizeAdd(2, i3))); + }); + rt.iterExtend(row, _43, undefined, 8, true); + const v2: Logged.Logged[][] = rt.vecFromElem(row, n(0), (a04: Logged.Logged[]): Logged.Logged[] => { + return rt.vecExact(a04.map(Logged.clone$Clone), 8); + }, drop$Vec_clones_Logged); + flush(log.value, out); + for (const _62 of v2) { + _11(_62, out); + rt.iterExtendBytes(out, rt.iterSlice(Uint8Array.of(254, rt.vecCapacity(_62, 8) & 0xff)), undefined, true); + } + rt.dropAll(v2, drop$Vec_clones_Logged); + flush(log.value, out); + } else if (_12 === 2) { + const part13 = at(0); + const _80: Logged.Logged = logged(part13); + const part14 = at(1); + const v3: Logged.Logged[][] = rt.vecFromElem([_80, logged(part14)], n(2), (a03: Logged.Logged[]): Logged.Logged[] => { + return a03.map(Logged.clone$Clone); + }, drop$array2_clones_Logged); + flush(log.value, out); + rt.iterForEach(rt.iterSlice(v3), (row3: Logged.Logged[]): undefined => { + _11(row3, out); + }); + rt.dropAll(v3, drop$array2_clones_Logged); + flush(log.value, out); + } else if (_12 === 3) { + const part11 = at(0); + const v4: Kept.Kept[] = rt.vecFromElem(new Kept.Kept(logged(part11)), n(1), Kept.clone$Clone, rt.drop); + flush(log.value, out); + rt.iterExtendBytes(out, rt.iterMap(rt.iterSlice(v4), (k: Kept.Kept): rt.u8 => { + return Logged.id(k._0); + }), undefined, true); + rt.dropAll(v4); + flush(log.value, out); + if (rt.u8Rem(at(2), 3) === 0) { + slot = { tag: "Empty" } as Slot.Slot; + } else { + const part12 = at(3); + slot = { tag: "Held", _0: new Kept.Kept(logged(part12)) } as Slot.Slot; + } + const slots: Slot.Slot[] = rt.vecFromElem(slot, n(4), Slot.clone$Clone, drop$clones_Slot); + flush(log.value, out); + rt.dropAll(slots, drop$clones_Slot); + flush(log.value, out); + } else if (_12 === 4) { + const part9 = at(0); + const some: Array = rt.vecFromElem(logged(part9), n(1), option_clone$1jx0028, rt.drop); + const none: Array = rt.vecFromElem(undefined as Logged.Logged | undefined, n(2), option_clone$1jx0028, rt.drop); + flush(log.value, out); + rt.iterExtendBytes(out, rt.iterMap(rt.iterSlice(some), (x: Logged.Logged | undefined): rt.u8 => { + return option_mapOr$1yabfom(x, 254, Logged.id); + }), undefined, true); + rt.byteVecPush(out, none.length & 0xff); + const part10 = at(3); + const _194: rt.u8 = at(4); + const pairs: [Logged.Logged, string][] = rt.vecFromElem([logged(part10), rt.fmtToString((f: rt.Formatter) => rt.u8Display(_194, f))] as [Logged.Logged, string], n(5), (a02: [Logged.Logged, string]): [Logged.Logged, string] => { + return rt.tupleClone(a02, [Logged.clone$Clone, undefined]); + }, drop$tuple2_clones_Logged_String); + flush(log.value, out); + rt.iterForEach(rt.iterSlice(pairs), (_2: [Logged.Logged, string]): undefined => { + rt.iterExtendBytes(out, rt.iterChain(rt.strBytes(_2[1]), rt.iterSlice(Uint8Array.of(Logged.id(_2[0])))), undefined, true); + }); + drop$tuple3_Vec_Option_clones_Logged_Vec_Option_clones_Logged_Vec_tuple2_clones_Logged_String([some, none, pairs] as [Array, Array, [Logged.Logged, string][]]); + flush(log.value, out); + } else if (_12 === 5) { + const part8 = at(0); + const v5: Logged.Logged[][] = rt.vecFromElem(rt.vecFromElem(logged(part8), n(1), Logged.clone$Clone, rt.drop), n(2), (a0: Logged.Logged[]): Logged.Logged[] => { + return rt.vecExact(a0.map(Logged.clone$Clone), 8); + }, drop$Vec_clones_Logged); + flush(log.value, out); + rt.iterForEach(rt.iterSlice(v5), (row2: Logged.Logged[]): undefined => { + _11(row2, out); + }); + rt.dropAll(v5, drop$Vec_clones_Logged); + flush(log.value, out); + } else if (_12 === 6) { + const count: () => rt.usize = (): rt.usize => { + const _510: rt.RefCell = rt.refCellBorrowMut(log.value); + rt.byteVecPush(_510.value, 251); + rt.refMutRelease(_510); + return n(1); + }; + const part5 = at(0); + const v6: Logged.Logged[] = rt.vecFromElem(logged(part5), count(), Logged.clone$Clone, rt.drop); + const _255: rt.RefCell = rt.refCellBorrowMut(log.value); + rt.byteVecPush(_255.value, 250); + rt.refMutRelease(_255); + flush(log.value, out); + _11(v6, out); + const part7 = at(2); + const empty: Logged.Logged[] = rt.vecFromElem(logged(part7), 0, Logged.clone$Clone, rt.drop); + const _276: rt.RefCell = rt.refCellBorrowMut(log.value); + rt.byteVecPush(_276.value, 250); + rt.refMutRelease(_276); + flush(log.value, out); + drop$tuple2_Vec_clones_Logged_Vec_clones_Logged([v6, empty] as [Logged.Logged[], Logged.Logged[]]); + flush(log.value, out); + } else { + const part = at(0); + const v7: Logged.Logged[] = repeated$clones_Logged(logged(part), n(1)); + flush(log.value, out); + _11(v7, out); + const part3 = at(2); + const _308: Logged.Logged = logged(part3); + const part4 = at(3); + const rows: Logged.Logged[][] = repeated$Vec_clones_Logged([_308, logged(part4)], n(4)); + flush(log.value, out); + drop$tuple2_Vec_clones_Logged_Vec_Vec_clones_Logged([v7, rows] as [Logged.Logged[], Logged.Logged[][]]); + flush(log.value, out); + } + log[Symbol.dispose](); +} + +/** `vec![x; n]` where only its bound says `x` clones. */ +export function repeated$Vec_clones_Logged(x: Logged.Logged[], n: rt.usize): Logged.Logged[][] { + return rt.vecFromElem(x, n, (a0: Logged.Logged[]): Logged.Logged[] => { + return rt.vecExact(a0.map(Logged.clone$Clone), 8); + }, drop$Vec_clones_Logged); +} + +/** `vec![x; n]` where only its bound says `x` clones. */ +export function repeated$clones_Logged(x: Logged.Logged, n: rt.usize): Logged.Logged[] { + return rt.vecFromElem(x, n, Logged.clone$Clone, rt.drop); +} + //# sourceMappingURL=clones.ts.map diff --git a/ts/fixtures/lang/clones/expected/core/option.ts b/ts/fixtures/lang/clones/expected/core/option.ts index 129c8e894..a7a2bcf20 100644 --- a/ts/fixtures/lang/clones/expected/core/option.ts +++ b/ts/fixtures/lang/clones/expected/core/option.ts @@ -45,4 +45,11 @@ export function clone$0bhdqd8(self: Logged.Logged[] | undefined): Logged.Logged[ return self.map(Logged.clone$Clone); } +export function clone$1jx0028(self: Logged.Logged | undefined): Logged.Logged | undefined { + if (self === undefined) { + return undefined; + } + return Logged.clone$Clone(self); +} + //# sourceMappingURL=option.ts.map diff --git a/ts/fixtures/lang/clones/expected/lib.ts b/ts/fixtures/lang/clones/expected/lib.ts index f176d9875..b842b1cd2 100644 --- a/ts/fixtures/lang/clones/expected/lib.ts +++ b/ts/fixtures/lang/clones/expected/lib.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { iters, arrays, cloneFroms } from "./clones.ts"; +import { iters, arrays, cloneFroms, repeats } from "./clones.ts"; import type * as Kept from "./clones.Kept.ts"; import type * as Logged from "./clones.Logged.ts"; import type * as Pair from "./clones.Pair.ts"; @@ -39,11 +39,25 @@ export function drop$tuple2_Vec_array2_clones_Logged_array2_array2_clones_Logged rt.dropAll(_1[1], drop$array2_clones_Logged); } +export function drop$tuple2_Vec_clones_Logged_Vec_Vec_clones_Logged(_1: [Logged.Logged[], Logged.Logged[][]]): void { + rt.dropAll(_1[0]); + rt.dropAll(_1[1], drop$Vec_clones_Logged); +} + +export function drop$tuple2_Vec_clones_Logged_Vec_clones_Logged(_1: [Logged.Logged[], Logged.Logged[]]): void { + rt.dropAll(_1[0]); + rt.dropAll(_1[1]); +} + export function drop$tuple2_array2_array2_clones_Logged_array2_array2_clones_Logged(_1: [Logged.Logged[][], Logged.Logged[][]]): void { rt.dropAll(_1[0], drop$array2_clones_Logged); rt.dropAll(_1[1], drop$array2_clones_Logged); } +export function drop$tuple2_clones_Logged_String(_1: [Logged.Logged, string]): void { + _1[0][Symbol.dispose](); +} + export function drop$tuple2_tuple3_array2_clones_Logged_String_clones_Logged_clones_Pair(_1: [[Logged.Logged[], string, Logged.Logged], Pair.Pair]): void { drop$tuple3_array2_clones_Logged_String_clones_Logged(_1[0]); _1[1][Symbol.dispose](); @@ -55,6 +69,12 @@ export function drop$tuple3_Option_array2_clones_Logged_Option_array2_clones_Log rt.dropAll(_1[2]); } +export function drop$tuple3_Vec_Option_clones_Logged_Vec_Option_clones_Logged_Vec_tuple2_clones_Logged_String(_1: [Array, Array, [Logged.Logged, string][]]): void { + rt.dropAll(_1[0]); + rt.dropAll(_1[1]); + rt.dropAll(_1[2], drop$tuple2_clones_Logged_String); +} + export function drop$tuple3_Vec_Vec_clones_Logged_Vec_Vec_clones_Logged_Vec_Vec_clones_Logged(_1: [Logged.Logged[][], Logged.Logged[][], Logged.Logged[][]]): void { rt.dropAll(_1[0], drop$Vec_clones_Logged); rt.dropAll(_1[1], drop$Vec_clones_Logged); @@ -110,7 +130,7 @@ export function drop$tuple7_Vec_clones_Kept_Vec_clones_Kept_array1_array2_clones * their count, so a case added at the end moves no other. */ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined][] { - return [["clones::iters", iters as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["clones::arrays", arrays as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["clones::clone_froms", cloneFroms as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined]]; + return [["clones::iters", iters as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["clones::arrays", arrays as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["clones::clone_froms", cloneFroms as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["clones::repeats", repeats as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined]]; } //# sourceMappingURL=lib.ts.map diff --git a/ts/fixtures/lang/clones/src/clones.rs b/ts/fixtures/lang/clones/src/clones.rs index 65c4271a9..dd0ab2db6 100644 --- a/ts/fixtures/lang/clones/src/clones.rs +++ b/ts/fixtures/lang/clones/src/clones.rs @@ -1,7 +1,7 @@ //! Clones of std's iterators and adaptors, and of closures: each read on from where the //! original was, independently of it, the two read in turns from either end after partial //! reads, with what they own cloned and dropped as std's do. And clones of arrays, each -//! element by its own `Clone`. +//! element by its own `Clone`, and `vec![x; n]`, as std's `from_elem` clones and drops `x`. use std::cell::RefCell; use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; @@ -872,3 +872,112 @@ pub fn clone_froms(kind: u8, data: &[u8], out: &mut Vec) { } } } + +/// `vec![x; n]` of values that clone and drop themselves, as std's `from_elem` makes it: +/// `n - 1` clones of `x` in order, then `x` itself, or `x` dropped where `n` is 0, of a +/// `Logged`, its own `Clone`, an array, an `Option`, a tuple, nested `Vec`s, and through a +/// generic function, with `n` from the input. +pub fn repeats(kind: u8, data: &[u8], out: &mut Vec) { + let log = Rc::new(RefCell::new(Vec::new())); + let logged = |id: u8| Logged { id, log: log.clone() }; + let at = |i: usize| data.get(i).copied().unwrap_or((i as u8).wrapping_mul(0x30)) & 0x7e; + // Counts of 0 to 4. + let n = |i: usize| usize::from(data.get(i).copied().unwrap_or(i as u8) % 5); + let ids = |a: &[Logged], out: &mut Vec| out.extend(a.iter().map(Logged::id)); + match kind % 8 { + 0 => { + let v = vec![logged(at(0)); n(1)]; + flush(&log, out); + ids(&v, out); + out.push(v.capacity() as u8); + drop(v); + flush(&log, out); + } + // Each clone has room for exactly its elements, and the last row is the original. + 1 => { + let mut row = Vec::with_capacity(6); + row.extend((0..n(1)).map(|i| logged(at(2 + i)))); + let v = vec![row; n(0)]; + flush(&log, out); + for row in &v { + ids(row, out); + out.extend([0xfe, row.capacity() as u8]); + } + drop(v); + flush(&log, out); + } + 2 => { + let v = vec![[logged(at(0)), logged(at(1))]; n(2)]; + flush(&log, out); + v.iter().for_each(|row| ids(row, out)); + drop(v); + flush(&log, out); + } + // Its own `Clone`, which logs and clones what it holds. + 3 => { + let v = vec![Kept(logged(at(0))); n(1)]; + flush(&log, out); + out.extend(v.iter().map(|k| k.0.id())); + drop(v); + flush(&log, out); + let slot = match at(2) % 3 { + 0 => Slot::Empty, + _ => Slot::Held(Kept(logged(at(3)))), + }; + let slots = vec![slot; n(4)]; + flush(&log, out); + drop(slots); + flush(&log, out); + } + 4 => { + let some = vec![Some(logged(at(0))); n(1)]; + let none: Vec> = vec![None; n(2)]; + flush(&log, out); + out.extend(some.iter().map(|x| x.as_ref().map_or(0xfe, Logged::id))); + out.push(none.len() as u8); + let pairs = vec![(logged(at(3)), at(4).to_string()); n(5)]; + flush(&log, out); + pairs.iter().for_each(|(l, s)| out.extend(s.bytes().chain([l.id()]))); + drop((some, none, pairs)); + flush(&log, out); + } + // Nested repeats: each inner one made once, then the outer clones it whole. + 5 => { + let v = vec![vec![logged(at(0)); n(1)]; n(2)]; + flush(&log, out); + v.iter().for_each(|row| ids(row, out)); + drop(v); + flush(&log, out); + } + // The value is made before the count, whose making logs, and dropped at once where it is 0. + 6 => { + let count = || { + log.borrow_mut().push(0xfb); + n(1) + }; + let v = vec![logged(at(0)); count()]; + log.borrow_mut().push(0xfa); + flush(&log, out); + ids(&v, out); + let empty = vec![logged(at(2)); 0]; + log.borrow_mut().push(0xfa); + flush(&log, out); + drop((v, empty)); + flush(&log, out); + } + _ => { + let v = repeated(logged(at(0)), n(1)); + flush(&log, out); + ids(&v, out); + let rows = repeated(vec![logged(at(2)), logged(at(3))], n(4)); + flush(&log, out); + drop((v, rows)); + flush(&log, out); + } + } +} + +/// `vec![x; n]` where only its bound says `x` clones. +fn repeated(x: T, n: usize) -> Vec { + vec![x; n] +} diff --git a/ts/fixtures/lang/clones/src/lib.rs b/ts/fixtures/lang/clones/src/lib.rs index 8670fc49a..3c379e2e2 100644 --- a/ts/fixtures/lang/clones/src/lib.rs +++ b/ts/fixtures/lang/clones/src/lib.rs @@ -19,5 +19,6 @@ fn cases() -> Vec<(&'static str, Case)> { ("clones::iters", clones::iters), ("clones::arrays", clones::arrays), ("clones::clone_froms", clones::clone_froms), + ("clones::repeats", clones::repeats), ] } diff --git a/ts/runtime/rt.ts b/ts/runtime/rt.ts index 2864e9f00..4e73e5064 100644 --- a/ts/runtime/rt.ts +++ b/ts/runtime/rt.ts @@ -1535,14 +1535,16 @@ export function byteSliceToByteVec(s: ByteSlice): ByteVec { return v; } -/** `vec![value; n]`: `n - 1` clones of `value`, in order, then `value` itself, with room for exactly them; `clone` is `undefined` where a clone is the value. */ -export function vecFromElem(value: T, n: number, clone: ((value: T) => T) | undefined): T[] { +/** `vec![value; n]`: `n - 1` clones of `value`, in order, then `value` itself, with room for exactly them, or `value` dropped where `n` is 0; `clone` is `undefined` where a clone is the value. */ +export function vecFromElem(value: T, n: number, clone: ((value: T) => T) | undefined, drop?: (item: T) => void): T[] { const v: T[] & Capped = []; - for (let i = 1; i < n; i++) v.push(clone === undefined ? value : clone(value)); - if (n > 0) { - v.push(value); - v.cap = n; + if (n === 0) { + drop?.(value); + return v; } + for (let i = 1; i < n; i++) v.push(clone === undefined ? value : clone(value)); + v.push(value); + v.cap = n; return v; } diff --git a/ts/src/lower/library.rs b/ts/src/lower/library.rs index 9979bac44..aaa8aa344 100644 --- a/ts/src/lower/library.rs +++ b/ts/src/lower/library.rs @@ -962,6 +962,8 @@ fn discards(owner: &str, name: &str) -> Option<&'static [usize]> { // A slice's `fill`, `fill_with`, and `clone_from_slice` drop each element they write // over, and `fill` its value if there is no slot for it. ("slice", "fill" | "fill_with" | "clone_from_slice") => &[0], + // `vec![x; 0]` drops `x`. + ("vec", "from_elem") => &[0], // `get_or_init::` drops its function uncalled if the cell is set. ("OnceCell" | "OnceLock", "get_or_init" | "get_or_try_init") => &[1], // A guard's map calls its function, filtered or not. @@ -2258,7 +2260,7 @@ fn mapping<'tcx>( } ("Box", "new") => args.pop()?, // `vec![x; n]`: `n - 1` clones of `x`, then `x` itself, which the caller gives as its - // own, as it does whatever std keeps. + // own, as it does whatever std keeps, or `x` dropped where `n` is 0. ("vec", "from_elem") => { let elem = targ(gargs, 0); let mut values = args;