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
2 changes: 1 addition & 1 deletion quest/m3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ generic std gap is planned here even when moq never calls it.
- [Relink without rebuilding](/quest/m3/relink.md) - measure upstream's skip of dependency-only rebuilds on moq's edits once it reaches nightly
- [Unsafe code the translator can model](/quest/m3/ts-unsafe.md) - a dependency using only `unsafe` the translator can model translates from source
- [The capacity of a BytesMut](/quest/m3/ts-bytes-capacity.md) - `BytesMut::capacity` and what reads it translate with `bytes`' values
- [Trait objects](/quest/m3/ts-dyn.md) - `dyn Error` and `dyn Iterator` translate, so a consumer neither rewrites nor binds them
- [Trait objects](/quest/m3/ts-dyn.md) - `dyn Iterator` and `dyn Error`'s downcasts translate, so a consumer neither rewrites nor binds them
- [A trait object in a struct's last field](/quest/m3/ts-dyn-tail.md) - `Rc<RefCell<dyn Trait>>` and the like translate, the last field as the object
- [bytes' Chain as a Buf object](/quest/m3/ts-buf-chain.md) - `Chain::copy_to_bytes` across both halves translates, and with it a `Chain` as a `dyn Buf`
- [A Buf object's size](/quest/m3/ts-dyn-buf-size.md) - a `dyn Buf` object holds only the methods some call dispatches to
Expand Down
8 changes: 5 additions & 3 deletions quest/m3/ts-dyn.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Goal

[rust2-ts](/ts/README.md) translates the trait objects of std's and mapped crates' traits
that moq still refuses (`dyn Error` and `dyn Iterator`), and `dyn FusedIterator`, behind
that moq still refuses (`dyn Iterator`), and `dyn FusedIterator`, behind
any pointer std coerces to one, such as `&mut dyn Debug` and
`Arc<dyn Iterator>`, with Rust's dispatch and drop order, proven by differential tests against native Rust, so a consumer
neither rewrites nor binds them.
Expand All @@ -18,8 +18,10 @@ config, so trait objects move from never to not yet.
as an `rt.Future`, `dyn Any` as the class a downcast checks for, `dyn Fn*` as the
closure itself, and the translated crates' traits as an object with the trait's methods.
Build the rest on those.
- `Error` needs `source` and its supertraits' `Debug` and `Display` on the object, and its
`downcast_ref` can check classes as `dyn Any`'s does; moq's are thiserror's `source`.
- `dyn Error` translates as an object of `source`, `Debug`, and `Display`. Its downcasts
remain: checking the class of `Error.of$T` as `dyn Any`'s do would miss a value upcast
from the crate's own trait over `Error`, whose object is that trait's class, so a
downcast needs the class of the value's own type, as an upcast to `dyn Any` does.
- `Iterator` can be an `rt.Iter`.
- `bytes::Buf` is an object of its methods, as the crate's own traits are, decided with the
maintainer on 2026-11-02, so it works for any implementor.
Expand Down
17 changes: 13 additions & 4 deletions ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ crate translation reaches is refused, and so are two versions of one crate it re
| closure | an arrow function, with `[Symbol.dispose]` if it owns a value that drops, and what makes its clone if a clone can't be itself |
| `fn` pointer, function item as a value | the function, as a closure is typed: a `()` result is `undefined`, where a declared function returns `void` |
| `&dyn Trait`, `&mut dyn Trait`, `Box<dyn Trait>`, `Arc<dyn Trait>`, `Rc<dyn Trait>`, `Weak<dyn Trait>`, `Pin<P>` of one | an object with the trait's methods, its supertraits' included: `shape.area$Shape()`; behind an `Arc` or an `Rc`, an `rt.ArcDyn` sharing the count of the `Arc` it was made from |
| `dyn Error` | an object of its `source`, `fmt$Debug`, and `fmt$Display`: `e.source$Error()` |
| `dyn Any` | an instance of the class its value's type was wrapped in, which a downcast checks for: `a instanceof Any.of$dyns_Square ? a.v : undefined` |
| `dyn Fn`, `dyn FnMut`, `dyn FnOnce` | the closure or function it was made of: `(a0: rt.u8) => rt.u32` |
| constant | its value, inlined as Rust inlines it, so each use of one holding a cell is a cell of its own; one with no value tree, or holding a cell, read from the memory rustc evaluated |
Expand Down Expand Up @@ -481,10 +482,18 @@ A `dyn Fn`, `dyn FnMut`, or `dyn FnOnce` is the closure or function it was made
a call calls as it does the closure, and which a box drops as it does the closure. One
that `Fn` or `FnMut` calls, boxed as a `dyn FnOnce`, is `rt.once` of it, which drops it
after the call, as an `FnOnce` closure's body drops what it owns, and a box of a
reference to one is `rt.lent` of it, which never drops it. A trait object of std's
reference to one is `rt.lent` of it, which never drops it.

A `dyn Error` is such an object of `source` and its supertraits' `Debug` and `Display`,
`Error.of$errors_Leaf` from `core/error.Error.ts`, whose `source` is `None` for a type
that keeps `Error`'s own. `From` an error into a `Box<dyn Error>`, which `?` converts by,
makes one. Its deprecated `description` and `cause`, and its downcasts, are refused, and so
is `From` a string, which boxes std's own private error type.

A trait object of std's
or a mapped crate's trait other than `dyn Debug` and `dyn Display` behind `&`, `&mut`,
`Box`, `Arc`, or `Rc`, `dyn Future`,
`dyn Any`, `dyn Buf`, and the function traits, of a generic
`dyn Any`, `dyn Error`, `dyn Buf`, and the function traits, of a generic
trait or one with an associated type, of a byte array, or behind any other pointer, such
as a struct's last field, is refused, and so are an upcast to `dyn Any` and
`Any::type_id`.
Expand Down Expand Up @@ -863,8 +872,8 @@ they need.

Byte reinterpretation, pointer addresses, pointer arithmetic on a value JS keeps as an object (other than wrapping arithmetic on a null or dangling pointer), memory from `alloc` other than an array of values JS copies, and unions whose
fields overlap in different types ([unsafe](/quest/m3/ts-unsafe.md)), and trait objects other than
`dyn Debug`, `dyn Display`, `dyn Future`, `dyn Any`, `dyn Fn*`, `dyn Buf`, and the translated crates' traits'
behind `&`, `&mut`, `Box`, `Arc`, `Rc`, `Weak`, or `Pin` ([trait objects](/quest/m3/ts-dyn.md)), fail translation. Until they translate, an item that needs one, such as a `dyn Error`, binds to
`dyn Debug`, `dyn Display`, `dyn Future`, `dyn Any`, `dyn Error`, `dyn Fn*`, `dyn Buf`, and the translated crates' traits'
behind `&`, `&mut`, `Box`, `Arc`, `Rc`, `Weak`, or `Pin` ([trait objects](/quest/m3/ts-dyn.md)), fail translation. Until they translate, an item that needs one, such as a `Box<dyn Error>` made `From` a string, binds to
hand-written TS ([bindings](#bindings)).

These fail translation: `f16` and `f128`, a `Poll` of a `Poll` of a `Poll`, a future whose output can be pending, an instance of a generic type whose field is a `Poll` of a type parameter that can be pending, a closure that owns a value
Expand Down
168 changes: 168 additions & 0 deletions ts/fixtures/lang/expected/core/error.Error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "../rt.ts";
import { drop$errors_Failure } from "../lib.ts";
import * as Failure from "../errors.Failure.ts";
import * as Leaf from "../errors.Leaf.ts";
import * as Transported from "../errors.Transported.ts";

export type Error =
| { source$Error: () => Error | undefined; fmt$Display: (a0: rt.Formatter) => rt.Result<undefined, rt.FmtError>; fmt$Debug: (a0: rt.Formatter) => rt.Result<undefined, rt.FmtError> };

export class of$boxed_Box_errors_Failure {
v: Failure.Failure;

constructor(v: Failure.Failure) {
this.v = v;
}

[Symbol.dispose](): void {
drop$errors_Failure(this.v);
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Debug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Display(this.v, a);
}

source$Error(): Error | undefined {
return Failure.source$Error(this.v);
}
}

export class of$errors_Failure {
v: Failure.Failure;

constructor(v: Failure.Failure) {
this.v = v;
}

[Symbol.dispose](): void {
drop$errors_Failure(this.v);
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Debug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Display(this.v, a);
}

source$Error(): Error | undefined {
return Failure.source$Error(this.v);
}
}

export class of$errors_Leaf {
v: Leaf.Leaf;

constructor(v: Leaf.Leaf) {
this.v = v;
}

[Symbol.dispose](): void {
this.v[Symbol.dispose]();
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Leaf.fmt$fmt_Debug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Leaf.fmt$fmt_Display(this.v, a);
}

source$Error(): Error | undefined {
return undefined;
}
}

export class of$errors_Transported {
v: Transported.Transported;

constructor(v: Transported.Transported) {
this.v = v;
}

[Symbol.dispose](): void {
this.v[Symbol.dispose]();
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Transported.fmt$fmt_Debug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Transported.fmt$fmt_Display(this.v, a);
}

source$Error(): Error | undefined {
return Transported.source$Error(this.v);
}
}

export class of$num_ParseIntError {
v: rt.ParseIntError;

constructor(v: rt.ParseIntError) {
this.v = v;
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return rt.fmtParseIntErrorDebug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return rt.fmtParseIntErrorDisplay(this.v, a);
}

source$Error(): Error | undefined {
return undefined;
}
}

export class of$ref_errors_Failure {
v: Failure.Failure;

constructor(v: Failure.Failure) {
this.v = v;
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Debug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Display(this.v, a);
}

source$Error(): Error | undefined {
return Failure.source$Error(this.v);
}
}

export class of$ref_errors_Leaf {
v: Leaf.Leaf;

constructor(v: Leaf.Leaf) {
this.v = v;
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Leaf.fmt$fmt_Debug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Leaf.fmt$fmt_Display(this.v, a);
}

source$Error(): Error | undefined {
return undefined;
}
}

//# sourceMappingURL=error.Error.ts.map
8 changes: 8 additions & 0 deletions ts/fixtures/lang/expected/core/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { eq$1horntk as float_parse_eq$1horntk } from "../core/num/float_parse.ts
import { eq$1v93ipo as str_error_eq$1v93ipo, eq$1f3e51k as str_error_eq$1f3e51k } from "../core/str/error.ts";
import { cmp$175ka5q as poll_cmp$175ka5q } from "../core/task/poll.ts";
import { eq$0dhyfbb as time_eq$0dhyfbb } from "../core/time.ts";
import type * as Failure from "../errors.Failure.ts";
import type * as heap_Fault from "../heap.Fault.ts";
import type * as locks_Tally from "../locks.Tally.ts";
import * as Part from "../orders.Part.ts";
Expand Down Expand Up @@ -782,6 +783,13 @@ export function mapErr$1lsqfgi(self: rt.Result<rt.u8, rt.u16>, op: (a0: rt.u16)
return rt.Err(op(self.error));
}

export function mapErr$1ff8zoy(self: rt.Result<rt.u8, rt.ParseIntError>, _: (a0: rt.ParseIntError) => Failure.Failure): rt.Result<rt.u8, Failure.Failure> {
if (self.ok) {
return self as rt.Result<rt.u8, Failure.Failure>;
}
return rt.Err({ tag: "Parse", _0: self.error } as Failure.Failure);
}

export function mapErr$0u5yxjj(self: rt.Result<rt.u8, rt.ParseIntError>, op: (a0: rt.ParseIntError) => rt.IntErrorKind): rt.Result<rt.u8, rt.IntErrorKind> {
if (self.ok) {
return self as rt.Result<rt.u8, rt.IntErrorKind>;
Expand Down
40 changes: 40 additions & 0 deletions ts/fixtures/lang/expected/errors.Coded.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import { drop$errors_Failure } from "./lib.ts";
import type * as error_Error from "./core/error.Error.ts";
import * as Failure from "./errors.Failure.ts";

/** The crate's own trait over `Error`, as moq's transports' errors are. */
export type Coded =
| { code$Coded: () => rt.u8; source$Error: () => error_Error.Error | undefined; fmt$Display: (a0: rt.Formatter) => rt.Result<undefined, rt.FmtError>; fmt$Debug: (a0: rt.Formatter) => rt.Result<undefined, rt.FmtError> };

export class of$errors_Failure {
v: Failure.Failure;

constructor(v: Failure.Failure) {
this.v = v;
}

[Symbol.dispose](): void {
drop$errors_Failure(this.v);
}

code$Coded(): rt.u8 {
return Failure.code$errors_Coded(this.v);
}

fmt$Debug(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Debug(this.v, a);
}

fmt$Display(a: rt.Formatter): rt.Result<undefined, rt.FmtError> {
return Failure.fmt$fmt_Display(this.v, a);
}

source$Error(): error_Error.Error | undefined {
return Failure.source$Error(this.v);
}
}

//# sourceMappingURL=errors.Coded.ts.map
61 changes: 61 additions & 0 deletions ts/fixtures/lang/expected/errors.Failure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Generated by rust2-ts. Do not edit.

import * as rt from "./rt.ts";
import * as error_Error from "./core/error.Error.ts";
import * as Leaf from "./errors.Leaf.ts";

export function fmt$fmt_Debug(self: Failure, f: rt.Formatter): rt.Result<undefined, rt.FmtError> {
if (self.tag === "Short") {
const _5: string = "Short";
const _6: rt.Fmt = (f: rt.Formatter) => rt.u8Debug(self._0, f);
return rt.debugTupleFinish(rt.debugTupleField(rt.fmtDebugTuple(f, _5), _6));
}
if (self.tag === "Wrapped") {
const _9: string = "Wrapped";
const _10: rt.Fmt = (f: rt.Formatter) => Leaf.fmt$fmt_Debug(self._0, f);
return rt.debugTupleFinish(rt.debugTupleField(rt.fmtDebugTuple(f, _9), _10));
}
const _13: string = "Parse";
const _14: rt.Fmt = (f: rt.Formatter) => rt.fmtParseIntErrorDebug(self._0, f);
return rt.debugTupleFinish(rt.debugTupleField(rt.fmtDebugTuple(f, _13), _14));
}

/** An error whose source is the crate's, std's, or none. */
export type Failure =
| { tag: "Short"; _0: rt.u8 }
| { tag: "Wrapped"; _0: Leaf.Leaf }
| { tag: "Parse"; _0: rt.ParseIntError };

export function fmt$fmt_Display(self: Failure, f: rt.Formatter): rt.Result<undefined, rt.FmtError> {
if (self.tag === "Short") {
const arg = self._0;
const args2: rt.Fmt[] = [(f: rt.Formatter) => rt.u8Display(arg, f)];
return rt.fmtWriteFmt(f, ["short by ", args2[0]]);
}
if (self.tag === "Wrapped") {
return rt.fmtWriteStr(f, "wrapped");
}
return rt.fmtWriteStr(f, "unparsed");
}

export function source$Error(self: Failure): error_Error.Error | undefined {
if (self.tag === "Short") {
return undefined;
}
if (self.tag === "Wrapped") {
return new error_Error.of$errors_Leaf(self._0);
}
return new error_Error.of$num_ParseIntError(self._0);
}

export function code$errors_Coded(self: Failure): rt.u8 {
if (self.tag === "Short") {
return self._0;
}
if (self.tag === "Wrapped") {
return self._0.code;
}
return 0;
}

//# sourceMappingURL=errors.Failure.ts.map
Loading