diff --git a/quest/m3/README.md b/quest/m3/README.md index 3cd92bbac..23c07aada 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -17,14 +17,14 @@ generic std gap is planned here even when moq never calls it. - [Relink without rebuilding](/quest/m3/relink.md) - measure upstream's skip of dependency-only rebuilds on moq's edits once it reaches nightly - [Unsafe code the translator can model](/quest/m3/ts-unsafe.md) - a dependency using only `unsafe` the translator can model translates from source - [The capacity of a BytesMut](/quest/m3/ts-bytes-capacity.md) - `BytesMut::capacity` and what reads it translate with `bytes`' values -- [Trait objects](/quest/m3/ts-dyn.md) - `dyn Error`'s downcasts, `dyn FusedIterator`, and `Arc` translate, so a consumer neither rewrites nor binds them +- [Trait objects](/quest/m3/ts-dyn.md) - `dyn Error`'s downcasts translate, so a consumer neither rewrites nor binds them - [A trait object in a struct's last field](/quest/m3/ts-dyn-tail.md) - `Rc>` and the like translate, the last field as the object - [bytes' Chain as a Buf object](/quest/m3/ts-buf-chain.md) - `Chain::copy_to_bytes` across both halves translates, and with it a `Chain` as a `dyn Buf` - [A Buf object's size](/quest/m3/ts-dyn-buf-size.md) - a `dyn Buf` object holds only the methods some call dispatches to - [A boxed iterator folds as std's box does](/quest/m3/ts-box-folds.md) - a `Box` reads by `next` where std's `Iterator` for `Box` forwards nothing else - [by_ref of a mutable reference to an iterator](/quest/m3/ts-by-ref-place.md) - `Iterator::by_ref` of a `&mut I` in a local translates - [mem::take as a function value](/quest/m3/ts-take-value.md) - `mem::take`, `replace`, and `swap` passed as a function value translate -- [Debug of a RefCell](/quest/m3/ts-refcell-debug.md) - `{:?}` of a `RefCell`, `Ref`, and `RefMut` translates as std's writes them +- [Debug of a RefCell or a Cell](/quest/m3/ts-refcell-debug.md) - `{:?}` of a `RefCell`, `Ref`, `RefMut`, and `Cell` translates as std's writes them - [Placeholders written to a crate's own fmt::Write](/quest/m3/ts-fmt-calls.md) - `write!` with placeholders into a crate's own writer translates, its `write_str` seeing each call std makes - [Waiting: drop the lang fixture's mul_add allowance](/quest/m3/ts-fma-allowance.md) - the differential fails on any `mul_add` difference again once wasm32's `fma` rounds exactly - [Waiting: type-check in process with tsrs](/quest/m3/ts-tsrs.md) - rust2-ts checks bindings through tsrs instead of `tsc` once it is a stable library diff --git a/quest/m3/ts-dyn.md b/quest/m3/ts-dyn.md index e09e68083..c709a3fba 100644 --- a/quest/m3/ts-dyn.md +++ b/quest/m3/ts-dyn.md @@ -1,12 +1,10 @@ -# [L] Trait objects +# [M] Trait objects ## Goal -[rust2-ts](/ts/README.md) translates the trait objects of std's and mapped crates' traits -that it still refuses, `dyn Error`'s downcasts and `dyn FusedIterator`, behind -any pointer std coerces to one, such as `&mut dyn Debug` and -`Arc`, with Rust's dispatch and drop order, proven by differential tests against native Rust, so a consumer -neither rewrites nor binds them. +[rust2-ts](/ts/README.md) translates `dyn Error`'s downcasts, with Rust's answers for a +value upcast from the crate's own trait, proven by differential tests against native Rust, +so a consumer neither rewrites nor binds them. ## Plan @@ -16,7 +14,8 @@ config, so trait objects move from never to not yet. - `dyn Debug` and `dyn Display` translate as functions writing their value, behind `&`, `&mut`, `Box`, `Arc`, and `Rc`, `dyn Future` as an `rt.Future`, `dyn Any` as the class a downcast checks for, `dyn Fn*` as the - closure itself, `dyn Iterator` and its subtraits as an `rt.Iter`, and the translated + closure itself, `dyn Iterator` and its subtraits as an `rt.Iter`, behind `&`, `&mut`, + `Box`, `Arc`, and `Rc`, and the translated crates' traits as an object with the trait's methods. Build the rest on those. - `dyn Error` translates as an object of `source`, `Debug`, and `Display`. Its downcasts remain: checking the class of `Error.of$T` as `dyn Any`'s do would miss a value upcast diff --git a/quest/m3/ts-refcell-debug.md b/quest/m3/ts-refcell-debug.md index 7acf62ffc..45cc894a6 100644 --- a/quest/m3/ts-refcell-debug.md +++ b/quest/m3/ts-refcell-debug.md @@ -1,14 +1,17 @@ -# [XS] Debug of a RefCell +# [XS] Debug of a RefCell or a Cell ## Goal [rust2-ts](/ts/README.md) translates `{:?}` of a `RefCell` and its `Ref` and `RefMut`, -writing `RefCell { value: .. }` and `` while mutably borrowed as std's does, -proven by a differential test against native Rust. +writing `RefCell { value: .. }` and `` while mutably borrowed as std's does, and +of a `Cell` of a `Copy` value, writing `Cell { value: .. }`, proven by a differential test +against native Rust. ## Plan Found writing [`Box`](https://github.com/moq-dev/rust2/pull/445): it is an -"unsupported call to `> as Debug>::fmt`". +"unsupported call to `> as Debug>::fmt`". `Cell`'s, found writing +[`&mut dyn Debug`](https://github.com/moq-dev/rust2/pull/453), is the same; the +maintainer chose on 2026-11-02 to cover both here. Public API: the translator's output. diff --git a/ts/README.md b/ts/README.md index ac0ff7939..d81a14fd3 100644 --- a/ts/README.md +++ b/ts/README.md @@ -126,7 +126,7 @@ crate translation reaches is refused, and so are two versions of one crate it re | `dyn Error` | an object of its `source`, `fmt$Debug`, and `fmt$Display`: `e.source$Error()` | | `dyn Any` | an instance of the class its value's type was wrapped in, which a downcast checks for: `a instanceof Any.of$dyns_Square ? a.v : undefined` | | `dyn Fn`, `dyn FnMut`, `dyn FnOnce` | the closure or function it was made of: `(a0: rt.u8) => rt.u32` | -| `dyn Iterator`, `dyn DoubleEndedIterator`, `dyn ExactSizeIterator` | `rt.Iter`: `rt.iterDyn` of the iterator it was made of | +| `dyn Iterator`, `dyn DoubleEndedIterator`, `dyn ExactSizeIterator`, `dyn FusedIterator` | `rt.Iter`: `rt.iterDyn` of the iterator it was made of | | constant | its value, inlined as Rust inlines it, so each use of one holding a cell is a cell of its own; one with no value tree, or holding a cell, read from the memory rustc evaluated | | `static` | a module's value, named for the static, that every use shares, read from the memory rustc evaluated; one that reads another static or a class is an `rt.LazyCell` of it, made on first use, since that may not be made yet when its module is. A `static mut`, and a pointer in one into memory the translation can't name, fail translation | | a type that drops | a class with `[Symbol.dispose]`: its `Drop` impl, then its fields | @@ -493,12 +493,14 @@ that keeps `Error`'s own. `From` an error into a `Box`, which `?` con makes one. Its deprecated `description` and `cause`, and its downcasts, are refused, and so is `From` a string, which boxes std's own private error type. -A `dyn Iterator`, `dyn DoubleEndedIterator`, or `dyn ExactSizeIterator` behind `&`, -`&mut`, or `Box` is an `rt.Iter` of its items, `rt.iterDyn` of the iterator it was made +A `dyn Iterator`, `dyn DoubleEndedIterator`, `dyn ExactSizeIterator`, or `dyn FusedIterator` +behind `&`, `&mut`, `Box`, `Arc`, or `Rc` is an `rt.Iter` of its items, `rt.iterDyn` of the iterator it was made of, std's or the crate's own: it reads that iterator by the methods std's vtable holds, such as `next`, `nth`, and `size_hint`, and folds it by `next`, as std's defaults read a trait object, so a `count` or `last` of a boxed `Chain` drops its items in the order the -trait object's does. A box drops the iterator it owns. +trait object's does. A box drops the iterator it owns, and the last `Arc` the one they +share. `Arc::get_mut` and `Rc::get_mut` of a trait object lend its object while no other +`Arc` or `Weak` shares it, but a `dyn Any`'s, whose value `Arc::downcast` gives back. A trait object of std's or a mapped crate's trait other than `dyn Debug` and `dyn Display` behind `&`, `&mut`, diff --git a/ts/fixtures/lang/expected/core/option.ts b/ts/fixtures/lang/expected/core/option.ts index 100fd0b87..755b61c8c 100644 --- a/ts/fixtures/lang/expected/core/option.ts +++ b/ts/fixtures/lang/expected/core/option.ts @@ -301,6 +301,13 @@ export function map$1i542n2(self: rt.Arc | undefined, f: (a0: return f(self); } +export function map$0oedb57(self: rt.Arc> | undefined, f: (a0: rt.Arc>) => [rt.usize, rt.usize | undefined]): [rt.usize, rt.usize | undefined] | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + export function map$1mp7tga(self: rt.i64[] | undefined, f: (a0: rt.i64[]) => rt.usize): rt.usize | undefined { if (self === undefined) { return undefined; @@ -393,6 +400,13 @@ export function map$1jcn6wm(self: rt.ByteSlice | undefined, f: (a0: rt.ByteSlice return f(self); } +export function map$1qfllpb(self: rt.Iter | undefined, f: (a0: rt.Iter) => rt.u8 | undefined): rt.u8 | undefined | rt.None { + if (self === undefined) { + return rt.None; + } + return f(self); +} + export function map$1k7m6zz(self: [rt.u8, rt.u8] | undefined, f: (a0: [rt.u8, rt.u8]) => [rt.u8, rt.u8]): [rt.u8, rt.u8] | undefined { if (self === undefined) { return undefined; @@ -1740,6 +1754,20 @@ export function andThen$0qni7te(self: rt.u8 | undefined | rt.None, f: (a0: rt.u8 return f(self); } +export function andThen$0v0bj8w(self: rt.Iter | undefined, f: (a0: rt.Iter) => dyns_Tagged.Tagged | undefined): dyns_Tagged.Tagged | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + +export function andThen$0ntiuws(self: rt.Iter | undefined, f: (a0: rt.Iter) => rt.u8 | undefined): rt.u8 | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + export function andThen$101gy6h(self: rt.Place | undefined, f: (a0: rt.Place) => rt.Place | undefined): rt.Place | undefined { if (self === undefined) { return undefined; diff --git a/ts/fixtures/lang/expected/dyns.Resuming.ts b/ts/fixtures/lang/expected/dyns.Resuming.ts new file mode 100644 index 000000000..58727756b --- /dev/null +++ b/ts/fixtures/lang/expected/dyns.Resuming.ts @@ -0,0 +1,21 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; + +/** Counts down to its end, then starts over, as a fused iterator never does. */ +export interface Resuming { + _0: rt.u8; +} + +export function next$iter_Iterator(self: Resuming): rt.u8 | rt.Done { + const _2: rt.u8 | undefined = rt.u8CheckedSub(self._0, 1); + if (_2 === undefined) { + self._0 = 2; + return rt.DONE; + } + const n: rt.u8 = _2; + self._0 = n; + return n; +} + +//# sourceMappingURL=dyns.Resuming.ts.map diff --git a/ts/fixtures/lang/expected/dyns.ts b/ts/fixtures/lang/expected/dyns.ts index 39b0d541a..3000dca26 100644 --- a/ts/fixtures/lang/expected/dyns.ts +++ b/ts/fixtures/lang/expected/dyns.ts @@ -4,7 +4,7 @@ import * as rt from "./rt.ts"; import { drop$Result_Rc_dyns_Noisy_Rc_dyn_Any, drop$Result_Rc_dyns_Square_Rc_dyn_Any, drop$Result_Rc_dyns_Tri_Rc_dyn_Any } from "./lib.ts"; import * as Any from "./core/any.Any.ts"; import { thenSome$0d5am30 } from "./core/bool.ts"; -import { unwrapOr$1iemzn8 as option_unwrapOr$1iemzn8, unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$1i542n2 as option_map$1i542n2, map$08a6hpg as option_map$08a6hpg, map$0j05j69 as option_map$0j05j69, mapOr$06rr3sb as option_mapOr$06rr3sb } from "./core/option.ts"; +import { unwrapOr$1iemzn8 as option_unwrapOr$1iemzn8, unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$1i542n2 as option_map$1i542n2, map$0oedb57 as option_map$0oedb57, map$08a6hpg as option_map$08a6hpg, map$1qfllpb as option_map$1qfllpb, map$0j05j69 as option_map$0j05j69, mapOr$06rr3sb as option_mapOr$06rr3sb, andThen$0v0bj8w as option_andThen$0v0bj8w, andThen$0ntiuws as option_andThen$0ntiuws } from "./core/option.ts"; import { ok$00p9ap8, ok$0lceojf, map$0zzqmaz as result_map$0zzqmaz } from "./core/result.ts"; import * as Chunks from "./dyns.Chunks.ts"; import * as dyns_Countdown from "./dyns.Countdown.ts"; @@ -14,6 +14,7 @@ import * as Hook from "./dyns.Hook.ts"; import * as Looped from "./dyns.Looped.ts"; import * as dyns_Named from "./dyns.Named.ts"; import * as dyns_Noisy from "./dyns.Noisy.ts"; +import * as Resuming from "./dyns.Resuming.ts"; import * as dyns_Shape from "./dyns.Shape.ts"; import * as Shown from "./dyns.Shown.ts"; import * as Square from "./dyns.Square.ts"; @@ -874,10 +875,106 @@ export function iters(data: rt.ByteSlice, out: rt.ByteVec): void { }), "next", 4); const _369: rt.usize = rt.ownedCount(split.chunks$Chunks()); const args20: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtList(lens_, f, rt.usizeDebug), (f: rt.Formatter) => rt.usizeDisplay(_369, f)]; - fmt_put(out, rt.fmtFormat([args20[0], " ", args20[1]])); + fmt_put(out, rt.fmtFormat([args20[0], " ", args20[1], " "])); + const part3 = drain(rt.iterDyn(rt.iterBorrow(rt.iterFuse(rt.iterFrom({ _0: rt.u8Rem(a, 4) }, (it: Resuming.Resuming) => Resuming.next$iter_Iterator(it)))))); + const args22: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtPad(f, part3)]; + fmt_put(out, rt.fmtFormat([args22[0], " "])); + const resuming: Resuming.Resuming = { _0: rt.u8Rem(b, 4) }; + const up: rt.Iter = rt.iterDyn(rt.iterFrom(resuming, (it: Resuming.Resuming) => Resuming.next$iter_Iterator(it))); + const part4 = [rt.iterToOption(up.next()), rt.iterToOption(up.next()), rt.iterToOption(up.next()), rt.iterToOption(up.next()), rt.iterToOption(up.next())] as [rt.u8 | undefined, rt.u8 | undefined, rt.u8 | undefined, rt.u8 | undefined, rt.u8 | undefined]; + const args24: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.fmtOption(part4[0], f, rt.u8Debug), (f: rt.Formatter) => rt.fmtOption(part4[1], f, rt.u8Debug), (f: rt.Formatter) => rt.fmtOption(part4[2], f, rt.u8Debug), (f: rt.Formatter) => rt.fmtOption(part4[3], f, rt.u8Debug), (f: rt.Formatter) => rt.fmtOption(part4[4], f, rt.u8Debug)], f)]; + fmt_put(out, rt.fmtFormat([args24[0], " "])); + const boxed3: rt.Iter = rt.iterDyn(rt.iterFrom(new dyns_Countdown.Countdown(rt.u8Rem(a, 8)), (it: dyns_Countdown.Countdown) => dyns_Countdown.next$iter_Iterator(it), rt.drop)); + const part5 = drain(boxed3); + const args26: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtPad(f, part5)]; + fmt_put(out, rt.fmtFormat([args26[0], " "])); + rt.drop(boxed3); + dropped(out); + const concrete: rt.Arc> = new rt.Arc(rt.iterSlice(tagged(front), rt.drop)); + const shared: rt.Arc> = new rt.ArcDyn(concrete, rt.iterDyn(concrete.value)); + const other: rt.Arc> = rt.arcClone(shared); + const weak: rt.Weak> = new rt.Weak(other); + const _456: [rt.usize, rt.usize | undefined] = other.value.sizeHint(); + const _460: boolean = rt.arcGetMut(shared) === undefined; + const args28: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.usizeDebug(_456[0], f), (f: rt.Formatter) => rt.fmtOption(_456[1], f, rt.usizeDebug)], f), (f: rt.Formatter) => rt.fmtBool(_460, f)]; + fmt_put(out, rt.fmtFormat([args28[0], " ", args28[1], " "])); + other[Symbol.dispose](); + weak[Symbol.dispose](); + const taken: rt.u8 | undefined = option_map$08a6hpg(option_andThen$0v0bj8w(rt.arcGetMut(shared), (it2: rt.Iter): dyns_Tagged.Tagged | undefined => { + return rt.iterToOption(it2.next()); + }), (t5: dyns_Tagged.Tagged): rt.u8 => { + const _013: rt.u8 = t5._0; + t5[Symbol.dispose](); + return _013; + }); + dropped(out); + const args30: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtOption(taken, f, rt.u8Debug)]; + fmt_put(out, rt.fmtFormat([args30[0], " "])); + shared[Symbol.dispose](); + dropped(out); + const _492: rt.Arc> = new rt.Arc(rt.byteVecIntoIter(rt.byteSliceToByteVec(back))); + const exact: rt.Arc> = new rt.ArcDyn(_492, rt.iterDyn(_492.value)); + const up2: rt.Arc> = rt.arcClone(exact); + const _506: rt.usize = exact.value.len(); + const _510: rt.u8 | undefined | rt.None = option_map$1qfllpb(rt.arcGetMut(up2), (it3: rt.Iter): rt.u8 | undefined => { + return rt.iterToOption(it3.next()); + }); + const args32: rt.Fmt[] = [(f: rt.Formatter) => rt.usizeDisplay(_506, f), (f: rt.Formatter) => rt.fmtOption(_510, f, (value: rt.u8 | undefined, f: rt.Formatter) => rt.fmtOption(value, f, rt.u8Debug), rt.None)]; + fmt_put(out, rt.fmtFormat([args32[0], " ", args32[1], " "])); + exact[Symbol.dispose](); + const next: rt.u8 | undefined = option_andThen$0ntiuws(rt.arcGetMut(up2), (it4: rt.Iter): rt.u8 | undefined => { + return rt.iterToOption(it4.next()); + }); + const _530: [rt.usize, rt.usize | undefined] = up2.value.sizeHint(); + const args34: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtOption(next, f, rt.u8Debug), (f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.usizeDebug(_530[0], f), (f: rt.Formatter) => rt.fmtOption(_530[1], f, rt.usizeDebug)], f)]; + fmt_put(out, rt.fmtFormat([args34[0], " ", args34[1], " "])); + const fromBox: rt.Arc> = new rt.Arc(rt.iterDyn(rt.iterFrom(new dyns_Countdown.Countdown(rt.u8Rem(b, 8)), (it: dyns_Countdown.Countdown) => dyns_Countdown.next$iter_Iterator(it), rt.drop))); + const part6 = option_andThen$0ntiuws(rt.arcGetMut(fromBox), (it5: rt.Iter): rt.u8 | undefined => { + return rt.iterToOption(it5.next()); + }); + const args36: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtOption(part6, f, rt.u8Debug)]; + fmt_put(out, rt.fmtFormat([args36[0], " "])); + fromBox[Symbol.dispose](); + dropped(out); + const counted2: rt.Arc = new rt.Arc(new dyns_Countdown.Countdown(rt.u8Rem(a, 8))); + const weak3: rt.Weak> = rt.weakDyn(new rt.Weak(counted2), (arc: rt.Arc): rt.Arc> => { + return new rt.ArcDyn(arc, rt.iterDyn(rt.iterFrom(arc.value, (it: dyns_Countdown.Countdown) => dyns_Countdown.next$iter_Iterator(it), rt.drop))); + }); + const part7 = option_map$0oedb57(rt.weakUpgrade(weak3), (it6: rt.Arc>): [rt.usize, rt.usize | undefined] => { + const _017: [rt.usize, rt.usize | undefined] = it6.value.sizeHint(); + it6[Symbol.dispose](); + return _017; + }); + const args38: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtOption(part7, f, (value: [rt.usize, rt.usize | undefined], f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.usizeDebug(value[0], f), (f: rt.Formatter) => rt.fmtOption(value[1], f, rt.usizeDebug)], f))]; + fmt_put(out, rt.fmtFormat([args38[0], " "])); + counted2[Symbol.dispose](); + const _596: rt.Arc> | undefined = rt.weakUpgrade(weak3); + const part8 = _596 === undefined; + const args40: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtBool(part8, f)]; + const _590: string = rt.fmtFormat([args40[0], " "]); + rt.drop(_596); + fmt_put(out, _590); + dropped(out); + const pinned: rt.Iter = rt.iterDyn(rt.iterFrom(new dyns_Countdown.Countdown(rt.u8Rem(b, 8)), (it: dyns_Countdown.Countdown) => dyns_Countdown.next$iter_Iterator(it), rt.drop)); + const part9 = rt.iterToOption(pinned.next()); + const args42: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtOption(part9, f, rt.u8Debug)]; + fmt_put(out, rt.fmtFormat([args42[0]])); + rt.drop(pinned); + dropped(out); + weak3[Symbol.dispose](); + up2[Symbol.dispose](); } } +/** What `it` yields up to its end and twice past it. */ +export function drain(it: rt.Iter): string { + const items: rt.ByteVec = rt.iterToByteVec(rt.iterBorrow(it), "next"); + const _7: rt.u8 | undefined = rt.iterToOption(it.next()); + const _9: rt.u8 | undefined = rt.iterToOption(it.next()); + const args2: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtList(rt.byteVecView(items), f, rt.u8Debug), (f: rt.Formatter) => rt.fmtOption(_7, f, rt.u8Debug), (f: rt.Formatter) => rt.fmtOption(_9, f, rt.u8Debug)]; + return rt.fmtFormat([args2[0], " ", args2[1], " ", args2[2]]); +} + /** * Trait objects behind the rest of the pointers std coerces to one: an `Rc`, a `Weak` of * an `Rc` or an `Arc`, a `Pin`, and an `Rc` or an `Arc` made from a box of one. Counts, diff --git a/ts/fixtures/lang/src/dyns.rs b/ts/fixtures/lang/src/dyns.rs index c5f442f4b..56149fd9f 100644 --- a/ts/fixtures/lang/src/dyns.rs +++ b/ts/fixtures/lang/src/dyns.rs @@ -6,14 +6,15 @@ //! was made of, borrowed, boxed, or shared. A `dyn Fn*` of a closure, a function, or a //! function pointer calls it, changing what it captured, borrowed, boxed, or shared. An //! `Rc`, a `Weak`, a `Pin`, and an `Rc` or an `Arc` made from a box hold one too. A -//! `dyn Iterator`, `dyn DoubleEndedIterator`, or `dyn ExactSizeIterator` of std's -//! iterators or the crate's own, borrowed or boxed, reads by its vtable's methods and -//! folds by `next`, dropping what it owns in std's order. +//! `dyn Iterator`, `dyn DoubleEndedIterator`, `dyn ExactSizeIterator`, or +//! `dyn FusedIterator` of std's iterators or the crate's own, borrowed, boxed, or shared, +//! reads by its vtable's methods and folds by `next`, dropping what it owns in std's order. use std::any::Any; use std::cell::RefCell; use std::collections::BTreeMap; use std::fmt::{self, Debug}; +use std::iter::FusedIterator; use std::pin::Pin; use std::rc::{self, Rc}; use std::sync::atomic::{AtomicU32, Ordering}; @@ -732,7 +733,94 @@ pub fn iters(data: &[u8], out: &mut Vec) { // Returned through a trait object of the crate's own trait. let split: &dyn Chunks = &Lease(rest.to_vec(), usize::from(b % 4) + 1); let lens: Vec = split.chunks().map(<[u8]>::len).collect(); - put(out, &format!("{lens:?} {}", split.chunks().count())); + put(out, &format!("{lens:?} {} ", split.chunks().count())); + + // Fused: through the object, a `fuse` keeps an iterator that resumes at its end, and the + // crate's own fused iterator, done. + let mut fused = Resuming(a % 4).fuse(); + put(out, &format!("{} ", drain(&mut fused))); + let mut resuming = Resuming(b % 4); + let up: &mut dyn Iterator = &mut resuming; + put( + out, + &format!("{:?} ", (up.next(), up.next(), up.next(), up.next(), up.next())), + ); + let mut boxed: Box> = Box::new(Countdown(a % 8)); + put(out, &format!("{} ", drain(&mut *boxed))); + drop(boxed); + dropped(out); + + // Shared: the vtable's `&self` methods through any `Arc`, `next` only through the one + // `get_mut` lends, and the items left drop with the last `Arc`. + let concrete = Arc::new(tagged(front).into_iter()); + let mut shared: Arc> = concrete; + let other = shared.clone(); + let weak = Arc::downgrade(&other); + put( + out, + &format!("{:?} {} ", other.size_hint(), Arc::get_mut(&mut shared).is_none()), + ); + drop(other); + drop(weak); + let taken = Arc::get_mut(&mut shared).and_then(|it| it.next()).map(|t| t.0); + dropped(out); + put(out, &format!("{taken:?} ")); + drop(shared); + dropped(out); + let exact: Rc> = Rc::new(back.to_vec().into_iter()); + let mut up: Rc> = exact.clone(); + put( + out, + &format!("{} {:?} ", exact.len(), Rc::get_mut(&mut up).map(|it| it.next())), + ); + drop(exact); + let next = Rc::get_mut(&mut up).and_then(|it| it.next()); + put(out, &format!("{next:?} {:?} ", up.size_hint())); + let boxed: Box> = Box::new(Countdown(b % 8)); + let mut from_box: Arc> = Arc::from(boxed); + let first = Arc::get_mut(&mut from_box).and_then(|it| it.next()); + put(out, &format!("{first:?} ")); + drop(from_box); + dropped(out); + let counted = Rc::new(Countdown(a % 8)); + let weak: rc::Weak = Rc::downgrade(&counted); + let weak: rc::Weak> = weak; + put(out, &format!("{:?} ", weak.upgrade().map(|it| it.size_hint()))); + drop(counted); + put(out, &format!("{} ", weak.upgrade().is_none())); + dropped(out); + let mut pinned: Pin + Unpin>> = Box::pin(Countdown(b % 8)); + put(out, &format!("{:?}", pinned.as_mut().next())); + drop(pinned); + dropped(out); +} + +/// Counts down to its end, then starts over, as a fused iterator never does. +struct Resuming(u8); + +impl Iterator for Resuming { + type Item = u8; + + fn next(&mut self) -> Option { + match self.0.checked_sub(1) { + Some(n) => { + self.0 = n; + Some(n) + } + None => { + self.0 = 2; + None + } + } + } +} + +impl FusedIterator for Countdown {} + +/// What `it` yields up to its end and twice past it. +fn drain(it: &mut dyn FusedIterator) -> String { + let items: Vec = (&mut *it).collect(); + format!("{items:?} {:?} {:?}", it.next(), it.next()) } /// A shape that notes its id in a log as it drops. diff --git a/ts/src/lower/iter.rs b/ts/src/lower/iter.rs index 4efad521e..7181f772e 100644 --- a/ts/src/lower/iter.rs +++ b/ts/src/lower/iter.rs @@ -47,15 +47,19 @@ impl<'tcx> Translator<'_, 'tcx> { .ok() } - /// `Iterator::Item` of `ty`, if it is a `dyn Iterator`, `dyn DoubleEndedIterator`, or - /// `dyn ExactSizeIterator`, whose JS value is an `rt.Iter` of its items. + /// `Iterator::Item` of `ty`, if it is a `dyn Iterator`, `dyn DoubleEndedIterator`, + /// `dyn ExactSizeIterator`, or `dyn FusedIterator`, whose JS value is an `rt.Iter` of its + /// items. pub fn dyn_iter(&self, ty: Ty<'tcx>) -> Option> { let ty::Dynamic(preds, ..) = ty.kind() else { return None; }; let principal = preds.principal_def_id()?; let name = library::std_path(self.tcx, principal).pop()?; - if !matches!(name.as_str(), "Iterator" | "DoubleEndedIterator" | "ExactSizeIterator") { + if !matches!( + name.as_str(), + "Iterator" | "DoubleEndedIterator" | "ExactSizeIterator" | "FusedIterator" + ) { return None; } self.iter_item(ty) diff --git a/ts/src/lower/mir/rvalue.rs b/ts/src/lower/mir/rvalue.rs index 5192e1252..8f3692b1b 100644 --- a/ts/src/lower/mir/rvalue.rs +++ b/ts/src/lower/mir/rvalue.rs @@ -463,9 +463,12 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } }; let (from, to) = (Ty::new_box(tcx, pointee), Ty::new_box(tcx, object)); - let value = match self.b.t.dyn_future(object) { - Some(_) => self.b.dyn_future(from, to, value, span), - None => self.b.object_of(value, from, to, span), + let value = if self.b.t.dyn_future(object).is_some() { + self.b.dyn_future(from, to, value, span) + } else if self.b.t.dyn_iter(object).is_some() { + self.b.dyn_iter(from, to, value, span) + } else { + self.b.object_of(value, from, to, span) }; match arc { Some(arc) => Expr::New(Box::new(Expr::Rt("ArcDyn".into())), vec![arc, value]), diff --git a/ts/src/lower/task.rs b/ts/src/lower/task.rs index 09a6b7d82..551a7f823 100644 --- a/ts/src/lower/task.rs +++ b/ts/src/lower/task.rs @@ -141,6 +141,13 @@ pub fn call<'tcx>( ("Arc" | "Rc", "new_cyclic") => Expr::rt("arcNewCyclic", args), // JS erases the pin. ("Arc" | "Rc", "pin") => Expr::New(Box::new(Expr::Rt("Arc".into())), args), + // The object of one of a trait object, which every pointer to it shares. A `dyn Any`'s + // may hold a copy of a value JS copies, which `Arc::downcast` would not see written. + ("Arc" | "Rc", "get_mut") + if call.args.type_at(0).is_trait() && !super::dynamic::is_any_object(tcx, call.args.type_at(0)) => + { + Expr::rt("arcGetMut", args) + } // A `&mut` to the value, which is where it is for one JS copies. `make_mut` takes the // `Arc`'s place, which it may point at a new `Arc`. ("Arc" | "Rc", "get_mut" | "make_mut") if call.args.type_at(0).is_sized(tcx, b.t.typing_env_pub()) => {