diff --git a/quest/m3/README.md b/quest/m3/README.md index 0dde22cfc..3aaf07a9c 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -26,7 +26,8 @@ generic std gap is planned here even when moq never calls it. - [Waiting: a slice object for a mutable part of other values](/quest/m3/ts-slice-object.md) - waits on a moq profile showing the `Proxy` view's traps matter, then decides on an `rt.ByteSlice`-like form - [Port the workflow guard to kixelated/quest](/quest/m3/quest-workflow-guard.md) - quest's environment guard parses YAML as this repository's does - [A deque's two slices](/quest/m3/ts-deque-slices.md) - `VecDeque::as_slices` and `{:?}` of a deque's `Iter`, `IterMut`, and `Drain` translate, split where std's are -- [Places through an adaptor's argument](/quest/m3/ts-adaptor-places.md) - `iter.zip(&mut v)` of values JS copies lends places as `for x in &mut v` does +- [Comparing places](/quest/m3/ts-place-compares.md) - `a.iter_mut().eq(&mut b)` and its orderings compare the values places lend +- [std::iter::zip](/quest/m3/ts-iter-zip.md) - the free `zip` translates as `Iterator::zip` does - [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 diff --git a/quest/m3/ts-adaptor-places.md b/quest/m3/ts-adaptor-places.md deleted file mode 100644 index ce9ab36a6..000000000 --- a/quest/m3/ts-adaptor-places.md +++ /dev/null @@ -1,18 +0,0 @@ -# [S] Places through an adaptor's argument - -## Goal - -[rust2-ts](/ts/README.md) translates an adaptor given a `&mut` to a collection of values -JS copies, as `iter.zip(&mut v)` or `iter.chain(&mut res)` where `iter` yields no places, -proven by a differential test against native Rust that writes through each. - -## Plan - -Found writing [Result's iterators](https://github.com/moq-dev/rust2/pull/381): such a -call is refused as "a mutable reference to a value JS copies, which its mapping would -copy", while `for x in &mut v` and `v.iter_mut().zip(&mut w)` translate. `std_places` in -`ts/src/lower/mir/call.rs` accepts places only from a listed mapping or from what the -adaptor's receiver yields. Accepting every generic `IntoIterator` argument is too broad: -`iter.eq(&mut v)` would compare places, not values, so check each adaptor's mapping. - -Public API: the translator's output. diff --git a/quest/m3/ts-iter-zip.md b/quest/m3/ts-iter-zip.md new file mode 100644 index 000000000..5a2155e09 --- /dev/null +++ b/quest/m3/ts-iter-zip.md @@ -0,0 +1,14 @@ +# [XS] std::iter::zip + +## Goal + +[rust2-ts](/ts/README.md) translates `std::iter::zip(a, b)`, including of a `&mut` to a +collection of values JS copies, as `a.into_iter().zip(b)` does, proven by a differential +test against native Rust. + +## Plan + +Found writing [places through an adaptor's argument](https://github.com/moq-dev/rust2/pull/391): +the free function is an "unsupported call", while `Iterator::zip` maps. + +Public API: the translator's output. diff --git a/quest/m3/ts-place-compares.md b/quest/m3/ts-place-compares.md new file mode 100644 index 000000000..830ecf5f8 --- /dev/null +++ b/quest/m3/ts-place-compares.md @@ -0,0 +1,17 @@ +# [S] Comparing places + +## Goal + +[rust2-ts](/ts/README.md) compares `&mut` to values JS copies by the values they lend, as +`a.iter_mut().eq(&mut b)`, `a.iter_mut().lt(&mut b)`, and `a.iter_mut().cmp(&mut b)` do, +proven by a differential test against native Rust. + +## Plan + +Found writing [places through an adaptor's argument](https://github.com/moq-dev/rust2/pull/391): +an iterator of places compared with another is refused as "comparing `&mut u8` as a key" +or "ordering `&mut u8`", while one of values compared with places already reads each +place's value. `cmp_fn` and `eq_fn` in `ts/src/lower/collections.rs` peel shared +references but not a place. + +Public API: the translator's output. diff --git a/ts/fixtures/lang/expected/core/result.ts b/ts/fixtures/lang/expected/core/result.ts index 5cab658e1..c684cb71a 100644 --- a/ts/fixtures/lang/expected/core/result.ts +++ b/ts/fixtures/lang/expected/core/result.ts @@ -488,6 +488,13 @@ export function err$0jf6smd(self: rt.Result): rt.ParseI return self.error; } +export function err$1nqzzwu(self: rt.Result): rt.u8 | undefined { + if (self.ok) { + return undefined; + } + return self.error; +} + export function err$05f656d(self: rt.Result): rt.u16 | undefined { if (self.ok) { return undefined; @@ -523,6 +530,13 @@ export function asRef$1glrxiq(self: rt.Result): rt.Result): rt.Result { + if (self.ok) { + return rt.Ok(self.value); + } + return rt.Err(self.error); +} + export function map$0d4vpmj(self: rt.Result, op: (a0: rt.i64[]) => rt.usize): rt.Result { if (self.ok) { return rt.Ok(op(self.value)); diff --git a/ts/fixtures/lang/expected/harness.ts b/ts/fixtures/lang/expected/harness.ts index f176e94c4..a4b115d32 100644 --- a/ts/fixtures/lang/expected/harness.ts +++ b/ts/fixtures/lang/expected/harness.ts @@ -46,7 +46,7 @@ import { nested as nested_nested } from "./nested.ts"; import { objects } from "./objects.ts"; import { orders, bytes as orders_bytes, tuples as orders_tuples, held as orders_held, btrees } from "./orders.ts"; import { lists as pointers_lists, shared as pointers_shared, places as pointers_places, unchecked, nested as pointers_nested, copies as pointers_copies, sentinels, cells as pointers_cells, rebound as pointers_rebound, lapsed, writers, made as pointers_made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken as pointers_taken, bytes as pointers_bytes, loaded, reattached, partWritten, aimed } from "./pointers.ts"; -import { refs, toRefs } from "./refs.ts"; +import { refs, toRefs, adapted as refs_adapted } from "./refs.ts"; import { results as results_results } from "./results.ts"; import { returns } from "./returns.ts"; import { u8s as saturates_u8s, u16s as saturates_u16s, u32s as saturates_u32s, u64s as saturates_u64s, u128s as saturates_u128s, usizes as saturates_usizes, i8s as saturates_i8s, i16s as saturates_i16s, i32s as saturates_i32s, i64s as saturates_i64s, i128s as saturates_i128s, isizes as saturates_isizes } from "./saturates.ts"; @@ -258,26 +258,28 @@ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) runs(data89, out90); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::guards", (_2190: rt.u8, data90: rt.ByteSlice, out91: rt.ByteVec): undefined => { guards(data90, out91); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["results::results", results_results as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fields::fields", fields_fields as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["strs::gets", gets as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::maps", (_2191: rt.u8, data91: rt.ByteSlice, out92: rt.ByteVec): undefined => { - 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], ["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], ["results::results", results_results as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fields::fields", fields_fields as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["strs::gets", gets as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["refs::adapted", (_2191: rt.u8, data91: rt.ByteSlice, out92: rt.ByteVec): undefined => { + refs_adapted(data91, out92); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::maps", (_2192: rt.u8, data92: rt.ByteSlice, out93: rt.ByteVec): undefined => { + borrows_maps(data92, out93); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locks::mapped", (_2193: rt.u8, data93: rt.ByteSlice, out94: rt.ByteVec): undefined => { + mapped(data93, out94); + }] 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", (_2194: rt.u8, data94: rt.ByteSlice, out95: rt.ByteVec): undefined => { + tries(data94, out95); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locks::mutex_mapped", (_2195: rt.u8, data95: rt.ByteSlice, out96: rt.ByteVec): undefined => { + mutexMapped(data95, out96); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locks::printed", (_2196: rt.u8, data96: rt.ByteSlice, out97: rt.ByteVec): undefined => { + locks_printed(data96, out97); }] 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 => { - leaks(data97, out98); + }] 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", (_2197: rt.u8, data97: rt.ByteSlice, out98: rt.ByteVec): undefined => { + downgraded(data97, out98); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::leaks", (_2198: rt.u8, data98: rt.ByteSlice, out99: rt.ByteVec): undefined => { + leaks(data98, out99); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined]]; } diff --git a/ts/fixtures/lang/expected/refs.ts b/ts/fixtures/lang/expected/refs.ts index 71e1a2200..028122014 100644 --- a/ts/fixtures/lang/expected/refs.ts +++ b/ts/fixtures/lang/expected/refs.ts @@ -2,8 +2,10 @@ import * as rt from "./rt.ts"; import { copy$Option_tuple2_u8_u8, copy$array_u32 } from "./lib.ts"; +import { madeOut$u8 } from "./cells.ts"; import { thenSome$0xksuo2, then$0ozmvid } from "./core/bool.ts"; import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, unwrapOr$0x6fy3l as option_unwrapOr$0x6fy3l, unwrapOr$11c0we2 as option_unwrapOr$11c0we2, unwrapOrDefault$165pyh5 as option_unwrapOrDefault$165pyh5, unwrapOrDefault$0wx44sw as option_unwrapOrDefault$0wx44sw, map$1k7m6zz as option_map$1k7m6zz, map$1fmzefe as option_map$1fmzefe, map$0cs071d as option_map$0cs071d, mapOr$1xztl8h as option_mapOr$1xztl8h, mapOr$0lcrgk1 as option_mapOr$0lcrgk1, mapOr$1giiemz as option_mapOr$1giiemz, mapOr$1surhce as option_mapOr$1surhce, mapOr$0mmpeqx as option_mapOr$0mmpeqx, mapOr$1m2xzon as option_mapOr$1m2xzon, andThen$101gy6h as option_andThen$101gy6h, filter$033ccod, or$1loyskh as option_or$1loyskh } from "./core/option.ts"; +import { err$1nqzzwu, asRef$0ssaoss } from "./core/result.ts"; import * as Counts from "./refs.Counts.ts"; import * as Cursor from "./refs.Cursor.ts"; import type * as Fetch from "./refs.Fetch.ts"; @@ -1377,4 +1379,92 @@ export function toRefs(data: rt.ByteSlice, out: rt.ByteVec): void { } } +/** `Ok` for an even byte, else `Err`. */ +export function parity(b: rt.u8): rt.u8 | undefined { + if (!(rt.u8Rem(b, 2) === 0)) { + madeOut$u8.err = b; + return undefined; + } + return b; +} + +/** + * A `&mut` to a `Vec`, an `Option`, or a `Result` of bytes that an adaptor is given, + * beside items of no places, lends each element as its place to write through. One a + * consumer is given compares as values. + */ +export function adapted(data: rt.ByteSlice, out: rt.ByteVec): void { + const v: rt.ByteVec = rt.byteSliceToByteVec(data); + let opt: rt.u8 | undefined = rt.byteSliceFirst(data); + const part = parity(option_unwrapOr$0r2bmpr(rt.byteSliceGet(data, 1), 4)); + let res: rt.Result = part === undefined ? rt.Err(madeOut$u8.err as rt.u8) : rt.Ok(part); + const iter: rt.Iter<[rt.u8, rt.Place]> = rt.iterZip(rt.iterRev(rt.iterSlice(rt.byteSliceView(data))), rt.iterSliceMut(rt.byteVecView(v)), "len"); + while (true) { + const _16: [rt.u8, rt.Place] | rt.Done = iter.next(); + if (_16 === rt.DONE) { + break; + } + _16[1].v = rt.intRotateLeft(rt.u8WrappingAdd(_16[1].v, _16[0]), 1, 8, false); + } + const iter2: rt.Iter<[rt.u8, rt.Place]> = rt.iterZip(new rt.RangeFrom(1, rt.u8Step), rt.iterOption(rt.optionAsMut(rt.place(() => opt, (value) => opt = value))), "len"); + while (true) { + const _29: [rt.u8, rt.Place] | rt.Done = iter2.next(); + if (_29 === rt.DONE) { + break; + } + _29[1].v = _29[1].v ^ ((_29[0] << 6) & 0xff); + } + const iter3: rt.Iter<[rt.u8, rt.Place]> = rt.iterZip(new rt.RangeFrom(3, rt.u8Step), rt.iterOption(rt.resultAsMut(rt.place(() => res, (value2) => res = value2))), "len"); + while (true) { + const _42: [rt.u8, rt.Place] | rt.Done = iter3.next(); + if (_42 === rt.DONE) { + break; + } + _42[1].v = rt.u8WrappingMul(_42[1].v, _42[0]); + } + const iter4: rt.Iter<[[rt.u8, rt.Place], rt.Place]> = rt.iterZip(rt.iterZip(rt.iterSlice(rt.byteSliceView(rt.byteVecSlice(v))), rt.iterOption(rt.optionAsMut(rt.place(() => opt, (value3) => opt = value3))), "len"), rt.iterOption(rt.resultAsMut(rt.place(() => res, (value4) => res = value4))), "len"); + while (true) { + const _58: [[rt.u8, rt.Place], rt.Place] | rt.Done = iter4.next(); + if (_58 === rt.DONE) { + break; + } + const swapped = _58[0][1].v; + _58[0][1].v = _58[1].v; + _58[1].v = swapped; + _58[1].v = _58[1].v ^ _58[0][0]; + } + const iter5: rt.Iter> = rt.iterChain(rt.iterChain(rt.iterChain(rt.iterOption(undefined as rt.Place | undefined), rt.iterSliceMut(rt.byteVecView(v))), rt.iterOption(rt.optionAsMut(rt.place(() => opt, (value5) => opt = value5)))), rt.iterOption(rt.resultAsMut(rt.place(() => res, (value6) => res = value6)))); + while (true) { + const _76: rt.Place | rt.Done = iter5.next(); + if (_76 === rt.DONE) { + break; + } + _76.v = rt.u8WrappingAdd(_76.v, 5); + } + rt.iterExtendBytes(out, rt.iterSlice(rt.byteVecView(v)), undefined, true); + rt.iterExtendBytes(out, rt.iterOption(opt), undefined, true); + rt.iterExtendBytes(out, rt.iterChain(rt.iterResult(res), rt.iterOption(err$1nqzzwu(asRef$0ssaoss(res)))), undefined, true); + const copy: rt.ByteVec = rt.byteVecFrom(rt.byteVecView(v)); + let other: rt.u8 | undefined = option_map$0cs071d(opt, (x: rt.u8): rt.u8 => { + return x ^ 1; + }); + const _114: boolean = rt.iterEq(rt.iterOption(opt), rt.iterOption(rt.optionAsMut(rt.place(() => other, (value7) => other = value7))), (a04: rt.u8, a14: rt.Place): boolean => { + return a04 === a14.v; + }, true); + const _118: boolean = rt.iterEq(rt.iterResult(res), rt.iterOption(rt.optionAsMut(rt.place(() => opt, (value8) => opt = value8))), (a05: rt.u8, a15: rt.Place): boolean => { + return a05 === a15.v; + }, true); + rt.iterExtendBytes(out, rt.iterSlice(rt.bytesMap([rt.iterEq(rt.iterSlice(rt.byteSliceView(rt.byteVecSlice(copy))), rt.iterSliceMut(rt.byteVecView(v)), (a0: rt.u8, a1: rt.Place): boolean => { + return a0 === a1.v; + }, true), !rt.iterEq(rt.iterSlice(rt.byteSliceView(rt.byteVecSlice(copy))), rt.iterSliceMut(rt.byteVecView(v)), (a02: rt.u8, a12: rt.Place): boolean => { + return a02 === a12.v; + }, true), rt.iterEq(rt.iterSkip(rt.iterSlice(rt.byteSliceView(rt.byteVecSlice(copy))), 1), rt.iterSliceMut(rt.byteVecView(v)), (a03: rt.u8, a13: rt.Place): boolean => { + return a03 === a13.v; + }, true), _114, _118, !rt.iterEq(rt.iterSlice(rt.byteSliceView(data)), rt.iterOption(rt.resultAsMut(rt.place(() => res, (value9) => res = value9))), (a06: rt.u8, a16: rt.Place): boolean => { + return a06 === a16.v; + }, true)], (a07: boolean): rt.u8 => { + return a07 ? 1 : 0; + })), undefined, true); +} + //# sourceMappingURL=refs.ts.map diff --git a/ts/fixtures/lang/src/harness.rs b/ts/fixtures/lang/src/harness.rs index 252679af8..0a4253ba2 100644 --- a/ts/fixtures/lang/src/harness.rs +++ b/ts/fixtures/lang/src/harness.rs @@ -273,6 +273,7 @@ fn cases() -> Vec<(&'static str, Case)> { ("results::results", results::results), ("fields::fields", fields::fields), ("strs::gets", strs::gets), + ("refs::adapted", |_, data, out| refs::adapted(data, out)), ("borrows::maps", |_, data, out| borrows::maps(data, out)), ("locks::mapped", |_, data, out| locks::mapped(data, out)), ("boxes::impls", boxes::impls), diff --git a/ts/fixtures/lang/src/refs.rs b/ts/fixtures/lang/src/refs.rs index 0fb13fcee..85cce4961 100644 --- a/ts/fixtures/lang/src/refs.rs +++ b/ts/fixtures/lang/src/refs.rs @@ -935,3 +935,47 @@ pub fn to_refs(data: &[u8], out: &mut Vec) { out.push(call_twice(Box::new(&mut counted))); out.push(call_twice(Box::new(&mut &mut counted))); } + +/// `Ok` for an even byte, else `Err`. +fn parity(b: u8) -> Result { + if b % 2 == 0 { Ok(b) } else { Err(b) } +} + +/// A `&mut` to a `Vec`, an `Option`, or a `Result` of bytes that an adaptor is given, +/// beside items of no places, lends each element as its place to write through. One a +/// consumer is given compares as values. +pub fn adapted(data: &[u8], out: &mut Vec) { + let mut v = data.to_vec(); + let mut opt = data.first().copied(); + let mut res = parity(data.get(1).copied().unwrap_or(4)); + for (&b, x) in data.iter().rev().zip(&mut v) { + *x = x.wrapping_add(b).rotate_left(1); + } + for (i, x) in (1u8..).zip(&mut opt) { + *x ^= i << 6; + } + for (i, x) in (3u8..).zip(&mut res) { + *x = x.wrapping_mul(i); + } + for ((x, y), z) in v.iter().zip(&mut opt).zip(&mut res) { + std::mem::swap(y, z); + *z ^= *x; + } + for x in None.into_iter().chain(&mut v).chain(&mut opt).chain(&mut res) { + *x = x.wrapping_add(5); + } + out.extend(&v); + out.extend(opt); + out.extend(res.iter().chain(res.as_ref().err())); + let copy = v.clone(); + let mut other = opt.map(|x| x ^ 1); + let all = [ + copy.iter().eq(&mut v), + copy.iter().ne(&mut v), + copy.iter().skip(1).eq(&mut v), + opt.iter().eq(&mut other), + res.iter().eq(&mut opt), + data.iter().ne(&mut res), + ]; + out.extend(all.map(u8::from)); +} diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 699bb3f64..0b8443030 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -338,10 +338,33 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { return None; } let t = &*self.b.t; + // What `IntoIterator::into_iter` lends of a `&mut` to a collection, each element as + // its place. + let into_places = |ty: Ty<'tcx>| { + let ty = crate::lower::iter::unbox_ref(tcx, ty); + ty.ref_mutability() == Some(ty::Mutability::Mut) + && match ty.peel_refs().kind() { + ty::Slice(_) | ty::Array(..) => true, + _ => matches!( + t.std_of(ty.peel_refs()), + Some( + Std::Vec + | Std::Option + | Std::Result + | Std::HashMap + | Std::HashTable + | Std::BTreeMap + | Std::VecDeque + | Std::LinkedList + ) + ), + } + }; // Mappings that lend an element JS copies as its place, as a map's `get_mut` does, the // item a `Peekable` peeked, the place a `&mut dyn Any` wraps, a `RefCell` key's value, - // a `Result`'s payload, the value of an `Arc`, a lock, or a cell, or what a write guard - // lends its map or its leak. + // a `Result`'s payload, the value of an `Arc`, a lock, or a cell, what a write guard + // lends its map or its leak, or what `zip` makes of the collection it is given by + // `into_iter`. let lends = match library::key(tcx, def_id) { Some((owner, name)) => match (owner.as_str(), name.as_str()) { ("slice", "get_mut" | "first_mut" | "last_mut" | "iter_mut" | "split_first_mut" | "split_last_mut") @@ -358,26 +381,8 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { "RefMut" | "MutexGuard" | "MappedMutexGuard" | "RwLockWriteGuard" | "MappedRwLockWriteGuard", "map" | "filter_map" | "try_map" | "map_split", ) => true, - ("IntoIterator", "into_iter") => gargs.types().next().is_some_and(|ty| { - let ty = crate::lower::iter::unbox_ref(tcx, ty); - ty.ref_mutability() == Some(ty::Mutability::Mut) - && match ty.peel_refs().kind() { - ty::Slice(_) | ty::Array(..) => true, - _ => matches!( - t.std_of(ty.peel_refs()), - Some( - Std::Vec - | Std::Option - | Std::Result - | Std::HashMap - | Std::HashTable - | Std::BTreeMap - | Std::VecDeque - | Std::LinkedList - ) - ), - } - }), + ("IntoIterator", "into_iter") => gargs.types().next().is_some_and(into_places), + ("Iterator", "zip") => gargs.types().nth(1).is_some_and(into_places), _ => false, }, None => false,