diff --git a/quest/m3/README.md b/quest/m3/README.md index 61ad78020..d0e05438c 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -33,7 +33,6 @@ generic std gap is planned here even when moq never calls it. - [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 - [The rest of a boxed slice's impls](/quest/m3/ts-boxed-slice-rest.md) - `clone_from`, `AsRef`, `AsMut`, `TryFrom` into a boxed array, and `Box` into bytes translate for boxed slices - [The rest of a vector's methods](/quest/m3/ts-vec-rest.md) - `append`, `resize`, `split_off`, `try_reserve`, and `Vec::from(VecDeque)` translate -- [The rest of a guard's methods](/quest/m3/ts-guard-rest.md) - `Debug` of `RefCell` and `RwLock` guards, `MutexGuard::map`, and the guards' `try_map` translate - [Aborts in the differential](/quest/m3/ts-abort-differential.md) - the lang differential compares wasm32's aborts, so an `RwLock` locked again while held is tested - [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 diff --git a/quest/m3/ts-guard-rest.md b/quest/m3/ts-guard-rest.md deleted file mode 100644 index 5bf292ed6..000000000 --- a/quest/m3/ts-guard-rest.md +++ /dev/null @@ -1,15 +0,0 @@ -# [S] The rest of a guard's methods - -## Goal - -[rust2-ts](/ts/README.md) translates what is still refused of std's guards: `Debug` of a -`Ref`, a `RefMut`, and an `RwLock`'s guards, mapped or not; `MutexGuard::map` and -`filter_map` and the `MappedMutexGuard` they make; and `Ref::try_map` and -`RefMut::try_map`. Each releases its lock or cell as std's does, proven by differential -tests against native Rust. - -## Plan - -The maps reuse what a `RefCell` guard's map makes: an `rt.RefMap` of where its part is, -so a write through a mapped exclusive guard reaches the value. A `MutexGuard` is its -mutex, so a mapped one needs the mutex to unlock beside the part it lends. diff --git a/ts/README.md b/ts/README.md index 52da3d1c6..66692bd28 100644 --- a/ts/README.md +++ b/ts/README.md @@ -121,9 +121,9 @@ crate translation reaches is refused, and so are two versions of one crate it re | `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 | | a type's function | a function of the type's module, taking `self` first: `Decoder.slice(dec, 4)` after `import * as Decoder` | -| `Arc`, `Rc`, `Weak`, `Mutex` | `rt.Arc` for either, with its strong and weak counts, and `new_cyclic`'s `Weak` upgrading only once its value is made, `get_mut` lending its value only while no other `Arc` or `Weak` shares it, `make_mut` cloning it into a new `rt.Arc` while another `Arc` shares it, or moving it there while only `Weak`s do, which then dangle, and `into_inner` giving it only from the last strong `Arc`, after which its `Weak`s no longer upgrade; `rt.Weak`, counted in its `rt.Arc` until it drops, and of no `Arc` once made by `Weak::new`; `rt.Mutex`, its own guard, which a lock gives as `Ok` and the guard's drop unlocks: locking it again while held panics and `try_lock` gives `WouldBlock`, as wasm32's do, and a panic aborts, so nothing poisons it and `thread::panicking` is `false`; a `Mutex`'s, a `RefCell`'s, and an `RwLock`'s `into_inner` gives its value and `get_mut` lends it, or an `rt.Place` of it where JS copies it | +| `Arc`, `Rc`, `Weak`, `Mutex` | `rt.Arc` for either, with its strong and weak counts, and `new_cyclic`'s `Weak` upgrading only once its value is made, `get_mut` lending its value only while no other `Arc` or `Weak` shares it, `make_mut` cloning it into a new `rt.Arc` while another `Arc` shares it, or moving it there while only `Weak`s do, which then dangle, and `into_inner` giving it only from the last strong `Arc`, after which its `Weak`s no longer upgrade; `rt.Weak`, counted in its `rt.Arc` until it drops, and of no `Arc` once made by `Weak::new`; `rt.Mutex`, its own guard, which a lock gives as `Ok` and the guard's drop unlocks: locking it again while held panics and `try_lock` gives `WouldBlock`, as wasm32's do, and a guard's `map` and `filter_map` make `rt.MutexMap`s, each where the part it lends is, which hold the mutex locked until they drop, and a panic aborts, so nothing poisons it and `thread::panicking` is `false`; a `Mutex`'s, a `RefCell`'s, and an `RwLock`'s `into_inner` gives its value and `get_mut` lends it, or an `rt.Place` of it where JS copies it | | `PoisonError`, `TryLockError` | the `T` it holds; `rt.TryLockError`, tagged as `Bound` is | -| `Cell`, `UnsafeCell`, `RefCell`, `RwLock` | `rt.Cell`, the place holding its value in `v`, for either of the first two: `UnsafeCell::get`'s pointer is the cell where it is an `rt.Place`, and otherwise the object in `v`, refused where the crate may put another cell or object in its place; `rt.RefCell` for either of the others, its own guard, counting its borrows: borrowing it while that conflicts panics as `RefCell`'s does, and locking it so aborts as wasm32's `RwLock` does; `try_borrow` and `try_borrow_mut` fail with `rt.BorrowError` and `rt.BorrowMutError` instead, a clone borrows the cell, `Ref::clone` counts another borrow of it, and a guard's `map`, `filter_map`, and `map_split`, an `RwLock` guard's included, make `rt.RefMap`s, each where a part it lends is, which a write through a mapped `RefMut` reaches in place, sharing the cell's borrow until the last drops; refused for a part of a sized type JS can't write in place, such as a binding's | +| `Cell`, `UnsafeCell`, `RefCell`, `RwLock` | `rt.Cell`, the place holding its value in `v`, for either of the first two: `UnsafeCell::get`'s pointer is the cell where it is an `rt.Place`, and otherwise the object in `v`, refused where the crate may put another cell or object in its place; `rt.RefCell` for either of the others, its own guard, counting its borrows: borrowing it while that conflicts panics as `RefCell`'s does, and locking it so aborts as wasm32's `RwLock` does; `try_borrow` and `try_borrow_mut` fail with `rt.BorrowError` and `rt.BorrowMutError` instead, a clone borrows the cell, `Ref::clone` counts another borrow of it, and a guard's `map`, `filter_map`, `try_map`, and `map_split`, an `RwLock` guard's included, make `rt.RefMap`s, each where a part it lends is, which a write through a mapped `RefMut` reaches in place, sharing the cell's borrow until the last drops; refused for a part of a sized type JS can't write in place, such as a binding's | | `thread_local!`, `LocalKey` | an `rt.LocalKey`, a module's value, which makes its value on first use from the `const` or the expression and never drops it, as wasm32's do; `with` calls its function on that value, `try_with` never fails, the `Cell` and `RefCell` helpers lower from std, and a `RefCell` key's `set` and `with_borrow_mut` borrow it as `borrow_mut` does | | `OnceCell`, `OnceLock` | `rt.OnceCell` and `rt.OnceLock`, holding the `Option` in `v`, which is the place `get_mut` lends where JS copies the value; a `OnceLock` initialized from within its own initialization panics, as wasm32's does, and `get_or_try_init`'s error leaves either empty | | `Once`, `OnceState` | `rt.Once`, an `rt.OnceLock` that holds `true` once `call_once` or `call_once_force` ran its function, and panics as wasm32's does if run again from within it; `rt.OnceState`, that `Once`, which a panic never poisons, as it aborts | @@ -241,7 +241,8 @@ and of their iterators and `Chars` the items each has left, in std's wrapper; a `Iter` and `IterMut`, which write its buffer's two halves, and its `Drain`, which writes its counts, are refused; a list's `Iter` and `IterMut` write what is left and its length, as std's do. `Debug` of `Bound`, `Ordering`, `Reverse`, and `ControlFlow` is -their derived one, whichever way each is held. A caller prints a +their derived one, whichever way each is held, and a guard of a cell or a lock, mapped or +not, writes what it lends by its own `Debug` and `Display`. A caller prints a `printable` type as `to_string()` and `format!("{err:?}")` would: `rt.fmtToString((f) => DecodeError.fmt$fmt_Display(err, f))` and `rt.fmtFormat([(f) => DecodeError.fmt$fmt_Debug(err, f)])`, after diff --git a/ts/fixtures/lang/caller.ts b/ts/fixtures/lang/caller.ts index 8b588da7f..2e52741fe 100644 --- a/ts/fixtures/lang/caller.ts +++ b/ts/fixtures/lang/caller.ts @@ -421,6 +421,26 @@ const mappedLocks = [[0x04, 0x01, 0x02, 0x86, 0x05, 0x0a, 0x03, 0x02, 0x80, 0x01 Uint8Array.of(op("locks::mapped"), 0, ...bytes), ); +/** + * `borrows::tries` scripts: each operation through tried guards, given back beside each + * error, printed, and each borrow again while a tried guard holds the cell, which panics. + */ +const tries = [ + [0x00, 0x00, 0x10, 0xf0, 0xe0, 0x01, 0x02, 0x05, 0x20, 0x21, 0x06, 0x03], + [0x01, 0x10, 0x01, 0x12, 0xe2], + [0xd3], + [0xe3], + [0x00, 0xf3], +].map((bytes) => Uint8Array.of(op("borrows::tries"), 0, ...bytes)); + +/** `locks::mutex_mapped` scripts: writes and reads through mapped and filtered mutex guards. */ +const mutexMaps = [[0x04, 0x01, 0x02, 0x86, 0xf5, 0x0a, 0x03, 0x02, 0xe1, 0x80, 0x07, 0x02]].map((bytes) => + Uint8Array.of(op("locks::mutex_mapped"), 0, ...bytes), +); + +/** `locks::printed` scripts: each lock's guards printed, empty and not. */ +const printed = [[], [0, 1, 25, 200]].map((bytes) => Uint8Array.of(op("locks::printed"), 0, ...bytes)); + /** `locks::rwlocks` scripts: `get_mut` and `into_inner` among writes and reads. */ const rwlocks = [[6, 7, 0, 6, 2, 14, 15, 1]].map((bytes) => Uint8Array.of(op("locks::rwlocks"), 0, ...bytes)); @@ -807,4 +827,4 @@ const capacityCases = [ ...Array.from({ length: 8 * 6 }, (_, i) => Uint8Array.of(op("capacity::collects"), i % 8, [0, 1, 2, 5, 9, 23][Math.floor(i / 8)], 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), ]; -export const cases: Uint8Array[] = [...loops, ...durations, ...ints(), ...utf8, ...floats, ...formats, ...parsed, ...singlesCases, ...singlesFormats, ...singlesParsed, ...dyns, ...cells, ...shared, ...locks, ...guards, ...maps, ...mappedLocks, ...rwlocks, ...onces, ...bounds, ...unsafeCells, ...edits, ...subslices, ...consumers, ...closures, ...elements, ...ranges, ...reducers, ...sliceEdits, ...drains, ...iters, ...hinted, ...peekedOthers, ...hints, ...reserves, ...setOps, ...zeros, ...writes, ...floatsMulAdds, ...singlesMulAdds, ...unlikeEqs, ...ownNes, ...rangeFroms, ...compares, ...unlikeCompares, ...rangeMethods, ...wraps(), ...refOrders, ...saturates(), ...methods(), ...separated, ...asciiCases, ...gets, ...capacityCases]; +export const cases: Uint8Array[] = [...loops, ...durations, ...ints(), ...utf8, ...floats, ...formats, ...parsed, ...singlesCases, ...singlesFormats, ...singlesParsed, ...dyns, ...cells, ...shared, ...locks, ...guards, ...maps, ...mappedLocks, ...tries, ...mutexMaps, ...printed, ...rwlocks, ...onces, ...bounds, ...unsafeCells, ...edits, ...subslices, ...consumers, ...closures, ...elements, ...ranges, ...reducers, ...sliceEdits, ...drains, ...iters, ...hinted, ...peekedOthers, ...hints, ...reserves, ...setOps, ...zeros, ...writes, ...floatsMulAdds, ...singlesMulAdds, ...unlikeEqs, ...ownNes, ...rangeFroms, ...compares, ...unlikeCompares, ...rangeMethods, ...wraps(), ...refOrders, ...saturates(), ...methods(), ...separated, ...asciiCases, ...gets, ...capacityCases]; diff --git a/ts/fixtures/lang/expected/borrows.ts b/ts/fixtures/lang/expected/borrows.ts index 4b47d9301..21afb269f 100644 --- a/ts/fixtures/lang/expected/borrows.ts +++ b/ts/fixtures/lang/expected/borrows.ts @@ -1,13 +1,13 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { copy$Option_tuple2_u64_u64, copy$tuple2_u64_u64, drop$Result_cell_RefMut_Vec_u8_cell_BorrowMutError, drop$Result_cell_RefMut_array4_u8_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Parts_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Tagged_cell_BorrowMutError, drop$Result_cell_RefMut_u32_cell_BorrowMutError, drop$Result_cell_Ref_Vec_u8_cell_BorrowError, drop$Result_cell_Ref_array4_u8_cell_BorrowError, drop$Result_cell_Ref_borrows_Parts_cell_BorrowError, drop$Result_cell_Ref_u32_cell_BorrowError, drop$Result_cell_Ref_u32_cell_Ref_borrows_Parts, drop$tuple2_Option_cell_Ref_borrows_Tagged_Option_cell_RefMut_borrows_Tagged, drop$tuple2_Result_cell_Ref_Vec_u8_cell_BorrowError_cell_Ref_Vec_u8, drop$tuple2_cell_RefMut_slice_u8_cell_RefMut_slice_u8, drop$tuple2_cell_Ref_Vec_u8_cell_Ref_u8, drop$tuple2_cell_Ref_str_cell_RefMut_u32 } from "./lib.ts"; +import { copy$Option_tuple2_u64_u64, copy$tuple2_u64_u64, drop$Result_cell_RefMut_Vec_u8_cell_BorrowMutError, drop$Result_cell_RefMut_array4_u8_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Parts_cell_BorrowMutError, drop$Result_cell_RefMut_borrows_Tagged_cell_BorrowMutError, drop$Result_cell_RefMut_u32_cell_BorrowMutError, drop$Result_cell_RefMut_u32_tuple2_cell_RefMut_borrows_Parts_unit, drop$Result_cell_Ref_Vec_u8_cell_BorrowError, drop$Result_cell_Ref_array4_u8_cell_BorrowError, drop$Result_cell_Ref_borrows_Parts_cell_BorrowError, drop$Result_cell_Ref_ref_str_tuple2_cell_Ref_tuple2_u8_ref_str_u8, drop$Result_cell_Ref_u32_cell_BorrowError, drop$Result_cell_Ref_u32_cell_Ref_borrows_Parts, drop$Result_cell_Ref_u32_tuple2_cell_Ref_borrows_Parts_unit, drop$tuple2_Option_cell_Ref_borrows_Tagged_Option_cell_RefMut_borrows_Tagged, drop$tuple2_Result_cell_Ref_Vec_u8_cell_BorrowError_cell_Ref_Vec_u8, drop$tuple2_cell_RefMut_slice_u8_cell_RefMut_slice_u8, drop$tuple2_cell_Ref_Vec_u8_cell_Ref_u8, drop$tuple2_cell_Ref_str_cell_RefMut_u32, drop$tuple3_cell_Ref_String_cell_Ref_array4_u8_cell_Ref_u32 } from "./lib.ts"; import * as borrows_Noisy from "./borrows.Noisy.ts"; import * as borrows_Parts from "./borrows.Parts.ts"; import * as borrows_Polling from "./borrows.Polling.ts"; import * as borrows_Tagged from "./borrows.Tagged.ts"; import { then$1jgls7y } from "./core/bool.ts"; -import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, mapOr$0qqbaqw as option_mapOr$0qqbaqw, mapOr$1dvn0au as option_mapOr$1dvn0au, mapOr$099335t as option_mapOr$099335t, mapOr$0ucbp7g as option_mapOr$0ucbp7g } from "./core/option.ts"; +import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, mapOr$0qqbaqw as option_mapOr$0qqbaqw, mapOr$1dvn0au as option_mapOr$1dvn0au, mapOr$099335t as option_mapOr$099335t, mapOr$0ucbp7g as option_mapOr$0ucbp7g, okOr$1oufq9m as option_okOr$1oufq9m } from "./core/option.ts"; import { map$0n1xnuw as result_map$0n1xnuw, unwrapOr$0k9nfhn as result_unwrapOr$0k9nfhn } from "./core/result.ts"; /** @@ -617,6 +617,208 @@ export function maps(data: rt.ByteSlice, out: rt.ByteVec): void { log[Symbol.dispose](); } +/** + * Each byte an operation on `RefCell`s through guards tried on their parts, its value the + * operand, and printed by `Debug` and `Display`; each puts what it read and printed + * through them, what the cell holds after they drop, whether it is still borrowed, and + * what dropped. + */ +export function tries(data: rt.ByteSlice, out: rt.ByteVec): void { + const log: rt.Arc> = new rt.Arc(new rt.Mutex(new rt.ByteVec())); + const parts: rt.RefCell = new rt.RefCell(new borrows_Parts.Parts(0, "", new rt.ByteVec(), new borrows_Tagged.Tagged(1, 0, rt.arcClone(log)), undefined, Uint8Array.of(1, 2, 3, 4))); + const number: rt.RefCell = new rt.RefCell(0); + for (const op of rt.byteSliceView(data)) { + const _23: rt.u8 = rt.u8Rem(op, 4); + if (_23 === 0) { + const _26: rt.RefCell = rt.refCellBorrowMut(parts); + const tried: rt.Result, [rt.RefCell, borrows_Noisy.Noisy]> = rt.refMutTryMap(_26, ((p9: borrows_Parts.Parts): rt.Result, borrows_Noisy.Noisy> => { + const _39: rt.Place | undefined = p9.maybe === undefined ? undefined : rt.place(() => p9.maybe as rt.u16, (value3) => p9.maybe = value3); + if (_39 === undefined) { + return rt.Err(new borrows_Noisy.Noisy(op, rt.arcClone(log))); + } + return rt.Ok(_39); + })(_26.value)); + if (tried.ok) { + const m: rt.RefCell = tried.value; + const _33: rt.u16 = rt.u16WrappingAdd(m.value, op as rt.u16); + m.value = _33; + rt.byteVecPush(out, borrowed$borrows_Parts(parts)); + rt.refMutRelease(m); + } else { + const whole: rt.RefCell = tried.error[0]; + const noisy: borrows_Noisy.Noisy = tried.error[1]; + whole.value.maybe = noisy.id as rt.u16; + rt.byteVecPush(out, borrowed$borrows_Parts(parts)); + noisy[Symbol.dispose](); + flush(log.value, out); + rt.byteVecPush(out, borrowed$borrows_Parts(parts)); + rt.refMutRelease(whole); + } + const _64: rt.u8 = borrowed$borrows_Parts(parts); + const _70: rt.RefCell = rt.refCellBorrow(parts); + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(_64, option_mapOr$099335t(_70.value.maybe, 253, (m2: rt.u16): rt.u8 => { + return m2 & 0xff; + })))); + rt.refRelease(_70); + const _73: rt.RefCell = rt.refCellBorrowMut(parts); + const part = ((p10: borrows_Parts.Parts): rt.Result => { + if ((op & 16) === 0) { + return rt.Ok(p10.bytes); + } + return rt.ERR; + })(_73.value); + const tried2: rt.Result, [rt.RefCell, undefined]> = rt.refMutTryMap(_73, part.ok ? rt.Ok(rt.partPlace(part.value)) : rt.Err(part.error)); + if (tried2.ok) { + const bytes: rt.RefCell = tried2.value; + rt.byteVecPush(bytes.value, op); + if ((op >> 5) === 7) { + const _87: rt.ByteVec = rt.byteVecOf(op, 2); + bytes.value = _87; + } + rt.refMutRelease(bytes); + } else { + const whole2: rt.RefCell = tried2.error[0]; + rt.byteVecPush(out, 240 | (whole2.value.bytes.length & 0xff)); + rt.refMutRelease(whole2); + } + rt.byteVecPush(out, borrowed$borrows_Parts(parts)); + const _105: rt.RefCell = rt.refCellBorrow(parts); + rt.byteVecExtendFromSlice(out, rt.byteVecSlice(_105.value.bytes)); + rt.refRelease(_105); + const _109: rt.RefCell = rt.refCellBorrowMut(number); + const n: rt.RefCell = rt.resultUnwrap(rt.refMutTryMap(_109, ((n3: rt.Place): rt.Result, rt.u8> => { + return rt.Ok(n3); + })(rt.valuePlace(_109))), (value: [rt.RefCell, rt.u8], f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.u32Debug(value[0].value, f), (f: rt.Formatter) => rt.u8Debug(value[1], f)], f)); + n.value = rt.u32Add(n.value, op as rt.u32); + rt.refMutRelease(n); + const _122: rt.RefCell = rt.refCellBorrow(number); + rt.byteVecPush(out, _122.value & 0xff); + rt.refRelease(_122); + } else if (_23 === 1) { + const other: rt.RefCell = rt.refCellBorrow(parts); + const _128: rt.RefCell = rt.refCellBorrow(parts); + const _127: rt.Result, [rt.RefCell, rt.usize]> = rt.refTryMap(_128, ((p8: borrows_Parts.Parts): rt.Result => { + return option_okOr$1oufq9m(rt.byteSliceLast(rt.byteVecSlice(p8.bytes)), p8.bytes.length); + })(_128.value)); + if (_127.ok) { + const last: rt.RefCell = _127.value; + const tried3: rt.Result, [rt.RefCell, rt.u8]> = rt.refTryMap(last, ((b3: rt.u8): rt.Result => { + if (!((b3 & 1) === 0)) { + return rt.Err(b3); + } + return rt.Ok(b3); + })(last.value)); + if (tried3.ok) { + const b: rt.RefCell = tried3.value; + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(b.value, borrowed$borrows_Parts(parts)))); + rt.refRelease(b); + } else { + const b2: rt.RefCell = tried3.error[0]; + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(b2.value ^ tried3.error[1], borrowed$borrows_Parts(parts)))); + rt.refRelease(b2); + } + } else { + const whole3: rt.RefCell = _127.error[0]; + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(whole3.value.n & 0xff, _127.error[1] & 0xff))); + rt.refRelease(whole3); + } + rt.byteVecPush(out, borrowed$borrows_Parts(parts)); + rt.refRelease(other); + rt.byteVecPush(out, borrowed$borrows_Parts(parts)); + } else if (_23 === 2) { + const _175: rt.RefCell = rt.refCellBorrowMut(parts); + _175.value.text = _175.value.text + String.fromCodePoint(rt.u8Add(97, rt.u8Rem(op, 26)) as rt.char); + rt.refMutRelease(_175); + const _183: rt.RefCell = rt.refCellBorrow(parts); + const text: rt.RefCell = rt.refMap(_183, ((p3: borrows_Parts.Parts): string => { + return p3.text; + })(_183.value)); + const _186: rt.RefCell = rt.refCellBorrow(parts); + const quad: rt.RefCell = rt.refMap(_186, rt.byteSliceView(((p4: borrows_Parts.Parts): rt.ByteSlice => { + return rt.byteSliceFrom(p4.quad); + })(_186.value))); + const n2: rt.RefCell = rt.refCellBorrow(number); + const args2: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtPad(f, text.value), (f: rt.Formatter) => rt.fmtStr(text.value, f), (f: rt.Formatter) => rt.fmtList(quad.value, f, rt.u8Debug), (f: rt.Formatter) => rt.u32Debug(n2.value, f), (f: rt.Formatter) => rt.u32Display(n2.value, f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args2[0], " ", args2[1], " [", rt.fmtSpec({ align: ">", width: 4 }, args2[0]), "] ", args2[2], " ", rt.fmtSpec({ alternate: true }, args2[3]), " ", args2[3], " ", rt.fmtSpec({ align: ">", width: 5 }, args2[4])]))); + drop$tuple3_cell_Ref_String_cell_Ref_array4_u8_cell_Ref_u32([text, quad, n2] as [rt.RefCell, rt.RefCell, rt.RefCell]); + const _211: rt.RefCell = rt.refCellBorrowMut(parts); + const maybe: rt.RefCell = rt.refMutMap(_211, ((p5: borrows_Parts.Parts): rt.Place => { + return rt.place(() => p5.maybe, (value2) => p5.maybe = value2); + })(_211.value)); + const args4: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtOption(maybe.value, f, rt.u16Debug)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args4[0], " ", rt.fmtSpec({ alternate: true }, args4[0])]))); + rt.refMutRelease(maybe); + const _227: rt.RefCell = rt.refCellBorrowMut(parts); + const bytes2: rt.RefCell = rt.refMutMap(_227, rt.partPlace(((p6: borrows_Parts.Parts): rt.ByteVec => { + return p6.bytes; + })(_227.value))); + const _237: rt.RefCell = rt.refCellBorrowMut(number); + const args6: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtList(rt.byteVecView(bytes2.value), f, rt.u8Debug), (f: rt.Formatter) => rt.u32Debug(_237.value, f)]; + const _233: string = rt.fmtFormat([args6[0], " ", args6[1]]); + rt.refMutRelease(_237); + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(_233)); + rt.refMutRelease(bytes2); + const pair: rt.RefCell<[rt.u8, string]> = new rt.RefCell([op, "pair"] as [rt.u8, string]); + const _256: rt.RefCell<[rt.u8, string]> = rt.refCellBorrow(pair); + const args8: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.u8Debug(_256.value[0], f), (f: rt.Formatter) => rt.fmtStr(_256.value[1], f)], f)]; + const _252: string = rt.fmtFormat([args8[0], " "]); + rt.refRelease(_256); + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(_252)); + const _270: rt.RefCell<[rt.u8, string]> = rt.refCellBorrowMut(pair); + const args10: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.u8Debug(_270.value[0], f), (f: rt.Formatter) => rt.fmtStr(_270.value[1], f)], f)]; + const _266: string = rt.fmtFormat([rt.fmtSpec({ alternate: true }, args10[0])]); + rt.refMutRelease(_270); + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(_266)); + const _277: rt.RefCell<[rt.u8, string]> = rt.refCellBorrow(pair); + const tried4: rt.Result, [rt.RefCell<[rt.u8, string]>, rt.u8]> = rt.refTryMap(_277, ((p7: [rt.u8, string]): rt.Result => { + if (!((p7[0] & 1) === 0)) { + return rt.Err(p7[0]); + } + return rt.Ok(p7[1]); + })(_277.value)); + const args12: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtResult(tried4, f, (value: rt.RefCell, f: rt.Formatter) => rt.fmtStr(value.value, f), (value: [rt.RefCell<[rt.u8, string]>, rt.u8], f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.u8Debug(value[0].value[0], f), (f: rt.Formatter) => rt.fmtStr(value[0].value[1], f)], f), (f: rt.Formatter) => rt.u8Debug(value[1], f)], f))]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args12[0]]))); + drop$Result_cell_Ref_ref_str_tuple2_cell_Ref_tuple2_u8_ref_str_u8(tried4); + } else { + const _291: rt.u8 = op >> 4; + if (_291 === 13) { + const _297: rt.RefCell = rt.refCellBorrowMut(parts); + const _whole: rt.RefCell = rt.resultUnwrapErr(rt.refMutTryMap(_297, ((_2: borrows_Parts.Parts): rt.Result, undefined> => { + return rt.ERR; + })(_297.value)), (value: rt.RefCell, f: rt.Formatter) => rt.u32Debug(value.value, f))[0]; + rt.refRelease(rt.refCellBorrow(parts)); + rt.refMutRelease(_whole); + } else if (_291 === 14) { + const _302: rt.RefCell = rt.refCellBorrow(parts); + const _n: rt.Result, [rt.RefCell, undefined]> = rt.refTryMap(_302, ((p2: borrows_Parts.Parts): rt.Result => { + return rt.Ok(p2.n); + })(_302.value)); + rt.refMutRelease(rt.refCellBorrowMut(parts)); + drop$Result_cell_Ref_u32_tuple2_cell_Ref_borrows_Parts_unit(_n); + } else if (_291 === 15) { + const _307: rt.RefCell = rt.refCellBorrowMut(parts); + const _n2: rt.Result, [rt.RefCell, undefined]> = rt.refMutTryMap(_307, ((p: borrows_Parts.Parts): rt.Result, undefined> => { + return rt.Ok(rt.place(() => p.n, (value) => p.n = value)); + })(_307.value)); + rt.refRelease(rt.refCellBorrow(parts)); + drop$Result_cell_RefMut_u32_tuple2_cell_RefMut_borrows_Parts_unit(_n2); + } else { + rt.byteVecPush(out, borrowed$borrows_Parts(parts)); + } + } + flush(log.value, out); + } + const last2: borrows_Parts.Parts = parts.value; + rt.byteVecExtendFromSlice(out, rt.byteSliceFrom(Uint8Array.of(last2.n & 0xff, option_mapOr$099335t(last2.maybe, 253, (m3: rt.u16): rt.u8 => { + return m3 & 0xff; + })))); + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(last2.text)); + rt.byteVecExtendFromSlice(out, rt.byteVecSlice(last2.bytes)); + last2[Symbol.dispose](); + flush(log.value, out); + log[Symbol.dispose](); +} + /** Whether `cell` is borrowed exclusively, as 2, shared, as 1, or not at all. */ export function borrowed$array4_u8(cell: rt.RefCell): rt.u8 { let _0!: rt.u8; diff --git a/ts/fixtures/lang/expected/core/option.ts b/ts/fixtures/lang/expected/core/option.ts index efd655972..edf47dcfc 100644 --- a/ts/fixtures/lang/expected/core/option.ts +++ b/ts/fixtures/lang/expected/core/option.ts @@ -1568,6 +1568,13 @@ export function okOr$1w08zpl(self: rt.ByteSlice | undefined, err: rt.usize): rt. return rt.Ok(self); } +export function okOr$1oufq9m(self: rt.u8 | undefined, err: rt.usize): rt.Result { + if (self === undefined) { + return rt.Err(err); + } + return rt.Ok(self); +} + export function okOr$0q0ogpn(self: [rt.ByteSlice, rt.ByteSlice] | undefined, err: rt.u8 | undefined): rt.Result<[rt.ByteSlice, rt.ByteSlice], rt.u8 | undefined> { if (self === undefined) { return rt.Err(err); diff --git a/ts/fixtures/lang/expected/core/result.ts b/ts/fixtures/lang/expected/core/result.ts index 1e8a81ea9..459479065 100644 --- a/ts/fixtures/lang/expected/core/result.ts +++ b/ts/fixtures/lang/expected/core/result.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rt from "../rt.ts"; -import { drop$Result_tuple2_usize_u8_sync_Arc_dyn_Any_marker_Sync_marker_Send } from "../lib.ts"; +import { drop$Result_sync_MutexGuard_locks_Tally_sync_TryLockError_sync_MutexGuard_locks_Tally, drop$Result_tuple2_usize_u8_sync_Arc_dyn_Any_marker_Sync_marker_Send } from "../lib.ts"; import { eq$09knsz6 as string_eq$09knsz6 } from "../alloc/string.ts"; import * as ByRef from "../collections.ByRef.ts"; import * as ByVal from "../collections.ByVal.ts"; @@ -634,6 +634,13 @@ export function map$0zzqmaz(self: rt.Result, rt.Arc, rt.Mutex>, op: (a0: rt.Mutex) => rt.Result, rt.Mutex>): rt.Result, rt.Mutex>, rt.Mutex> { + if (self.ok) { + return rt.Ok(op(self.value)); + } + return self as rt.Result, rt.Mutex>, rt.Mutex>; +} + export function map$1g6jln5(self: rt.Result, rt.RefCell>, op: (a0: rt.RefCell) => rt.Result, rt.RefCell>): rt.Result, rt.RefCell>, rt.RefCell> { if (self.ok) { return rt.Ok(op(self.value)); @@ -676,6 +683,20 @@ export function mapOr$0fbyajt(self: rt.Result return default_; } +export function mapOr$0666x4n(self: rt.Result, rt.TryLockError>>, default_: rt.u8, f: (a0: rt.Mutex) => rt.u8): rt.u8 { + let _0!: rt.u8; + if (self.ok) { + _0 = f(self.value); + } else { + _0 = default_; + } + if (!self.ok) { + drop$Result_sync_MutexGuard_locks_Tally_sync_TryLockError_sync_MutexGuard_locks_Tally(self); + return _0; + } + return _0; +} + export function mapOr$1mu5bvh(self: rt.Result, default_: rt.u8, f: (a0: rt.u8) => rt.u8): rt.u8 { if (self.ok) { return f(self.value); diff --git a/ts/fixtures/lang/expected/harness.ts b/ts/fixtures/lang/expected/harness.ts index 53f7a2b71..9ad40226a 100644 --- a/ts/fixtures/lang/expected/harness.ts +++ b/ts/fixtures/lang/expected/harness.ts @@ -5,7 +5,7 @@ import { args } from "./args.ts"; import { fromFns, maps as arrays_maps, lent as arrays_lent, asSlices, given, flattened as arrays_flattened } from "./arrays.ts"; import { bytes as ascii_bytes, chars as ascii_chars, slices as ascii_slices, parts as ascii_parts } from "./ascii.ts"; import { atomics } from "./atomics.ts"; -import { borrows as borrows_borrows, guards, maps as borrows_maps } from "./borrows.ts"; +import { borrows as borrows_borrows, guards, maps as borrows_maps, tries } from "./borrows.ts"; import { bounds as bounds_bounds } from "./bounds.ts"; import { boxes, places as boxes_places } from "./boxes.ts"; import { vecs as capacity_vecs, deques as capacity_deques, heaps as capacity_heaps, collects } from "./capacity.ts"; @@ -39,7 +39,7 @@ import { keys as keys_keys, overrides, bytes as keys_bytes, lentKeys, lentValues import { lengths } from "./lengths.ts"; import { lists as lists_lists } from "./lists.ts"; import { locals } from "./locals.ts"; -import { locks, relock, rwlocks, mapped } from "./locks.ts"; +import { locks, relock, rwlocks, mapped, mutexMapped, printed as locks_printed } from "./locks.ts"; import { u8s as methods_u8s, u16s as methods_u16s, u32s as methods_u32s, u64s as methods_u64s, u128s as methods_u128s, usizes as methods_usizes, i8s as methods_i8s, i16s as methods_i16s, i32s as methods_i32s, i64s as methods_i64s, i128s as methods_i128s, isizes as methods_isizes } from "./methods.ts"; import { nested as nested_nested } from "./nested.ts"; import { objects } from "./objects.ts"; @@ -261,6 +261,12 @@ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) 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], ["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], ["capacity::vecs", (a0152: rt.u8, a1152: rt.ByteSlice, a2152: rt.ByteVec): undefined => { capacity_vecs(a0152, a1152.data, a1152.start, a1152.length, a2152); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["capacity::deques", (a0153: rt.u8, a1153: rt.ByteSlice, a2153: rt.ByteVec): undefined => { diff --git a/ts/fixtures/lang/expected/lib.ts b/ts/fixtures/lang/expected/lib.ts index 996feba46..0998affce 100644 --- a/ts/fixtures/lang/expected/lib.ts +++ b/ts/fixtures/lang/expected/lib.ts @@ -824,6 +824,14 @@ export function drop$Option_cell_Ref_borrows_Tagged(_1: rt.RefCell, rt.Mutex>, rt.Mutex>): void { + if (_1.ok) { + drop$Result_sync_MappedMutexGuard_u64_sync_MappedMutexGuard_u64(_1.value); + } else { + rt.mutexUnlock(_1.error); + } +} + export function drop$Result_Result_sync_MappedRwLockWriteGuard_u64_sync_MappedRwLockWriteGuard_u64_sync_RwLockWriteGuard_locks_Tally(_1: rt.Result, rt.RefCell>, rt.RefCell>): void { if (_1.ok) { drop$Result_sync_MappedRwLockWriteGuard_u64_sync_MappedRwLockWriteGuard_u64(_1.value); @@ -862,6 +870,14 @@ export function drop$Result_cell_RefMut_u32_cell_BorrowMutError(_1: rt.Result, [rt.RefCell, undefined]>): void { + if (_1.ok) { + rt.refMutRelease(_1.value); + } else { + drop$tuple2_cell_RefMut_borrows_Parts_unit(_1.error); + } +} + export function drop$Result_cell_Ref_Vec_u8_cell_BorrowError(_1: rt.Result, rt.BorrowError>): void { if (_1.ok) { rt.refRelease(_1.value); @@ -880,6 +896,14 @@ export function drop$Result_cell_Ref_borrows_Parts_cell_BorrowError(_1: rt.Resul } } +export function drop$Result_cell_Ref_ref_str_tuple2_cell_Ref_tuple2_u8_ref_str_u8(_1: rt.Result, [rt.RefCell<[rt.u8, string]>, rt.u8]>): void { + if (_1.ok) { + rt.refRelease(_1.value); + } else { + drop$tuple2_cell_Ref_tuple2_u8_ref_str_u8(_1.error); + } +} + export function drop$Result_cell_Ref_u32_cell_BorrowError(_1: rt.Result, rt.BorrowError>): void { if (_1.ok) { rt.refRelease(_1.value); @@ -894,6 +918,14 @@ export function drop$Result_cell_Ref_u32_cell_Ref_borrows_Parts(_1: rt.Result, [rt.RefCell, undefined]>): void { + if (_1.ok) { + rt.refRelease(_1.value); + } else { + drop$tuple2_cell_Ref_borrows_Parts_unit(_1.error); + } +} + export function drop$Result_results_Logged_results_Logged(_1: rt.Result): void { if (_1.ok) { _1.value[Symbol.dispose](); @@ -902,6 +934,14 @@ export function drop$Result_results_Logged_results_Logged(_1: rt.Result, rt.Mutex>): void { + if (_1.ok) { + rt.mutexUnlock(_1.value); + } else { + rt.mutexUnlock(_1.error); + } +} + export function drop$Result_sync_MappedRwLockWriteGuard_u64_sync_MappedRwLockWriteGuard_u64(_1: rt.Result, rt.RefCell>): void { if (_1.ok) { rt.refMutRelease(_1.value); @@ -1048,6 +1088,10 @@ export function drop$tuple2_Vec_tuples_Logged_tuples_Tally(_1: [tuples_Logged.Lo rt.dropAll(_1[0]); } +export function drop$tuple2_cell_RefMut_borrows_Parts_unit(_1: [rt.RefCell, undefined]): void { + rt.refMutRelease(_1[0]); +} + export function drop$tuple2_cell_RefMut_slice_u8_cell_RefMut_slice_u8(_1: [rt.RefCell, rt.RefCell]): void { rt.refMutRelease(_1[0]); rt.refMutRelease(_1[1]); @@ -1058,11 +1102,19 @@ export function drop$tuple2_cell_Ref_Vec_u8_cell_Ref_u8(_1: [rt.RefCell, undefined]): void { + rt.refRelease(_1[0]); +} + export function drop$tuple2_cell_Ref_str_cell_RefMut_u32(_1: [rt.RefCell, rt.RefCell]): void { rt.refRelease(_1[0]); rt.refMutRelease(_1[1]); } +export function drop$tuple2_cell_Ref_tuple2_u8_ref_str_u8(_1: [rt.RefCell<[rt.u8, string]>, rt.u8]): void { + rt.refRelease(_1[0]); +} + export function drop$tuple2_sync_Arc_collections_Picky_u8(_1: [rt.Arc, rt.u8]): void { _1[0][Symbol.dispose](); } @@ -1087,6 +1139,12 @@ export function drop$tuple2_u8_tuples_Logged(_1: [rt.u8, tuples_Logged.Logged]): _1[1][Symbol.dispose](); } +export function drop$tuple3_cell_Ref_String_cell_Ref_array4_u8_cell_Ref_u32(_1: [rt.RefCell, rt.RefCell, rt.RefCell]): void { + rt.refRelease(_1[0]); + rt.refRelease(_1[1]); + rt.refRelease(_1[2]); +} + export function drop$tuple4_Vec_Vec_joins_Logged_array2_array2_joins_Logged_joins_Logged_Vec_joins_Logged(_1: [joins_Logged.Logged[][], joins_Logged.Logged[][], joins_Logged.Logged, joins_Logged.Logged[]]): void { rt.dropAll(_1[0], drop$Vec_joins_Logged); rt.dropAll(_1[1], drop$array2_joins_Logged); diff --git a/ts/fixtures/lang/expected/locks.ts b/ts/fixtures/lang/expected/locks.ts index b80893d2f..74de192f5 100644 --- a/ts/fixtures/lang/expected/locks.ts +++ b/ts/fixtures/lang/expected/locks.ts @@ -1,10 +1,10 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; -import { drop$Result_Result_sync_MappedRwLockWriteGuard_u64_sync_MappedRwLockWriteGuard_u64_sync_RwLockWriteGuard_locks_Tally, drop$Result_sync_MutexGuard_locks_Tally_sync_TryLockError_sync_MutexGuard_locks_Tally, drop$Result_sync_MutexGuard_u32_sync_TryLockError_sync_MutexGuard_u32, drop$Result_sync_RwLockReadGuard_locks_Tally_sync_TryLockError_sync_RwLockReadGuard_locks_Tally, drop$Result_sync_RwLockWriteGuard_locks_Tally_sync_TryLockError_sync_RwLockWriteGuard_locks_Tally } from "./lib.ts"; +import { drop$Result_Result_sync_MappedMutexGuard_u64_sync_MappedMutexGuard_u64_sync_MutexGuard_locks_Tally, drop$Result_Result_sync_MappedRwLockWriteGuard_u64_sync_MappedRwLockWriteGuard_u64_sync_RwLockWriteGuard_locks_Tally, drop$Result_sync_MutexGuard_locks_Tally_sync_TryLockError_sync_MutexGuard_locks_Tally, drop$Result_sync_MutexGuard_u32_sync_TryLockError_sync_MutexGuard_u32, drop$Result_sync_RwLockReadGuard_locks_Tally_sync_TryLockError_sync_RwLockReadGuard_locks_Tally, drop$Result_sync_RwLockWriteGuard_locks_Tally_sync_TryLockError_sync_RwLockWriteGuard_locks_Tally } from "./lib.ts"; import { thenSome$0sf2w1i, thenSome$0d5am30 } from "./core/bool.ts"; -import { isSomeAnd$09tpccf, unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr } from "./core/option.ts"; -import { map$1g6jln5 as result_map$1g6jln5 } from "./core/result.ts"; +import { isSomeAnd$09tpccf, unwrapOr$1iemzn8 as option_unwrapOr$1iemzn8, unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr } from "./core/option.ts"; +import { map$02txiwz as result_map$02txiwz, map$1g6jln5 as result_map$1g6jln5, mapOr$0666x4n as result_mapOr$0666x4n } from "./core/result.ts"; import * as locks_Lock from "./locks.Lock.ts"; import * as locks_Tally from "./locks.Tally.ts"; @@ -361,4 +361,230 @@ export function mapped(data: rt.ByteSlice, out: rt.ByteVec): void { rt.byteVecExtendFromSlice(out, rt.byteVecSlice(last3.seen)); } +/** + * Each byte an operation on mutexes through guards mapped and filtered to parts of their + * values, its value the operand; each puts what it read through them, what the mutex + * holds after they drop, and whether it is still locked. Locking it again while one is + * held aborts on wasm32, with no panic message to compare. + */ +export function mutexMapped(data: rt.ByteSlice, out: rt.ByteVec): void { + const mutex: rt.Mutex = new rt.Mutex(locks_Tally.default$Default()); + const number: rt.Mutex = new rt.Mutex(0); + const locked: (a0: rt.ByteVec) => undefined = (out2: rt.ByteVec): undefined => { + let tally!: boolean; + let number2!: boolean; + const _45: rt.Result, rt.TryLockError>> = rt.mutexTryLock(mutex); + if (!_45.ok) { + tally = true; + } else { + tally = false; + } + drop$Result_sync_MutexGuard_locks_Tally_sync_TryLockError_sync_MutexGuard_locks_Tally(_45); + const _8: rt.Result, rt.TryLockError>> = rt.mutexTryLock(number); + if (!_8.ok) { + number2 = true; + } else { + number2 = false; + } + drop$Result_sync_MutexGuard_u32_sync_TryLockError_sync_MutexGuard_u32(_8); + rt.byteVecPush(out2, ((((tally ? 1 : 0)) << 1) & 0xff) | ((number2 ? 1 : 0))); + }; + for (const op of rt.byteSliceView(data)) { + const _15: rt.u8 = rt.u8Rem(op, 4); + if (_15 === 0) { + const _18: rt.Mutex = rt.mutexLock(mutex); + const n: rt.Mutex = rt.mutexMap(_18, ((t7: locks_Tally.Tally): rt.Place => { + return rt.place(() => t7.n, (value3) => t7.n = value3); + })(_18.value)); + n.value = rt.u64Add(n.value, op as rt.u64); + locked(out); + const _29: rt.Mutex = n; + const again: rt.Mutex = rt.mutexMap(_29, ((n8: rt.Place): rt.Place => { + return n8; + })(rt.valuePlace(_29))); + const _30: rt.u64 = rt.u64WrappingMul(again.value, 3); + again.value = _30; + rt.mutexUnlock(again); + locked(out); + const _46: rt.Mutex = rt.mutexLock(mutex); + rt.byteVecPush(out, rt.u64Low(_46.value.n) & 0xff); + rt.mutexUnlock(_46); + const _50: rt.Mutex = rt.mutexLock(number); + const whole: rt.Mutex = rt.mutexMap(_50, ((n9: rt.Place): rt.Place => { + return n9; + })(rt.valuePlace(_50))); + const _58: rt.u32 = rt.u32Add(rt.u32WrappingMul(whole.value, 5), op as rt.u32); + whole.value = _58; + locked(out); + rt.mutexUnlock(whole); + const _71: rt.Mutex = rt.mutexLock(number); + rt.byteVecPush(out, _71.value & 0xff); + rt.mutexUnlock(_71); + } else if (_15 === 1) { + const _75: rt.Mutex = rt.mutexLock(mutex); + const seen: rt.Mutex = rt.mutexMap(_75, rt.partPlace(((t5: locks_Tally.Tally): rt.ByteVec => { + return t5.seen; + })(_75.value))); + rt.byteVecPush(seen.value, op); + if ((op >> 4) === 15) { + const _85: rt.ByteVec = rt.byteVecFrom(Uint8Array.of(op, op)); + seen.value = _85; + } + locked(out); + rt.mutexUnlock(seen); + const _99: rt.Mutex = rt.mutexLock(mutex); + rt.byteVecExtendFromSlice(out, rt.byteVecSlice(_99.value.seen)); + rt.mutexUnlock(_99); + if ((op >> 4) === 14) { + const _107: rt.Mutex = rt.mutexLock(mutex); + const whole2: rt.Mutex = rt.mutexMap(_107, rt.partPlace(((t6: locks_Tally.Tally): locks_Tally.Tally => { + return t6; + })(_107.value))); + const _110: locks_Tally.Tally = { n: op as rt.u64, seen: rt.byteVecFrom(Uint8Array.of(op)) }; + whole2.value = _110; + rt.mutexUnlock(whole2); + const _123: rt.Mutex = rt.mutexLock(mutex); + rt.byteVecPush(out, rt.u64Low(_123.value.n) & 0xff); + rt.mutexUnlock(_123); + } + } else if (_15 === 2) { + const _128: rt.Mutex = rt.mutexLock(mutex); + const _127: rt.Result, rt.Mutex> = rt.mutexFilterMap(_128, ((t3: locks_Tally.Tally): rt.Place | undefined => { + return rt.sliceLastMut(rt.byteSliceView(rt.byteVecSlice(t3.seen))); + })(_128.value)); + if (_127.ok) { + const last: rt.Mutex = _127.value; + const _133: rt.u8 = rt.u8WrappingAdd(last.value, op); + last.value = _133; + rt.mutexUnlock(last); + } else { + const whole3: rt.Mutex = _127.error; + rt.byteVecPush(whole3.value.seen, op); + rt.mutexUnlock(whole3); + } + locked(out); + const _147: rt.Mutex = rt.mutexLock(mutex); + const _151: rt.Result, rt.Mutex>, rt.Mutex> = result_map$02txiwz(rt.mutexFilterMap(_147, ((t4: locks_Tally.Tally): rt.Place | undefined => { + return rt.place(() => t4.n, (value2) => t4.n = value2); + })(_147.value)), (n6: rt.Mutex): rt.Result, rt.Mutex> => { + return rt.mutexFilterMap(n6, ((n7: rt.Place): rt.Place | undefined => { + return thenSome$0sf2w1i(rt.u64And(n7.v, 1) === 0, n7); + })(rt.valuePlace(n6))); + }); + if (_151.ok) { + if (_151.value.ok) { + const n3: rt.Mutex = _151.value.value; + n3.value = rt.u64Add(n3.value, 1); + rt.mutexUnlock(n3); + } else { + const n4: rt.Mutex = _151.value.error; + locked(out); + n4.value = rt.u64Add(n4.value, 2); + rt.mutexUnlock(n4); + } + } else { + rt.byteVecPush(out, 254); + } + if (!_151.ok) { + drop$Result_Result_sync_MappedMutexGuard_u64_sync_MappedMutexGuard_u64_sync_MutexGuard_locks_Tally(_151); + } + locked(out); + const _173: rt.Mutex = rt.mutexLock(mutex); + rt.byteVecPush(out, rt.u64Low(_173.value.n) & 0xff); + rt.mutexUnlock(_173); + const _181: rt.Mutex = rt.mutexLock(mutex); + rt.byteVecExtendFromSlice(out, rt.byteVecSlice(_181.value.seen)); + rt.mutexUnlock(_181); + } else { + const _185: rt.Mutex = rt.mutexLock(mutex); + const n5: rt.Mutex = rt.mutexMap(_185, ((t: locks_Tally.Tally): rt.Place => { + return rt.place(() => t.n, (value) => t.n = value); + })(_185.value)); + locked(out); + const _195: rt.Result, rt.TryLockError>> = rt.mutexTryLock(mutex); + rt.byteVecPush(out, !_195.ok ? 1 : 0); + drop$Result_sync_MutexGuard_locks_Tally_sync_TryLockError_sync_MutexGuard_locks_Tally(_195); + rt.mutexUnlock(n5); + rt.byteVecPush(out, result_mapOr$0666x4n(rt.mutexTryLock(mutex), 254, (t2: rt.Mutex): rt.u8 => { + const _0: rt.u8 = t2.value.seen.length & 0xff; + rt.mutexUnlock(t2); + return _0; + })); + locked(out); + } + } + const last2: locks_Tally.Tally = mutex.value; + rt.byteVecPush(out, rt.u64Low(last2.n) & 0xff); + rt.byteVecExtendFromSlice(out, rt.byteVecSlice(last2.seen)); + rt.byteVecPush(out, number.value & 0xff); +} + +/** + * Each lock's guards printed, plain and mapped, by `Debug` and `Display` and with options, + * as std writes what each lends. + */ +export function printed(data: rt.ByteSlice, out: rt.ByteVec): void { + const text: string = rt.stringExtendChars("", rt.iterMap(rt.iterSlice(rt.byteSliceView(data)), (b: rt.u8): rt.char => { + return rt.u8Add(97, rt.u8Rem(b, 26)) as rt.char; + })); + const mutex: rt.Mutex<[rt.u8, string]> = new rt.Mutex([data.length & 0xff, text] as [rt.u8, string]); + const lock: rt.RefCell<[rt.ByteVec, string]> = new rt.RefCell([rt.byteSliceToByteVec(data), text] as [rt.ByteVec, string]); + const guard: rt.Mutex<[rt.u8, string]> = rt.mutexLock(mutex); + const args2: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.u8Debug(guard.value[0], f), (f: rt.Formatter) => rt.fmtStr(guard.value[1], f)], f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args2[0], " ", rt.fmtSpec({ alternate: true }, args2[0])]))); + const n: rt.Mutex = rt.mutexMap(guard, ((t: [rt.u8, string]): rt.Place => { + return rt.place(() => t[0], (value) => t[0] = value); + })(guard.value)); + const args4: rt.Fmt[] = [(f: rt.Formatter) => rt.u8Display(n.value, f), (f: rt.Formatter) => rt.u8Debug(n.value, f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args4[0], " ", args4[1], " ", rt.fmtSpec({ align: ">", width: 4 }, args4[0]), " ", rt.fmtSpec({ zero: true, width: 4 }, args4[0])]))); + const n2: rt.Mutex = rt.mutexMap(n, ((n3: rt.Place): rt.Place => { + return n3; + })(rt.valuePlace(n))); + const args6: rt.Fmt[] = [(f: rt.Formatter) => rt.u8Display(n2.value, f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat(["[", rt.fmtSpec({ align: "<", width: 3 }, args6[0]), "]"]))); + rt.mutexUnlock(n2); + const _58: rt.Mutex<[rt.u8, string]> = rt.mutexLock(mutex); + const text2: rt.Mutex = rt.mutexMap(_58, ((t2: [rt.u8, string]): rt.Place => { + return rt.place(() => t2[1], (value2) => t2[1] = value2); + })(_58.value)); + const args8: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtPad(f, text2.value), (f: rt.Formatter) => rt.fmtStr(text2.value, f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args8[0], " ", args8[1], " [", rt.fmtSpec({ align: "^", width: 9 }, args8[0]), "]"]))); + rt.mutexUnlock(text2); + const read: rt.RefCell<[rt.ByteVec, string]> = rt.rwLockRead(lock); + const args10: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.fmtList(rt.byteVecView(read.value[0]), f, rt.u8Debug), (f: rt.Formatter) => rt.fmtStr(read.value[1], f)], f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args10[0]]))); + const bytes: rt.RefCell = rt.refMap(read, ((t3: [rt.ByteVec, string]): rt.ByteVec => { + return t3[0]; + })(read.value)); + const args12: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtList(rt.byteVecView(bytes.value), f, rt.u8Debug)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args12[0], " ", rt.fmtSpec({ alternate: true }, args12[0])]))); + const _104: rt.RefCell<[rt.ByteVec, string]> = rt.rwLockRead(lock); + const text3: rt.RefCell = rt.refMap(_104, ((t4: [rt.ByteVec, string]): string => { + return t4[1]; + })(_104.value)); + const first: rt.RefCell = rt.refMap(text3, ((t5: string): string => { + return option_unwrapOr$1iemzn8(rt.strGet(t5, 0, 1), "-"); + })(text3.value)); + const args14: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtPad(f, first.value), (f: rt.Formatter) => rt.fmtStr(first.value, f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args14[0], " ", args14[1], " ", rt.fmtSpec({ align: ">", width: 3 }, args14[0])]))); + rt.refRelease(first); + rt.refRelease(bytes); + const write: rt.RefCell<[rt.ByteVec, string]> = rt.rwLockWrite(lock); + const args16: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtTuple([(f: rt.Formatter) => rt.fmtList(rt.byteVecView(write.value[0]), f, rt.u8Debug), (f: rt.Formatter) => rt.fmtStr(write.value[1], f)], f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args16[0]]))); + const text4: rt.RefCell = rt.refMutMap(write, ((t6: [rt.ByteVec, string]): rt.Place => { + return rt.place(() => t6[1], (value3) => t6[1] = value3); + })(write.value)); + text4.value = text4.value + "!"; + const args18: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtPad(f, text4.value), (f: rt.Formatter) => rt.fmtStr(text4.value, f)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([args18[0], " ", args18[1]]))); + const _154: rt.RefCell = text4; + const text5: rt.RefCell = rt.refMutMap(_154, ((t7: rt.Place): rt.Place => { + return t7; + })(rt.valuePlace(_154))); + const args20: rt.Fmt[] = [(f: rt.Formatter) => rt.fmtPad(f, text5.value)]; + rt.byteVecExtendFromSlice(out, rt.stringAsBytes(rt.fmtFormat([rt.fmtSpec({ align: ">", width: 12 }, args20[0])]))); + rt.refMutRelease(text5); +} + //# sourceMappingURL=locks.ts.map diff --git a/ts/fixtures/lang/src/borrows.rs b/ts/fixtures/lang/src/borrows.rs index 66ee4491f..aa759f971 100644 --- a/ts/fixtures/lang/src/borrows.rs +++ b/ts/fixtures/lang/src/borrows.rs @@ -2,8 +2,9 @@ //! tuples, and values that drop; borrowed shared and exclusive, held in bindings and //! dropped; and borrowed again while held, which panics as wasm32's does. `RefCell`s //! cloned, written through `get_mut`, taken apart, tried, and borrowed through mapped and -//! cloned guards, each released as it drops; and guards mapped, filtered, and split to -//! parts JS copies and parts it doesn't, written through, released as the last drops. +//! cloned guards, each released as it drops; guards mapped, filtered, and split to parts +//! JS copies and parts it doesn't, written through, released as the last drops; and +//! guards tried on their parts, given back beside an error, and printed. use std::cell::{Cell, Ref, RefCell, RefMut}; use std::sync::{Arc, Mutex}; @@ -469,6 +470,137 @@ pub fn maps(data: &[u8], out: &mut Vec) { flush(&log, out); } +/// Each byte an operation on `RefCell`s through guards tried on their parts, its value the +/// operand, and printed by `Debug` and `Display`; each puts what it read and printed +/// through them, what the cell holds after they drop, whether it is still borrowed, and +/// what dropped. +pub fn tries(data: &[u8], out: &mut Vec) { + let log = Arc::new(Mutex::new(Vec::new())); + let parts = RefCell::new(Parts { + n: 0, + text: String::new(), + bytes: Vec::new(), + tagged: Tagged { + id: 1, + n: 0, + log: log.clone(), + }, + maybe: None, + quad: [1, 2, 3, 4], + }); + let number = RefCell::new(0u32); + for &op in data { + match op % 4 { + // Tried exclusive, to a number JS copies, with an error that drops, and to a + // `Vec` with one that doesn't, each giving the guard back beside its error. + 0 => { + let tried = RefMut::try_map(parts.borrow_mut(), |p| match p.maybe.as_mut() { + Some(m) => Ok(m), + None => Err(Noisy { + id: op, + log: log.clone(), + }), + }); + match tried { + Ok(mut m) => { + *m = m.wrapping_add(u16::from(op)); + out.push(borrowed(&parts)); + } + Err((mut whole, noisy)) => { + whole.maybe = Some(u16::from(noisy.id)); + out.push(borrowed(&parts)); + drop(noisy); + flush(&log, out); + out.push(borrowed(&parts)); + } + } + out.extend_from_slice(&[borrowed(&parts), parts.borrow().maybe.map_or(0xfd, |m| m as u8)]); + let tried = RefMut::try_map(parts.borrow_mut(), |p| match op & 0x10 { + 0 => Ok(&mut p.bytes), + _ => Err(()), + }); + match tried { + Ok(mut bytes) => { + bytes.push(op); + if op >> 5 == 7 { + *bytes = vec![op; 2]; + } + } + Err((whole, ())) => out.push(0xf0 | whole.bytes.len() as u8), + } + out.push(borrowed(&parts)); + out.extend_from_slice(&parts.borrow().bytes); + let mut n = RefMut::try_map(number.borrow_mut(), |n| Ok::<_, u8>(n)).unwrap(); + *n += u32::from(op); + drop(n); + out.push(*number.borrow() as u8); + } + // Tried shared, and again on what that lends, and its guard back while another + // shares the cell. + 1 => { + let other = parts.borrow(); + match Ref::try_map(parts.borrow(), |p| p.bytes.last().ok_or(p.bytes.len())) { + Ok(last) => { + let tried = Ref::try_map(last, |b| if *b & 1 == 0 { Ok(b) } else { Err(*b) }); + match tried { + Ok(b) => out.extend_from_slice(&[*b, borrowed(&parts)]), + Err((b, e)) => out.extend_from_slice(&[*b ^ e, borrowed(&parts)]), + } + } + Err((whole, len)) => out.extend_from_slice(&[whole.n as u8, len as u8]), + } + out.push(borrowed(&parts)); + drop(other); + out.push(borrowed(&parts)); + } + // Printed through guards, plain, mapped, and tried, by each trait and with options. + 2 => { + parts.borrow_mut().text.push(char::from(b'a' + op % 26)); + let text = Ref::map(parts.borrow(), |p| &p.text); + let quad = Ref::map(parts.borrow(), |p| &p.quad); + let n = number.borrow(); + let printed = format!("{text} {text:?} [{text:>4}] {quad:?} {n:#?} {n:?} {n:>5}"); + out.extend_from_slice(printed.as_bytes()); + drop((text, quad, n)); + let maybe = RefMut::map(parts.borrow_mut(), |p| &mut p.maybe); + out.extend_from_slice(format!("{maybe:?} {maybe:#?}").as_bytes()); + drop(maybe); + let bytes = RefMut::map(parts.borrow_mut(), |p| &mut p.bytes); + out.extend_from_slice(format!("{bytes:?} {:?}", number.borrow_mut()).as_bytes()); + drop(bytes); + let pair = RefCell::new((op, "pair")); + out.extend_from_slice(format!("{:?} ", pair.borrow()).as_bytes()); + out.extend_from_slice(format!("{:#?}", pair.borrow_mut()).as_bytes()); + let tried = Ref::try_map(pair.borrow(), |p| if p.0 & 1 == 0 { Ok(&p.1) } else { Err(p.0) }); + out.extend_from_slice(format!("{tried:?}").as_bytes()); + } + // Tried, as it borrows again while the guard it gave back holds it, which panics. + _ => match op >> 4 { + 0xd => { + let (_whole, ()) = RefMut::try_map(parts.borrow_mut(), |_| Err::<&mut u32, _>(())).unwrap_err(); + let _r = parts.borrow(); + } + 0xe => { + let _n = Ref::try_map(parts.borrow(), |p| Ok::<_, ()>(&p.n)); + let _w = parts.borrow_mut(); + } + 0xf => { + let _n = RefMut::try_map(parts.borrow_mut(), |p| Ok::<_, ()>(&mut p.n)); + let _r = parts.borrow(); + } + _ => out.push(borrowed(&parts)), + }, + } + flush(&log, out); + } + let last = parts.into_inner(); + out.extend_from_slice(&[last.n as u8, last.maybe.map_or(0xfd, |m| m as u8)]); + out.extend_from_slice(last.text.as_bytes()); + out.extend_from_slice(&last.bytes); + drop(last); + flush(&log, out); +} + /// Whether `cell` is borrowed exclusively, as 2, shared, as 1, or not at all. fn borrowed(cell: &RefCell) -> u8 { match (cell.try_borrow().is_err(), cell.try_borrow_mut().is_err()) { diff --git a/ts/fixtures/lang/src/harness.rs b/ts/fixtures/lang/src/harness.rs index e1583de67..11c181f32 100644 --- a/ts/fixtures/lang/src/harness.rs +++ b/ts/fixtures/lang/src/harness.rs @@ -275,6 +275,9 @@ fn cases() -> Vec<(&'static str, Case)> { ("strs::gets", strs::gets), ("borrows::maps", |_, data, out| borrows::maps(data, out)), ("locks::mapped", |_, data, out| locks::mapped(data, out)), + ("borrows::tries", |_, data, out| borrows::tries(data, out)), + ("locks::mutex_mapped", |_, data, out| locks::mutex_mapped(data, out)), + ("locks::printed", |_, data, out| locks::printed(data, out)), ("capacity::vecs", capacity::vecs), ("capacity::deques", capacity::deques), ("capacity::heaps", capacity::heaps), diff --git a/ts/fixtures/lang/src/lib.rs b/ts/fixtures/lang/src/lib.rs index e6c00b0f3..561911493 100644 --- a/ts/fixtures/lang/src/lib.rs +++ b/ts/fixtures/lang/src/lib.rs @@ -29,6 +29,7 @@ iter_order_by, mapped_lock_guards, once_cell_try, + refcell_try_map, split_as_slice, wrapping_int_impl, wrapping_next_power_of_two diff --git a/ts/fixtures/lang/src/locks.rs b/ts/fixtures/lang/src/locks.rs index 7d7014575..ce78e20d1 100644 --- a/ts/fixtures/lang/src/locks.rs +++ b/ts/fixtures/lang/src/locks.rs @@ -3,12 +3,13 @@ //! comes, read and written by `get_mut` without locking, taken apart, tried while held and free, and locked again //! while held, which panics as wasm32's does; and read-write locks, read and written, //! tried while read and while written, written through `get_mut`, taken apart, and read -//! and written through guards mapped and filtered to parts of their values. +//! and written through guards mapped and filtered to parts of their values, a mutex's +//! included; and each lock's guards printed. use std::fmt; use std::sync::{ - Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard, - TryLockError, + Arc, MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, PoisonError, RwLock, + RwLockReadGuard, RwLockWriteGuard, TryLockError, }; /// A value a mutex holds, which `Default` makes. @@ -245,3 +246,126 @@ pub fn mapped(data: &[u8], out: &mut Vec) { out.push(last.n as u8); out.extend_from_slice(&last.seen); } + +/// Each byte an operation on mutexes through guards mapped and filtered to parts of their +/// values, its value the operand; each puts what it read through them, what the mutex +/// holds after they drop, and whether it is still locked. Locking it again while one is +/// held aborts on wasm32, with no panic message to compare. +pub fn mutex_mapped(data: &[u8], out: &mut Vec) { + let mutex = Mutex::new(Tally::default()); + let number = Mutex::new(0u32); + let locked = |out: &mut Vec| { + let tally = matches!(mutex.try_lock(), Err(TryLockError::WouldBlock)); + let number = matches!(number.try_lock(), Err(TryLockError::WouldBlock)); + out.push(u8::from(tally) << 1 | u8::from(number)); + }; + for &op in data { + match op % 4 { + // A number written through a guard mapped to it, and through that one's map. + 0 => { + let mut n = MutexGuard::map(mutex.lock().unwrap(), |t| &mut t.n); + *n += u64::from(op); + locked(out); + let mut again = MappedMutexGuard::map(n, |n| n); + *again = again.wrapping_mul(3); + drop(again); + locked(out); + out.push(mutex.lock().unwrap().n as u8); + let mut whole = MutexGuard::map(number.lock().unwrap(), |n| n); + *whole = whole.wrapping_mul(5) + u32::from(op); + locked(out); + drop(whole); + out.push(*number.lock().unwrap() as u8); + } + // A `Vec`, which JS doesn't copy, pushed to and written whole, and the whole value + // written through a guard mapped to it. + 1 => { + let mut seen = MutexGuard::map(mutex.lock().unwrap(), |t| &mut t.seen); + seen.push(op); + if op >> 4 == 0xf { + *seen = vec![op, op]; + } + locked(out); + drop(seen); + out.extend_from_slice(&mutex.lock().unwrap().seen); + if op >> 4 == 0xe { + let mut whole = MutexGuard::map(mutex.lock().unwrap(), |t| t); + *whole = Tally { + n: u64::from(op), + seen: vec![op], + }; + drop(whole); + out.push(mutex.lock().unwrap().n as u8); + } + } + // Filtered, which gives the guard back where there is no part. + 2 => { + match MutexGuard::filter_map(mutex.lock().unwrap(), |t| t.seen.last_mut()) { + Ok(mut last) => *last = last.wrapping_add(op), + Err(mut whole) => whole.seen.push(op), + } + locked(out); + let n = MutexGuard::filter_map(mutex.lock().unwrap(), |t| Some(&mut t.n)); + match n.map(|n| MappedMutexGuard::filter_map(n, |n| (*n & 1 == 0).then_some(n))) { + Ok(Ok(mut n)) => *n += 1, + Ok(Err(mut n)) => { + locked(out); + *n += 2; + } + Err(_) => out.push(0xfe), + } + locked(out); + out.push(mutex.lock().unwrap().n as u8); + out.extend_from_slice(&mutex.lock().unwrap().seen); + } + // Tried while a mapped guard holds it, and once that guard drops. + _ => { + let n = MutexGuard::map(mutex.lock().unwrap(), |t| &mut t.n); + locked(out); + out.push(mutex.try_lock().is_err() as u8); + drop(n); + out.push(mutex.try_lock().map_or(0xfe, |t| t.seen.len() as u8)); + locked(out); + } + } + } + let last = mutex.into_inner().unwrap(); + out.push(last.n as u8); + out.extend_from_slice(&last.seen); + out.push(number.into_inner().unwrap() as u8); +} + +/// Each lock's guards printed, plain and mapped, by `Debug` and `Display` and with options, +/// as std writes what each lends. +pub fn printed(data: &[u8], out: &mut Vec) { + let text: String = data.iter().map(|b| char::from(b'a' + b % 26)).collect(); + let mutex = Mutex::new((data.len() as u8, text.clone())); + let lock = RwLock::new((data.to_vec(), text)); + { + let guard = mutex.lock().unwrap(); + out.extend_from_slice(format!("{guard:?} {guard:#?}").as_bytes()); + let n = MutexGuard::map(guard, |t| &mut t.0); + out.extend_from_slice(format!("{n} {n:?} {n:>4} {n:04}").as_bytes()); + let n = MappedMutexGuard::map(n, |n| n); + out.extend_from_slice(format!("[{n:<3}]").as_bytes()); + } + let text = MutexGuard::map(mutex.lock().unwrap(), |t| &mut t.1); + out.extend_from_slice(format!("{text} {text:?} [{text:^9}]").as_bytes()); + drop(text); + { + let read = lock.read().unwrap(); + out.extend_from_slice(format!("{read:?}").as_bytes()); + let bytes = RwLockReadGuard::map(read, |t| &t.0); + out.extend_from_slice(format!("{bytes:?} {bytes:#?}").as_bytes()); + let text = RwLockReadGuard::map(lock.read().unwrap(), |t| &t.1); + let first = MappedRwLockReadGuard::map(text, |t| t.get(..1).unwrap_or("-")); + out.extend_from_slice(format!("{first} {first:?} {first:>3}").as_bytes()); + } + let write = lock.write().unwrap(); + out.extend_from_slice(format!("{write:?}").as_bytes()); + let mut text = RwLockWriteGuard::map(write, |t| &mut t.1); + text.push('!'); + out.extend_from_slice(format!("{text} {text:?}").as_bytes()); + let text = MappedRwLockWriteGuard::map(text, |t| t); + out.extend_from_slice(format!("{text:>12}").as_bytes()); +} diff --git a/ts/runtime/rt/task.ts b/ts/runtime/rt/task.ts index b2cc0df99..8304317b9 100644 --- a/ts/runtime/rt/task.ts +++ b/ts/runtime/rt/task.ts @@ -335,9 +335,61 @@ export function mutexTryLock(mutex: Mutex): Result, TryLockError< return Ok(mutex); } -/** A `MutexGuard`'s drop. */ -export function mutexUnlock(mutex: Mutex): void { - mutex.locked = false; +/** + * What a guard's map lends: where its part of the value is, which it reads and writes as + * `value`, as its cell or mutex does its own value. + */ +abstract class Mapped implements Disposable { + readonly at: Place; + + constructor(at: Place) { + this.at = at; + } + + get value(): T { + return this.at.v; + } + + set value(value: T) { + this.at.v = value; + } + + /** A guard drops nothing of what it lends. */ + [Symbol.dispose](): void {} +} + +/** A `MappedMutexGuard`: a `Mapped` of a part of the value of `mutex`, a guard it took over. */ +export class MutexMap extends Mapped implements Mutex { + readonly mutex: Mutex; + + constructor(at: Place, mutex: Mutex) { + super(at); + this.mutex = mutex; + } + + /** Its mutex's lock, which its drop releases. */ + get locked(): boolean { + return this.mutex.locked; + } + + set locked(locked: boolean) { + this.mutex.locked = locked; + } +} + +/** A `MutexGuard`'s drop, or a `MappedMutexGuard`'s. */ +export function mutexUnlock(guard: Mutex): void { + guard.locked = false; +} + +/** `MutexGuard::map`: a `MappedMutexGuard` of the part of `guard`'s value at `at`, which takes over its lock. */ +export function mutexMap(guard: Mutex, at: Place): Mutex { + return new MutexMap(at, guard); +} + +/** `MutexGuard::filter_map`: `mutexMap` of the part at `at`, or `guard` back if there is none. */ +export function mutexFilterMap(guard: Mutex, at: Place | undefined): Result, Mutex> { + return at === undefined ? Err(guard) : Ok(new MutexMap(at, guard)); } /** @@ -409,31 +461,16 @@ export function rwLockTryWrite(lock: RefCell): Result, TryLockE return Ok(lock); } -/** - * A guard a map made: where the part of a cell's value it lends is, which it reads and - * writes as `value`, as a cell does its own, and whose borrow that cell counts. - */ -export class RefMap implements RefCell { +/** A guard a map of a cell's guard made: a `Mapped` of a part of its value, whose borrow that cell counts. */ +export class RefMap extends Mapped implements RefCell { /** A cell's count, which a guard's cell keeps instead. */ borrows = 0; - readonly at: Place; readonly cell: RefCell; constructor(at: Place, cell: RefCell) { - this.at = at; + super(at); this.cell = cell; } - - get value(): T { - return this.at.v; - } - - set value(value: T) { - this.at.v = value; - } - - /** A guard drops nothing of what it lends. */ - [Symbol.dispose](): void {} } /** The cell whose borrow `r`, a guard, holds. */ @@ -456,6 +493,11 @@ export function refFilterMap(r: RefCell, part: U | undefined): Result(r: RefCell, part: Result): Result, [RefCell, E]> { + return part.ok ? Ok(new RefMap({ v: part.value }, refCellOf(r))) : Err([r, part.error]); +} + /** `Ref::map_split`: `Ref`s of the two `parts` of `r`'s value, which share `r`'s borrow. */ export function refMapSplit(r: RefCell, [u, v]: [U, V]): [RefCell, RefCell] { const cell = refCellOf(r); @@ -493,6 +535,11 @@ export function refMutFilterMap(r: RefCell, at: Place | undefined): return at === undefined ? Err(r) : Ok(new RefMap(at, refCellOf(r))); } +/** `RefMut::try_map`: `refMutMap` of the part at where `f` gave, or `r` back beside its error. */ +export function refMutTryMap(r: RefCell, at: Result, E>): Result, [RefCell, E]> { + return at.ok ? Ok(new RefMap(at.value, refCellOf(r))) : Err([r, at.error]); +} + /** `RefMut::map_split`: `RefMut`s of the parts of `r`'s value at `a` and `b`, which share `r`'s borrow. */ export function refMutMapSplit(r: RefCell, [a, b]: [Place, Place]): [RefCell, RefCell] { const cell = refCellOf(r); diff --git a/ts/src/lower/fmt.rs b/ts/src/lower/fmt.rs index b80cae1c2..fd4e47d56 100644 --- a/ts/src/lower/fmt.rs +++ b/ts/src/lower/fmt.rs @@ -195,10 +195,11 @@ fn mentions(e: &Expr, name: &str) -> bool { } /// Whether reading `e` again gives the same value and does nothing else: a path, or an -/// element of one. +/// element or a field of one, such as what a tuple's guard lends. fn pure(e: &Expr) -> bool { match e { Expr::Index(base, index) => pure(base) && index.is_constant(), + Expr::Member(base, _) | Expr::Located(_, base) => pure(base), e => e.is_path(), } } @@ -328,6 +329,11 @@ pub fn std_impl<'tcx>( (Std::Arc, Trait::Display | Trait::Debug) => { b.fmt_call(tr, args.type_at(0), value.member("value"), f, span) } + // A guard writes what it lends; its first argument is its lifetime. + (Std::MutexGuard | Std::Ref | Std::RefMut, Trait::Display | Trait::Debug) => { + let held = args.types().next()?; + b.fmt_call(tr, held, value.member("value"), f, span) + } // Each bound is written with the range's options. ( std @ (Std::Range | Std::RangeInclusive | Std::RangeFrom | Std::RangeTo | Std::RangeToInclusive), diff --git a/ts/src/lower/library.rs b/ts/src/lower/library.rs index e3a3635bc..a37cd15f0 100644 --- a/ts/src/lower/library.rs +++ b/ts/src/lower/library.rs @@ -316,7 +316,8 @@ impl Std { ["cell", "BorrowError"] => Some(Self::BorrowError), ["cell", "BorrowMutError"] => Some(Self::BorrowMutError), ["sync", "once_lock", "OnceLock"] => Some(Self::OnceLock), - // What a read-write lock's guards map to, which guard as they do. + // What a lock's guards map to, which guard as they do. + ["sync", "poison", "mutex", "MappedMutexGuard"] => Some(Self::MutexGuard), ["sync", "poison", "rwlock", "MappedRwLockReadGuard"] => Some(Self::Ref), ["sync", "poison", "rwlock", "MappedRwLockWriteGuard"] => Some(Self::RefMut), ["sync", "once", "Once"] => Some(Self::Once), @@ -907,8 +908,9 @@ fn discards(owner: &str, name: &str) -> Option<&'static [usize]> { ("OnceCell" | "OnceLock", "get_or_init" | "get_or_try_init") => &[1], // A guard's map calls its function, filtered or not. ( - "Ref" | "RefMut" | "RwLockReadGuard" | "RwLockWriteGuard" | "MappedRwLockReadGuard" | "MappedRwLockWriteGuard", - "map" | "filter_map" | "map_split", + "Ref" | "RefMut" | "MutexGuard" | "MappedMutexGuard" | "RwLockReadGuard" | "RwLockWriteGuard" + | "MappedRwLockReadGuard" | "MappedRwLockWriteGuard", + "map" | "filter_map" | "try_map" | "map_split", ) => &[], ("Once", "call_once" | "call_once_force") => &[0], ("Arc" | "Rc", "new_cyclic") => &[1], diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 90976478e..cf08209fb 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -352,7 +352,10 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { | ("Result", "iter_mut") | ("Arc" | "Rc", "get_mut" | "make_mut") | ("Mutex" | "RwLock" | "RefCell" | "OnceCell" | "OnceLock", "get_mut") - | ("RefMut" | "RwLockWriteGuard" | "MappedRwLockWriteGuard", "map" | "filter_map" | "map_split") => true, + | ( + "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) diff --git a/ts/src/lower/task.rs b/ts/src/lower/task.rs index edff1345e..2db6087ed 100644 --- a/ts/src/lower/task.rs +++ b/ts/src/lower/task.rs @@ -260,12 +260,13 @@ pub fn call<'tcx>( ("RefCell", "borrow_mut") => Expr::rt("refCellBorrowMut", args), ("RefCell", "try_borrow") => Expr::rt("refCellTryBorrow", args), ("RefCell", "try_borrow_mut") => Expr::rt("refCellTryBorrowMut", args), - ("Ref" | "RwLockReadGuard" | "MappedRwLockReadGuard", "map" | "filter_map" | "map_split") => { - guard_map(b, call, name, false, args, out) + ("Ref" | "RwLockReadGuard" | "MappedRwLockReadGuard", "map" | "filter_map" | "try_map" | "map_split") => { + guard_map(b, call, name, "ref", args, out) } - ("RefMut" | "RwLockWriteGuard" | "MappedRwLockWriteGuard", "map" | "filter_map" | "map_split") => { - guard_map(b, call, name, true, args, out) + ("RefMut" | "RwLockWriteGuard" | "MappedRwLockWriteGuard", "map" | "filter_map" | "try_map" | "map_split") => { + guard_map(b, call, name, "refMut", args, out) } + ("MutexGuard" | "MappedMutexGuard", "map" | "filter_map") => guard_map(b, call, name, "mutex", args, out), ("Ref", "clone") => Expr::rt("refClone", args), ("RefCell", "replace") => Expr::rt("refCellReplace", args), ("RefCell", "take") => { @@ -576,18 +577,20 @@ fn held(ty: Option>) -> Ty<'_> { } } -/// `map`, `filter_map`, or `map_split` of a guard, `exclusive` for a `RefMut` or a write -/// guard: its function called on what the guard lends, and a guard of each part that -/// lends, which for an exclusive one is where the part is, so a write through it reaches. +/// `map`, `filter_map`, `try_map`, or `map_split` of a guard, by the runtime's functions +/// named for `guard_of`, `ref` for a `Ref` or a read guard: its function called on what +/// the guard lends, and a guard of each part that lends, which for an exclusive one is where the +/// part is, so a write through it reaches. fn guard_map<'tcx>( b: &mut Body<'_, '_, 'tcx>, call: &Call<'tcx>, name: &str, - exclusive: bool, + guard_of: &str, args: Vec, out: &mut Vec, ) -> Expr { let tcx = b.t.tcx; + let exclusive = guard_of != "ref"; let [guard, f] = two(args); let guard = match guard.is_path() { true => guard, @@ -623,10 +626,7 @@ fn guard_map<'tcx>( } }; let part = tys.next().expect("a part's type"); - let f = |name: &str| match exclusive { - true => format!("refMut{name}"), - false => format!("ref{name}"), - }; + let f = |name: &str| format!("{guard_of}{name}"); match name { "map" => { let at = at(b, part, lent); @@ -643,6 +643,17 @@ fn guard_map<'tcx>( ); Expr::rt(f("FilterMap"), vec![guard, at]) } + "try_map" if !converts(b, part) => Expr::rt(f("TryMap"), vec![guard, lent]), + "try_map" => { + let lent = b.spill(lent, "part", out); + let some = at(b, part, lent.clone().member("value")); + let at = Expr::Cond( + Box::new(lent.clone().member("ok")), + Box::new(Expr::result(true, some)), + Box::new(Expr::result(false, lent.member("error"))), + ); + Expr::rt(f("TryMap"), vec![guard, at]) + } _ => { let other = tys.next().expect("the other part's type"); if !converts(b, part) && !converts(b, other) {