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 @@ -34,7 +34,6 @@ generic std gap is planned here even when moq never calls it.
- [An Arc's pointer](/quest/m3/ts-arc-pointers.md) - `Arc::as_ptr` and `Rc::as_ptr` translate, comparable by `ptr::addr_eq`
- [The rest of a vector's methods](/quest/m3/ts-vec-rest.md) - `append`, `resize`, `split_off`, `try_reserve`, and `Vec::from(VecDeque)` translate
- [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
- [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
19 changes: 0 additions & 19 deletions quest/m3/ts-lazy-writes.md

This file was deleted.

6 changes: 3 additions & 3 deletions ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ crate translation reaches is refused, and so are two versions of one crate it re
| `ControlFlow<B, C>` | `rt.Result<C, B>`, `Ok` to continue; what `?` branches on is the value it was applied to, but for a `Poll`'s `Pending` and `Ready(None)`, which continue as `Ok` of themselves |
| tuple, `[T; N]`, `&[T]`, `Box<[T]>`, `Vec<T>` | an array; for bytes, `Uint8Array` for `[u8; N]`, `rt.ByteSlice` for `&[u8]`, `&[u8; N]`, and `Box<[u8]>`, and `rt.ByteVec` for `Vec<u8>` |
| `Cow<[T]>` | `rt.Cow<T>`: the `Vec` it owns, or an `rt.Borrowed` holding the slice it borrows; `rt.ByteCow` for bytes, an `rt.ByteVec` or an `rt.ByteSlice` |
| `&T`, `&mut T`, `Box<T>`, `Reverse<T>`, `NonZero<T>` | the value; a `&mut` to a value JS copies, such as a number, an `Option`, a `String`, or a reference, or to an `Arc` or a `Waker`, is an `rt.Place<T>`, which for what a box holds is the place holding the box |
| `*const T`, `*mut T`, `NonNull<T>` | what a `&mut T` is: the object, an `rt.ByteSlice` of a `[u8; N]`'s bytes, which compares by the buffer and offset they are at, or an `rt.Place<T>` to a value JS copies and to a library type such as a `Vec` or an `Arc`, which into an array, slice, or `Vec` is the `rt.AtIndex` of its element, which `add`, `sub`, `offset`, their wrapping forms, `offset_from`, `ptr::copy`, and `slice::from_raw_parts` move, measure, copy between, and view; one to a value in no array is element 0 of an array of one, as in Rust; JS keeps that array, and a shared subslice, which JS copies, apart from what holds it in Rust, so a pointer, slice, or copy the body shows reaching past either, a raw or mutable slice of a byte in no array, and `offset_from` between pointers the body shows JS keeps in different arrays are refused, and through a pointer from elsewhere, such as a field, any of these, or a read or write past the array JS keeps it in, fails at run time; a pointer into an array of values JS keeps as objects is refused; `rt.NULL` for null and `rt.DANGLING` for dangling. A `*const ()` is an identity, such as a waker's data, and `rt.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, so a pointer to a part kept as an object, such as a field of a tuple or struct type, is refused wherever the crate may put another object there while a pointer into that memory may still be read, which a write into a fresh allocation, or a variable before any pointer into it is taken, can't, whether by index or through std's iterators over its slots; a read copies what the crate writes into in place |
| `&T`, `&mut T`, `Box<T>`, `Reverse<T>`, `NonZero<T>` | the value; a `&mut` to a value JS copies, such as a number, an `Option`, a `String`, or a reference, or to an `Arc`, a `Waker`, or a `LocalKey`, whose copies share what it points at, is an `rt.Place<T>`, which for what a box holds is the place holding the box |
| `*const T`, `*mut T`, `NonNull<T>` | what a `&mut T` is: the object, an `rt.ByteSlice` of a `[u8; N]`'s bytes, which compares by the buffer and offset they are at, or an `rt.Place<T>` to a value JS copies and to a library type such as a `Vec` or an `Arc`, which into an array, slice, or `Vec` is the `rt.AtIndex` of its element, which `add`, `sub`, `offset`, their wrapping forms, `offset_from`, `ptr::copy`, and `slice::from_raw_parts` move, measure, copy between, and view; one to a value in no array is element 0 of an array of one, as in Rust; JS keeps that array, and a shared subslice, which JS copies, apart from what holds it in Rust, so a pointer, slice, or copy the body shows reaching past either, a raw or mutable slice of a byte in no array, and `offset_from` between pointers the body shows JS keeps in different arrays are refused, and through a pointer from elsewhere, such as a field, any of these, or a read or write past the array JS keeps it in, fails at run time; a pointer into an array of values JS keeps as objects is refused; `rt.NULL` for null and `rt.DANGLING` for dangling. A `*const ()` is an identity, such as a waker's data, and `rt.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, the state an `rt` class such as `rt.LazyCell` keeps private included, so a pointer to a part kept as an object, such as a field of a tuple or struct type, is refused wherever the crate may put another object there while a pointer into that memory may still be read, which a write into a fresh allocation, or a variable before any pointer into it is taken, can't, whether by index or through std's iterators over its slots; a read copies what the crate writes into in place |
| `MaybeUninit<T>` | `T`: `undefined` until written, or an object whose parts kept as objects are there, holding `undefined` |
| `ManuallyDrop<T>` | `T`, which nothing drops but its own `drop`; its `Debug`, comparisons, and constants are refused |
| `union` | an object of slots named by offset, `at0` and `at8`, each holding the field part kept there; fields must overlap in the same type, or in a non-null pointer and an `Option` of it |
Expand Down Expand Up @@ -135,7 +135,7 @@ crate translation reaches is refused, and so are two versions of one crate it re
| `Arc<T>`, `Rc<T>`, `Weak<T>`, `Mutex<T>` | `rt.Arc` for either, of what a `Box<T>` is for a slice or `str`, with its strong and weak counts, an `Arc<[u8]>` of an `Arc<str>`'s bytes sharing its count, 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<T>`, `TryLockError<T>` | the `T` it holds; `rt.TryLockError<T>`, tagged as `Bound` is |
| `Cell<T>`, `UnsafeCell<T>`, `RefCell<T>`, `RwLock<T>` | `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; `RwLockWriteGuard::downgrade` keeps the lock as one read, and `Ref::leak` and `RefMut::leak` keep the borrow until `RefCell::undo_leak` ends it; `Cell::update` calls its function on a copy, and `RefCell::replace_with` on the value, borrowed as `borrow_mut` does, or its place where JS copies it; refused for a part of a sized type JS can't write in place, such as a binding's |
| `thread_local!`, `LocalKey<T>` | 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 |
| `thread_local!`, `LocalKey<T>` | an `rt.LocalKey`, a module's value that each copy of the key is, 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<T>`, `OnceLock<T>` | `rt.OnceCell` and `rt.OnceLock`, holding the `Option<T>` 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 |
| `LazyCell<T>`, `LazyLock<T>` | `rt.LazyCell` and `rt.LazyLock`, which run their function on the first `force` or `Deref`, once, and drop it then, or with the cell if it never ran; forced from within it, either panics as std's does |
Expand Down
4 changes: 3 additions & 1 deletion ts/fixtures/lang/locals/expected/lib.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import { locals } from "./locals.ts";
import { locals, keys } from "./locals.ts";
import type * as Twin from "./locals.Twin.ts";

export function copy$locals_Twin(v: Twin.Twin): Twin.Twin {
Expand All @@ -19,6 +19,8 @@ export function copy$tuple2_locals_Twin_locals_Twin(v: [Twin.Twin, Twin.Twin]):
export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined][] {
return [["locals::locals", (_22: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): undefined => {
locals(data, out);
}] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["locals::keys", (_23: rt.u8, data2: rt.ByteSlice, out2: rt.ByteVec): undefined => {
keys(data2, out2);
}] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined]];
}

Expand Down
54 changes: 53 additions & 1 deletion ts/fixtures/lang/locals/expected/locals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { mapOr$18ipla0 as result_mapOr$18ipla0 } from "./core/result.ts";
import * as Noisy from "./locals.Noisy.ts";
import type * as Polling from "./locals.Polling.ts";
import type * as Twin from "./locals.Twin.ts";
import { set$0st49po, get$0x8wyln, get$1ei2eoj, take$09yi5f2, replace$0kxb7te, withBorrow$1qygtee, withBorrow$1k8k9m1, withBorrow$0r4ucls, withBorrow$1t1sbub, withBorrow$15ql9sa, take$1x34chv, take$122f15k, replace$0bb3zeq, replace$1xk72wn } from "./std/thread/local.ts";
import { set$0st49po, set$0xuu1fw, get$0x8wyln, get$1ei2eoj, get$18ignl5, take$09yi5f2, replace$0kxb7te, withBorrow$1qygtee, withBorrow$1k8k9m1, withBorrow$0r4ucls, withBorrow$1t1sbub, withBorrow$15ql9sa, take$1x34chv, take$122f15k, replace$0bb3zeq, replace$1xk72wn } from "./std/thread/local.ts";

export const COUNT: rt.LocalKey<rt.Cell<rt.u32>> = /* @__PURE__ */ new rt.LocalKey(COUNT$init, false);

Expand All @@ -22,6 +22,12 @@ export function HELD$init(): rt.RefCell<Noisy.Noisy | undefined> {
return new rt.RefCell(undefined as Noisy.Noisy | undefined);
}

export const LEFT: rt.LocalKey<rt.Cell<rt.u8>> = /* @__PURE__ */ new rt.LocalKey(LEFT$init, false);

export function LEFT$init(): rt.Cell<rt.u8> {
return new rt.Cell(1);
}

export const LOG: rt.LocalKey<rt.RefCell<rt.ByteVec>> = /* @__PURE__ */ new rt.LocalKey((): rt.RefCell<rt.ByteVec> => {
return LOG$init(undefined);
}, true);
Expand All @@ -45,6 +51,14 @@ export function POLLING$init(): Polling.Polling {
return { current: new rt.Cell(undefined as [rt.u64, rt.u64] | undefined), held: new rt.RefCell(new rt.ByteVec()) };
}

export const RIGHT: rt.LocalKey<rt.Cell<rt.u8>> = /* @__PURE__ */ new rt.LocalKey((): rt.Cell<rt.u8> => {
return RIGHT$init(undefined);
}, true);

export function RIGHT$init(_1: undefined): rt.Cell<rt.u8> {
return new rt.Cell(2);
}

export const TALLY: rt.LocalKey<rt.RefCell<rt.u32>> = /* @__PURE__ */ new rt.LocalKey((): rt.RefCell<rt.u32> => {
return TALLY$init(undefined);
}, true);
Expand Down Expand Up @@ -261,4 +275,42 @@ export function NEXT$init$serial(): rt.Cell<[rt.u8, rt.u8]> {
return new rt.Cell([0, 4] as [rt.u8, rt.u8]);
}

/** `*r = v`, through the reference. */
export function assign$thread_LocalKey_Cell_u8(r: rt.Place<rt.LocalKey<rt.Cell<rt.u8>>>, v: rt.LocalKey<rt.Cell<rt.u8>>): void {
r.v = v;
}

/**
* Each byte a copy of `LEFT` written whole with `RIGHT` by each way std moves a value, or
* not at all, after which adding the byte through the copy changes only the key it names.
* Each puts what the replaced copy names, then what each key holds. A key is used only
* through a `&'static`, so each copy is leaked.
*/
export function keys(data: rt.ByteSlice, out: rt.ByteVec): void {
for (const op of rt.byteSliceView(data)) {
const key: rt.Place<rt.LocalKey<rt.Cell<rt.u8>>> = { v: LEFT };
const _12: rt.u8 = rt.u8Rem(op, 5);
if (_12 === 0) {
assign$thread_LocalKey_Cell_u8(key, RIGHT);
} else if (_12 === 1) {
const old = key.v;
key.v = RIGHT;
rt.byteVecPush(out, get$18ignl5({ v: old }.v));
} else if (_12 === 2) {
const other: rt.Place<rt.LocalKey<rt.Cell<rt.u8>>> = { v: RIGHT };
const swapped = key.v;
key.v = other.v;
other.v = swapped;
rt.byteVecPush(out, get$18ignl5(other.v));
} else if (_12 === 3) {
rt.cellSet(rt.cellOfPlace(key), RIGHT);
}
const _28: rt.u8 = rt.u8WrappingAdd(get$18ignl5(key.v), op);
set$0xuu1fw(key.v, _28);
rt.iterExtendBytes(out, rt.iterSlice(Uint8Array.of(get$18ignl5(LEFT), get$18ignl5(RIGHT))), undefined, true);
}
set$0xuu1fw(LEFT, 1);
set$0xuu1fw(RIGHT, 2);
}

//# sourceMappingURL=locals.ts.map
14 changes: 14 additions & 0 deletions ts/fixtures/lang/locals/expected/std/thread/local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ export function set$0st49po(self: rt.LocalKey<rt.Cell<rt.u32>>, value: rt.u32):
});
}

export function set$0xuu1fw(self: rt.LocalKey<rt.Cell<rt.u8>>, value: rt.u8): void {
rt.localKeyInitializeWith(self, new rt.Cell(value), (value2: rt.Cell<rt.u8> | undefined, cell: rt.Cell<rt.u8>): undefined => {
if (value2 !== undefined) {
rt.cellSet(cell, value2.v);
}
});
}

export function get$0x8wyln(self: rt.LocalKey<rt.Cell<[Twin.Twin, Twin.Twin]>>): [Twin.Twin, Twin.Twin] {
return ((a0: rt.Cell<[Twin.Twin, Twin.Twin]>): [Twin.Twin, Twin.Twin] => {
return copy$tuple2_locals_Twin_locals_Twin(a0.v);
Expand All @@ -25,6 +33,12 @@ export function get$1ei2eoj(self: rt.LocalKey<rt.Cell<rt.u32>>): rt.u32 {
})(self.get());
}

export function get$18ignl5(self: rt.LocalKey<rt.Cell<rt.u8>>): rt.u8 {
return ((a0: rt.Cell<rt.u8>): rt.u8 => {
return a0.v;
})(self.get());
}

export function take$09yi5f2(self: rt.LocalKey<rt.Cell<rt.u32>>): rt.u32 {
return ((a0: rt.Cell<rt.u32>): rt.u32 => {
return rt.cellReplace(a0, 0);
Expand Down
5 changes: 4 additions & 1 deletion ts/fixtures/lang/locals/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ use harness::Case;
/// Each case's name and what it runs. The first two bytes, big-endian, pick one modulo
/// their count, so a case added at the end moves no other.
fn cases() -> Vec<(&'static str, Case)> {
vec![("locals::locals", |_, data, out| locals::locals(data, out))]
vec![
("locals::locals", |_, data, out| locals::locals(data, out)),
("locals::keys", |_, data, out| locals::keys(data, out)),
]
}
38 changes: 37 additions & 1 deletion ts/fixtures/lang/locals/src/locals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
//! and a struct of both, as kio's `Polling` is, initialized by a `const` block or lazily
//! by an expression; read and written through `with`, given a closure or a function,
//! `try_with`, and the `Cell` and `RefCell` helpers, as kio's `coop` does; nested; and
//! declared in a function, as kio's `serial` is. Each case leaves
//! declared in a function, as kio's `serial` is; and a copy of a key written whole through
//! a `&mut`, which then names the other's storage. Each case leaves
//! every thread-local as it found it, so a fresh instance and a reused one agree.

use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem;
use std::sync::{Arc, Mutex};
use std::thread::LocalKey;

/// Logs its id when it drops.
struct Noisy {
Expand Down Expand Up @@ -49,6 +52,9 @@ thread_local! {
x.0 = 3;
Cell::new((x, y))
};
/// The keys `keys` writes over each other's copies, initialized by a `const` block or lazily.
static LEFT: Cell<u8> = const { Cell::new(1) };
static RIGHT: Cell<u8> = Cell::new(2);
}

/// Each byte an operation on the thread-locals; each puts what it read and what dropped.
Expand Down Expand Up @@ -158,3 +164,33 @@ fn serial(reset: bool) -> u8 {
n
})
}

/// `*r = v`, through the reference.
fn assign<T>(r: &mut T, v: T) {
*r = v;
}

/// Each byte a copy of `LEFT` written whole with `RIGHT` by each way std moves a value, or
/// not at all, after which adding the byte through the copy changes only the key it names.
/// Each puts what the replaced copy names, then what each key holds. A key is used only
/// through a `&'static`, so each copy is leaked.
pub fn keys(data: &[u8], out: &mut Vec<u8>) {
for &op in data {
let key: &'static mut LocalKey<Cell<u8>> = Box::leak(Box::new(LEFT));
match op % 5 {
0 => assign(key, RIGHT),
1 => out.push(Box::leak(Box::new(mem::replace(key, RIGHT))).get()),
2 => {
let other = Box::leak(Box::new(RIGHT));
mem::swap(key, other);
out.push(other.get());
}
3 => Cell::from_mut(&mut *key).set(RIGHT),
_ => {}
}
key.set(key.get().wrapping_add(op));
out.extend([LEFT.get(), RIGHT.get()]);
}
LEFT.set(1);
RIGHT.set(2);
}
Loading