Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion quest/m3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ generic std gap is planned here even when moq never calls it.
- [A reversed zip's per-pair cost](/quest/m3/ts-zip-rev-overhead.md) - a reversed by-index zip costs per pair what a forward one does, or the gap is explained
- [Whole writes into a lazy cell or thread-local key](/quest/m3/ts-lazy-writes.md) - a whole write through a `&mut` to a `LazyCell`, `LazyLock`, or `LocalKey` keeps its state
- [Type errors at the Rust that made them](/quest/m3/ts-span-precision.md) - a `tsc` error on output names the tail expression or call argument that made it
- [An empty iterator of mutable references](/quest/m3/ts-empty-places.md) - `iter::empty::<&mut T>()` given to `zip` or `chain` translates
- [An Option of numbers in the result slot](/quest/m3/ts-option-slot.md) - `varint_opt`'s `Option` comes back through the slot with no object per call
- [Fresh writes through a zip, a copy, and a repeat](/quest/m3/ts-fresh-shapes.md) - zipping a `Vec<[T; N]>`, reading a `T` out of `Zip::next`, and `copied` or `vec![x; n]` over a `Copy` type translate
- [Python's wake on the loop's own thread](/quest/m3/ffi-python-wake.md) - a wake on the event loop's thread resumes the future with no socket pair
Expand Down
15 changes: 0 additions & 15 deletions quest/m3/ts-empty-places.md

This file was deleted.

54 changes: 49 additions & 5 deletions ts/fixtures/lang/refs/expected/refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1388,8 +1388,8 @@ export function parity(b: rt.u8): rt.u8 | undefined {

/**
* 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.
* beside items of no places or an empty iterator, 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);
Expand Down Expand Up @@ -1439,17 +1439,61 @@ export function adapted(data: rt.ByteSlice, out: rt.ByteVec): void {
}
_76.v = rt.u8WrappingAdd(_76.v, 5);
}
rt.iterEmpty();
const _84: rt.Iter<rt.Place<rt.u8>> = rt.iterChain(rt.iterEmpty() as rt.Iter<rt.Place<rt.u8>>, rt.iterSliceMut(rt.byteVecView(v)));
rt.iterEmpty();
const iter6: rt.Iter<rt.Place<rt.u8>> = rt.iterChain(_84, rt.iterEmpty() as rt.Iter<rt.Place<rt.u8>>);
while (true) {
const _89: rt.Place<rt.u8> | rt.Done = iter6.next();
if (_89 === rt.DONE) {
break;
}
_89.v = rt.intRotateLeft(_89.v, 3, 8, false);
}
const _97: rt.Iter<rt.Place<rt.u8>> = rt.iterSliceMut(rt.byteSliceView(rt.byteVecSlice(v)));
rt.iterEmpty();
const iter7: rt.Iter<[rt.Place<rt.u8>, rt.Place<rt.u8>]> = rt.iterZip(_97, rt.iterEmpty() as rt.Iter<rt.Place<rt.u8>>, "len");
while (true) {
const _102: [rt.Place<rt.u8>, rt.Place<rt.u8>] | rt.Done = iter7.next();
if (_102 === rt.DONE) {
break;
}
const swapped2 = _102[0].v;
_102[0].v = _102[1].v;
_102[1].v = swapped2;
}
rt.iterEmpty();
const iter8: rt.Iter<[rt.Place<rt.u8>, rt.Place<rt.u8>]> = rt.iterZip(rt.iterEmpty() as rt.Iter<rt.Place<rt.u8>>, rt.iterOption(rt.optionAsMut(rt.place(() => opt, (value7) => opt = value7))), "len");
while (true) {
const _113: [rt.Place<rt.u8>, rt.Place<rt.u8>] | rt.Done = iter8.next();
if (_113 === rt.DONE) {
break;
}
_113[0].v = _113[0].v ^ _113[1].v;
_113[1].v = 0;
}
const lists: rt.Iter<rt.Place<rt.u8>> = rt.listIter(rt.listNew());
const owned: rt.Iter<rt.Place<rt.u8>> = rt.listIter(rt.listNew());
rt.iterEmpty() as rt.Iter<rt.Place<rt.u8>>;
const iter9: rt.Iter<rt.Place<rt.u8>> = rt.iterChain(rt.iterChain(rt.iterChain(rt.iterEmpty() as rt.Iter<rt.Place<rt.u8>>, rt.iterSliceMut(rt.byteVecView(v))), lists), owned);
while (true) {
const _128: rt.Place<rt.u8> | rt.Done = iter9.next();
if (_128 === rt.DONE) {
break;
}
_128.v = rt.u8WrappingSub(_128.v, 1);
}
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<rt.u8>): boolean => {
const _166: boolean = rt.iterEq(rt.iterOption(opt), rt.iterOption(rt.optionAsMut(rt.place(() => other, (value8) => other = value8))), (a04: rt.u8, a14: rt.Place<rt.u8>): 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<rt.u8>): boolean => {
const _170: boolean = rt.iterEq(rt.iterResult(res), rt.iterOption(rt.optionAsMut(rt.place(() => opt, (value9) => opt = value9))), (a05: rt.u8, a15: rt.Place<rt.u8>): 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<rt.u8>): boolean => {
Expand All @@ -1458,7 +1502,7 @@ export function adapted(data: rt.ByteSlice, out: rt.ByteVec): void {
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<rt.u8>): 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<rt.u8>): boolean => {
}, true), _166, _170, !rt.iterEq(rt.iterSlice(rt.byteSliceView(data)), rt.iterOption(rt.resultAsMut(rt.place(() => res, (value10) => res = value10))), (a06: rt.u8, a16: rt.Place<rt.u8>): boolean => {
return a06 === a16.v;
}, true)], (a07: boolean): rt.u8 => {
return a07 ? 1 : 0;
Expand Down
20 changes: 18 additions & 2 deletions ts/fixtures/lang/refs/src/refs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,8 +935,8 @@ fn parity(b: u8) -> Result<u8, u8> {
}

/// 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.
/// beside items of no places or an empty iterator, 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<u8>) {
let mut v = data.to_vec();
let mut opt = data.first().copied();
Expand All @@ -957,6 +957,22 @@ pub fn adapted(data: &[u8], out: &mut Vec<u8>) {
for x in None.into_iter().chain(&mut v).chain(&mut opt).chain(&mut res) {
*x = x.wrapping_add(5);
}
// An empty iterator lends nothing, on either side.
for x in std::iter::empty().chain(&mut v).chain(std::iter::empty()) {
*x = x.rotate_left(3);
}
for (x, y) in v.iter_mut().zip(std::iter::empty::<&mut u8>()) {
std::mem::swap(x, y);
}
for (x, y) in std::iter::empty::<&mut u8>().zip(&mut opt) {
*x ^= *y;
*y = 0;
}
let lists = <std::collections::linked_list::IterMut<'_, u8>>::default();
let owned = <std::collections::linked_list::IntoIter<&mut u8>>::default();
for x in std::iter::Empty::default().chain(&mut v).chain(lists).chain(owned) {
*x = x.wrapping_sub(1);
}
out.extend(&v);
out.extend(opt);
out.extend(res.iter().chain(res.as_ref().err()));
Expand Down
1 change: 0 additions & 1 deletion ts/fixtures/reject/census.txt
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ fixtures/reject/src/values.rs:float+1:2: arithmetic on `f16`
fixtures/reject/src/values.rs:generic_unit+1:2: `values::Maybe<()>` is not supported: its field `value` is `Option<T>`, typed `T | undefined`, whose `None` would be `rt.None` as `Option<()>`
fixtures/reject/src/values.rs:lists+1:4: `std::collections::LinkedList::<T, A>::retain` with a mutable reference to a value JS copies, which its mapping would copy
fixtures/reject/src/values.rs:lists+2:4: `clone_from` of `std::collections::LinkedList<clones::Reused>`, whose items' `clone_from` is their own, which is not supported yet
fixtures/reject/src/values.rs:lists+3:2: `std::default::Default::default` with a mutable reference to a value JS copies, which its mapping would copy
fixtures/reject/src/values.rs:manual_impls+2:2: unsupported call to `<std::mem::ManuallyDrop<u32> as std::fmt::Debug>::fmt`
fixtures/reject/src/values.rs:manual_impls+2:25: unsupported call to `<std::mem::ManuallyDrop<u32> as std::cmp::PartialEq>::eq`
fixtures/reject/src/values.rs:manual_impls+2:34: a constant of type `std::mem::ManuallyDrop<u32>`
Expand Down
1 change: 0 additions & 1 deletion ts/fixtures/reject/expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ fixtures/reject/src/values.rs:float+1:2: arithmetic on `f16`
fixtures/reject/src/values.rs:generic_unit+1:2: `values::Maybe<()>` is not supported: its field `value` is `Option<T>`, typed `T | undefined`, whose `None` would be `rt.None` as `Option<()>`
fixtures/reject/src/values.rs:lists+1:4: `std::collections::LinkedList::<T, A>::retain` with a mutable reference to a value JS copies, which its mapping would copy
fixtures/reject/src/values.rs:lists+2:4: `clone_from` of `std::collections::LinkedList<clones::Reused>`, whose items' `clone_from` is their own, which is not supported yet
fixtures/reject/src/values.rs:lists+3:2: `std::default::Default::default` with a mutable reference to a value JS copies, which its mapping would copy
fixtures/reject/src/values.rs:manual_impls+2:11: unsupported call to `<std::mem::ManuallyDrop<u32> as std::fmt::Debug>::fmt`
fixtures/reject/src/values.rs:manual_impls+2:25: unsupported call to `<std::mem::ManuallyDrop<u32> as std::cmp::PartialEq>::eq`
fixtures/reject/src/values.rs:manual_impls+2:34: a constant of type `std::mem::ManuallyDrop<u32>`
Expand Down
6 changes: 2 additions & 4 deletions ts/fixtures/reject/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,10 @@ fn reclone<const N: usize>(a: Option<Block<N>>) -> Option<Block<N>> {
}

/// `LinkedList`'s unstable `retain`, which no mapping covers, as none does its other
/// unstable methods; its `clone_from` of items whose own `clone_from` it would call; and
/// an empty `IterMut` of bytes, whose places nothing makes.
pub fn lists(l: &mut std::collections::LinkedList<u8>, r: &mut std::collections::LinkedList<Reused>) -> usize {
/// unstable methods; and its `clone_from` of items whose own `clone_from` it would call.
pub fn lists(l: &mut std::collections::LinkedList<u8>, r: &mut std::collections::LinkedList<Reused>) {
l.retain(|x| *x > 1);
r.clone_from(&std::collections::LinkedList::new());
std::collections::linked_list::IterMut::<u8>::default().count()
}

/// `ManuallyDrop`'s impls beyond holding a value: compared, formatted, and a constant.
Expand Down
32 changes: 19 additions & 13 deletions ts/src/lower/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,24 @@ pub fn empty_iter(tcx: TyCtxt<'_>, did: DefId) -> bool {
is_std(tcx, did) && std_path(tcx, did) == ["iter", "sources", "empty", "Empty"]
}

/// Whether `did` is a list's `Iter`, `IterMut`, or `IntoIter`.
fn list_iter(tcx: TyCtxt<'_>, did: DefId) -> bool {
is_std(tcx, did)
&& matches!(
std_path(tcx, did)
.iter()
.map(String::as_str)
.collect::<Vec<_>>()
.as_slice(),
["collections", "linked_list", "Iter" | "IterMut" | "IntoIter"]
)
}

/// Whether `Default::default()` of `ty` is an iterator that yields nothing.
pub fn empty_default(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
matches!(ty.kind(), ty::Adt(adt, _) if empty_iter(tcx, adt.did()) || list_iter(tcx, adt.did()))
}

/// Whether a `ty`'s value is a JS string: a `str`, a `String`, a `Box<str>`, or a `Cow<str>`.
pub fn string<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind() {
Expand Down Expand Up @@ -3334,19 +3352,7 @@ pub fn default_value<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, span: Span,
_ if nothing(tcx, ty) => Expr::undefined(),
ty::Adt(adt, _) if empty_iter(tcx, adt.did()) => b.zero_sized_std(ty, Vec::new(), span).ok()?,
// A list's iterators, over an empty list.
ty::Adt(adt, _)
if is_std(tcx, adt.did())
&& matches!(
std_path(tcx, adt.did())
.iter()
.map(String::as_str)
.collect::<Vec<_>>()
.as_slice(),
["collections", "linked_list", "Iter" | "IterMut" | "IntoIter"]
) =>
{
Expr::rt("listIter", vec![Expr::rt("listNew", Vec::new())])
}
ty::Adt(adt, _) if list_iter(tcx, adt.did()) => Expr::rt("listIter", vec![Expr::rt("listNew", Vec::new())]),
ty::Tuple(parts) => Expr::Array(parts.iter().map(|t| part(b, t, out)).collect()),
ty::Array(elem, n) => {
let n = n.try_to_target_usize(tcx)?;
Expand Down
7 changes: 5 additions & 2 deletions ts/src/lower/mir/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,9 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> {
// 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, what a write guard
// lends its map or its leak, the `&mut` an iterator's `by_ref` is given, or what `zip`
// makes of the collection it is given by `into_iter`, as the free `zip` does of either.
// lends its map or its leak, the `&mut` an iterator's `by_ref` is given, what `zip`
// makes of the collection it is given by `into_iter`, as the free `zip` does of either,
// or an empty iterator, made or defaulted, which lends nothing.
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")
Expand All @@ -396,13 +397,15 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> {
| ("RefMut", "leak")
| ("RefCell", "undo_leak")
| ("Iterator", "by_ref")
| ("empty", "empty")
| (
"RefMut" | "MutexGuard" | "MappedMutexGuard" | "RwLockWriteGuard" | "MappedRwLockWriteGuard",
"map" | "filter_map" | "try_map" | "map_split",
) => true,
("IntoIterator", "into_iter") => gargs.types().next().is_some_and(into_places),
("Iterator", "zip") => gargs.types().nth(1).is_some_and(into_places),
("zip", "zip") => gargs.types().take(2).any(into_places),
("Default", "default") => gargs.types().next().is_some_and(|ty| library::empty_default(tcx, ty)),
_ => false,
},
None => false,
Expand Down