From da496ad4f8d74701ab24d414e2e0f3abcf08c276 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sun, 1 Nov 2026 06:37:42 -0800 Subject: [PATCH 1/3] chore(quest): claim quest/m3/ts-adaptor-places.md Quest-Claim: 9cac9d6c6b1de73b From 6ec4188114e332e6ed1488eedf5586cec229bdf0 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sun, 1 Nov 2026 07:09:33 -0800 Subject: [PATCH 2/3] feat(ts): lend places through zip's collection argument Co-Authored-By: Claude Opus 5.5 --- quest/m3/README.md | 3 +- quest/m3/ts-adaptor-places.md | 18 ----- quest/m3/ts-iter-zip.md | 14 ++++ quest/m3/ts-place-compares.md | 17 +++++ ts/fixtures/lang/expected/core/result.ts | 14 ++++ ts/fixtures/lang/expected/harness.ts | 12 ++-- ts/fixtures/lang/expected/refs.ts | 87 ++++++++++++++++++++++++ ts/fixtures/lang/src/harness.rs | 1 + ts/fixtures/lang/src/refs.rs | 44 ++++++++++++ ts/src/lower/mir/call.rs | 46 +++++++------ 10 files changed, 211 insertions(+), 45 deletions(-) delete mode 100644 quest/m3/ts-adaptor-places.md create mode 100644 quest/m3/ts-iter-zip.md create mode 100644 quest/m3/ts-place-compares.md diff --git a/quest/m3/README.md b/quest/m3/README.md index 2f5429677..9a8ac1af9 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -27,7 +27,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 eb059757c..834dfdcdd 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 f59dc2e82..4b05df89c 100644 --- a/ts/fixtures/lang/expected/harness.ts +++ b/ts/fixtures/lang/expected/harness.ts @@ -44,7 +44,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"; @@ -256,10 +256,12 @@ 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], ["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]]; } diff --git a/ts/fixtures/lang/expected/refs.ts b/ts/fixtures/lang/expected/refs.ts index 71e1a2200..6132af26e 100644 --- a/ts/fixtures/lang/expected/refs.ts +++ b/ts/fixtures/lang/expected/refs.ts @@ -4,6 +4,7 @@ import * as rt from "./rt.ts"; import { copy$Option_tuple2_u8_u8, copy$array_u32 } from "./lib.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 +1378,90 @@ export function toRefs(data: rt.ByteSlice, out: rt.ByteVec): void { } } +/** `Ok` for an even byte, else `Err`. */ +export function parity(b: rt.u8): rt.Result { + if (!(rt.u8Rem(b, 2) === 0)) { + return rt.Err(b); + } + return rt.Ok(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); + let res: rt.Result = parity(option_unwrapOr$0r2bmpr(rt.byteSliceGet(data, 1), 4)); + 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 80884713f..b25c6bd40 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)), ] 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 e54baba62..688bcf65a 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -334,10 +334,31 @@ 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::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. + // a `Result`'s payload, the value of an `Arc`, a lock, or a cell, what a write guard + // lends its map, 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") @@ -349,25 +370,8 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { | ("Arc" | "Rc", "get_mut" | "make_mut") | ("Mutex" | "RwLock" | "RefCell" | "OnceCell" | "OnceLock", "get_mut") | ("RefMut" | "RwLockWriteGuard" | "MappedRwLockWriteGuard", "map" | "filter_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::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, From ae561196b4f97d9c30b30958c24b84b133f7db15 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Sun, 1 Nov 2026 15:57:27 -0800 Subject: [PATCH 3/3] test(ts): regenerate the lang harness snapshot after the merge Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/expected/harness.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ts/fixtures/lang/expected/harness.ts b/ts/fixtures/lang/expected/harness.ts index e466dcae4..1d6274884 100644 --- a/ts/fixtures/lang/expected/harness.ts +++ b/ts/fixtures/lang/expected/harness.ts @@ -275,10 +275,10 @@ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) capacity_deques(a0153, a1153.data, a1153.start, a1153.length, a2153); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::heaps", (a0154: rt.u8, a1154: rt.ByteSlice, a2154: rt.ByteVec): undefined => { capacity_heaps(a0154, a1154.data, a1154.start, a1154.length, a2154); - }] 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]]; }