diff --git a/quest/m3/README.md b/quest/m3/README.md index d7f2b6578..73adb9533 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -16,7 +16,6 @@ generic std gap is planned here even when moq never calls it. - [Retrial Cranelift on moq's tests](/quest/m3/cranelift-unwind.md) - waits on upstream: moq's tests pass under Cranelift, so its CI can weigh it again - [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/README.md) - a dependency using only `unsafe` the translator can model translates from source -- [Trait objects' TypeId](/quest/m3/ts-dyn.md) - `Any::type_id` and `TypeId` translate with Rust's equality, order, and hash, so a consumer neither rewrites nor binds them - [Waiting: drop the lang fixture's mul_add allowance](/quest/m3/ts-fma-allowance.md) - the differential fails on any `mul_add` difference again once wasm32's `fma` rounds exactly - [Waiting: type-check in process with tsrs](/quest/m3/ts-tsrs.md) - rust2-ts checks bindings through tsrs instead of `tsc` once it is a stable library - [rs.Slice](/quest/m3/ts-slice-object.md) - references to arrays of other values are a runtime slice too, and pointers compare by where they point diff --git a/quest/m3/ts-dyn.md b/quest/m3/ts-dyn.md deleted file mode 100644 index 6b4e524ae..000000000 --- a/quest/m3/ts-dyn.md +++ /dev/null @@ -1,30 +0,0 @@ -# [S] Trait objects' TypeId - -## Goal - -[rust2-ts](/ts/README.md) translates `Any::type_id` and `TypeId`, of a trait object and of -`TypeId::of`, with Rust's equality, order, and hash, proven by differential tests -against native Rust, so a consumer neither rewrites nor binds them. - -## Plan - -Trait objects of every other trait translate, std's and mapped crates' included, each -std type's class calling the mapping of its impl. Decided with the maintainer on -2026-10-27: a stock crate translates with no annotations or config, so trait objects move -from never to not yet. - -`TypeId` has no JS value yet: the class of the value's own type stands for its type in a -downcast, but `TypeId::of` of a type no object holds has none, and a `&mut dyn Any` of a -value JS copies is of a second class. Decided with the maintainer on 2026-11-04: - -- ✅ rustc's 128-bit `type_id_hash` of the type, which std's `TypeId` holds at run time; - each object's class returns its type's as a constant. Its traits are std's `TypeId` - impls, not `u128`'s: `Hash` writes one `u64`, `Debug` prints `TypeId(0x…)`, and `Ord` - is derived over its words, so a test logs a `Hasher`'s calls and checks order and - `Debug`'s shape. Its value is native's only where the crate's metadata hash is, so tests - compare within a run rather than the exact bits. -- Rejected: the type's path as a string, whose order and `Debug` are not native's; the - class itself, which still needs a value for order and hash, and a class for a type no - object holds. - -Public API: the translator's output. diff --git a/quest/m3/ts-wasm-bindgen/spike.md b/quest/m3/ts-wasm-bindgen/spike.md index 98c886e32..287876e81 100644 --- a/quest/m3/ts-wasm-bindgen/spike.md +++ b/quest/m3/ts-wasm-bindgen/spike.md @@ -26,7 +26,7 @@ It also answers, for the imports quest: - How a mapped `JsValue`, `JsCast`, and `Closure` meet the generic code around them, such as `Closure::new` of a closure, which web-codecs' output and error callbacks need, given - that [trait objects](/quest/m3/ts-dyn.md) other than `TypeId` already translate. + that trait objects, `TypeId` included, already translate. - How the differential test's native side runs a wasm-bindgen build: the wasm-bindgen CLI in the dev shell at the fixture's pinned version, and a host with WebCodecs, which Bun lacks, so likely a headless browser. diff --git a/ts/README.md b/ts/README.md index 733c5cc90..63e0a8841 100644 --- a/ts/README.md +++ b/ts/README.md @@ -79,7 +79,7 @@ and each type a module of its own beside it, holding the type and its functions from. `rs.ts` beside them is the runtime: what std does that JS does not, with iterators in `rs/iter.ts`, `str`'s searches in `rs/str.ts`, the ASCII methods of `u8`, `char`, `[u8]`, and `str` in `rs/ascii.ts`, `char`'s Unicode classes, case maps, escapes, and encodings, and `str`'s case maps and escapes, in `rs/char.ts`, collections in `rs/collections.ts`, hashing in `rs/hash.ts`, ownership, atomics, and async in -`rs/task.ts`, `f64`'s and `f32`'s methods in `rs/float.ts`, `Duration` in `rs/time.ts`, and `Layout` and memory from `alloc` in `rs/alloc.ts`. What translates from std's own MIR, +`rs/task.ts`, `f64`'s and `f32`'s methods in `rs/float.ts`, `Duration` in `rs/time.ts`, `TypeId` in `rs/any.ts`, and `Layout` and memory from `alloc` in `rs/alloc.ts`. What translates from std's own MIR, `Option`'s, `Result`'s, and `Poll`'s methods, `==` of std's errors, `==` and ordering of a `Result` and a `Poll`, `cmp::max` and `min`, and the `str::parse` and `from_str_radix` of an integer or a `NonZero` and `bool`'s `str::parse`, goes in a module under its crate's name, as `core/option.ts` holds `map$1cf8u0y`. A dependency's modules go @@ -122,13 +122,14 @@ crate translation reaches is refused, and so are two versions of one crate it re | `PeekMut` | `rs.PeekMut`, the `rs.Place` of a heap's greatest element, which sifts it down as it drops once `DerefMut` lent it | | `Ordering`, `DefaultHasher`, `RandomState`, `BuildHasherDefault` | `rs.Ordering`, which is `-1`, `0`, or `1`; `rs.Hasher`; `rs.RandomState`, the keys of the `DefaultHasher` it builds, never changed once made; `undefined`, as `PhantomData` is, whose `build_hasher` is `H::default()` | | `Duration` | `rs.Duration`: `{ secs, nanos }`, std's whole seconds as an `rs.u64` and the nanoseconds past them, never changed once made | +| `TypeId` | `rs.TypeId`: rustc's 128-bit hash of the type, which std's holds at run time, as 32 hex digits, equal by `===`; ordered as std's derive orders the hash's pointer-sized words, lowest first, hashed as its high 64 bits, and written as `TypeId(0x…)`, as std's are | | `Layout`, `alloc::alloc` | `rs.Layout`: `{ size, align }`, never changed once made, checked as std's constructors check it; `alloc` gives an `rs.Block` of its bytes, which a cast of its pointer to `*mut T` the body shows came from `alloc` or `realloc` makes a JS array of `T`s, values JS copies, as an `rs.AtIndex` of its first; a cast back to `*mut u8` only as what `dealloc` or `realloc` takes, which free the array or copy it into a new one; its bytes, read, written, or cast to any other type, are refused, and fail at run time through a pointer from elsewhere | | 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, or std's an arrow that calls it as a written call does, as a closure is typed: a `()` result is `undefined`, where a declared function returns `void` | | `&dyn Trait`, `&mut dyn Trait`, `Box`, `Arc`, `Rc`, `Weak`, `Pin

` of one | an object with the trait's methods, its supertraits' included: `shape.area$Shape()`; behind an `Arc` or an `Rc`, an `rs.ArcDyn` sharing the count of the `Arc` it was made from | | a pointer to a struct whose last field unsizes to a trait object: `Rc>`, `Arc>`, `Arc>`, `&mut Cell`, `Rc>`, `Arc>`, the crate's own `&Tail` | `rs.tailDyn` of the struct it was made from: the same struct, whose other fields, borrows, and lock are that struct's own, and whose last field is the trait object a `&mut` to that field would be, made where it is read; a `dyn Future`'s polls the one future in place and drops it with the struct. A `ManuallyDrop` is the trait object of what it holds, which a box of it never drops | | `dyn Error` | an object of its `fmt$Debug` and `fmt$Display`, and its `source` where a call dispatches to it: `e.source$Error()` | -| `dyn Any` | an instance of the class its value's type was wrapped in, which a downcast checks for: `a instanceof of$dyns_Square ? a.v : undefined` | +| `dyn Any` | an instance of the class its value's type was wrapped in, which a downcast checks for: `a instanceof of$dyns_Square ? a.v : undefined`, and whose `type_id` gives that type's: `a.typeId$Any()` | | `dyn Fn`, `dyn FnMut`, `dyn FnOnce` | the closure or function it was made of: `(a0: rs.u8) => rs.u32` | | `dyn Iterator`, `dyn DoubleEndedIterator`, `dyn ExactSizeIterator`, `dyn FusedIterator` | `rs.Iter`: `rs.iterDyn` of the iterator it was made of | | constant | its value, inlined as Rust inlines it, so each use of one holding a cell is a cell of its own; one with no value tree, or holding a cell, read from the memory rustc evaluated | @@ -651,9 +652,11 @@ mapping makes, such as a `Vec`'s `io::Write`, is refused where a call dispat A `dyn Buf` holds no `chunks_vectored`, whose `IoSlice` has no JS value, so a call of it fails. -A `dyn Any` is such an object with no methods, whose class `is`, `downcast_ref`, +A `dyn Any` is such an object of `type_id` alone, whose class `is`, `downcast_ref`, `downcast_mut`, and `Box`'s, `Arc`'s, and `Rc`'s `downcast` check for: `a instanceof of$dyns_Square ? -a.v : undefined`. Its `Debug` writes `Any { .. }`, as std's does. A `&mut` one of a value JS copies is the class holding its `rs.Place`, +a.v : undefined`. Its `Debug` writes `Any { .. }`, as std's does. Its `type_id`, which it +holds once a call dispatches to it, and a crate's own trait over `Any` from the start, is a +constant of its class's type's `TypeId`, as `TypeId::of` and a value's `type_id` are. A `&mut` one of a value JS copies is the class holding its `rs.Place`, or the class itself behind a `Box`, as a `&mut dyn Trait`'s is, and `Arc::downcast` gives back the `Arc` the object was made from, or, of one made from a box, an `rs.ArcDyn` of the value sharing its count. @@ -685,7 +688,7 @@ box drops the iterator it owns, and the last `Arc` the one they share. `Arc::get `Arc` or `Weak` shares it, but a `dyn Any`'s, whose value `Arc::downcast` gives back. A trait object of a trait with a const parameter, of a byte array, or behind any other -pointer, such as a struct's last field, is refused, and so is `Any::type_id`. +pointer, such as a struct's last field, is refused. An `async fn` returns a future without running anything: an instance of a class of its own (`sumFuture`), the state machine MIR's coroutine transform makes. Its fields are the @@ -942,6 +945,11 @@ they need. `rs.Borrowed`, so it clones and drops as std's does and only the borrowed side pays a wrapper. Untagged, `to_mut` would clone on every call and a `match` would be refused; tagged, every `Cow` would be wrapped. +- A `TypeId` is the hash std's holds, rather than the class of its type, which a `&mut dyn + Any` of a value JS copies has a second of and a type no object holds has none of, or + the type's path, whose order and `Debug` are not std's. It is the crate's metadata's, so + a translation and a native build of one crate agree on which types are equal but not on + the bits. - A `Duration` keeps std's fields, whole seconds and nanoseconds, rather than one count of nanoseconds, so its overflow, `checked_*`, and ordering are std's exactly, and a lowering of its methods from core's MIR would read the same fields. @@ -1109,9 +1117,9 @@ they need. ## Not yet 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/README.md)), and trait objects other than -`dyn Debug`, `dyn Display`, `dyn Future`, `dyn Any`, `dyn Error`, `dyn Fn*`, `dyn Buf`, `dyn Iterator` and its subtraits, 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` made `From` a string, binds to +fields overlap in different types ([unsafe](/quest/m3/ts-unsafe/README.md)), and trait objects of a trait +with a const parameter, of an array, or behind a pointer other than `&`, `&mut`, `Box`, `Arc`, `Rc`, +`Weak`, or `Pin`, fail translation. Until they translate, an item that needs one, such as a `Box` 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, a closure that owns a value diff --git a/ts/fixtures/lang/dyns/caller.ts b/ts/fixtures/lang/dyns/caller.ts index 63150528f..743e8f7ae 100644 --- a/ts/fixtures/lang/dyns/caller.ts +++ b/ts/fixtures/lang/dyns/caller.ts @@ -20,4 +20,12 @@ const shared = [[], [1], [0, 0], [255, 255, 0, 1, 2, 3, 4, 5], [7, 0xff, 255, 25 Uint8Array.of(...op("dyns::shared"), 0, ...bytes), ); -export const cases: Uint8Array[] = [...dyns, ...shared]; +/** + * `ids::ids` too short to run, and boxing each kind of value, the first two bytes picking + * ids at both ends of the list. + */ +const ids = [[], [1], [0, 12, 0, 1, 2], [12, 0, 255, 254, 253, 3]].map((bytes) => + Uint8Array.of(...op("ids::ids"), 0, ...bytes), +); + +export const cases: Uint8Array[] = [...dyns, ...shared, ...ids]; diff --git a/ts/fixtures/lang/dyns/expected/core/any.Any.ts b/ts/fixtures/lang/dyns/expected/core/any.Any.ts index ba72bb625..fd88dfe56 100644 --- a/ts/fixtures/lang/dyns/expected/core/any.Any.ts +++ b/ts/fixtures/lang/dyns/expected/core/any.Any.ts @@ -1,5 +1,7 @@ // Generated by rust2-ts. Do not edit. -export type Any = object; +import * as rs from "../rs.ts"; + +export type Any = { typeId$Any: () => rs.TypeId }; //# sourceMappingURL=any.Any.ts.map diff --git a/ts/fixtures/lang/dyns/expected/core/char/methods.ts b/ts/fixtures/lang/dyns/expected/core/char/methods.ts new file mode 100644 index 000000000..5b52ab359 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/core/char/methods.ts @@ -0,0 +1,27 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "../../rs.ts"; + +export function toDigit$14z95r4(self: rs.char, radix: rs.u32): rs.u32 | undefined { + let value!: rs.u32; + if (radix >= 2) { + if (radix <= 36) { + b1: { + if (self > 57) { + if (radix > 10) { + value = rs.u32Add((rs.u32WrappingSub(self as rs.u32, 65 as rs.u32) & 4294967263) >>> 0, 10); + break b1; + } + } + value = rs.u32WrappingSub(self as rs.u32, 48 as rs.u32); + } + if (!(value < radix)) { + return undefined; + } + return value; + } + } + rs.panicFmt(["to_digit: invalid radix -- radix must be in the range 2 to 36 inclusive"]); +} + +//# sourceMappingURL=methods.ts.map diff --git a/ts/fixtures/lang/dyns/expected/core/num.ts b/ts/fixtures/lang/dyns/expected/core/num.ts new file mode 100644 index 000000000..a60c6ad71 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/core/num.ts @@ -0,0 +1,181 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "../rs.ts"; +import { toDigit$14z95r4 } from "../core/char/methods.ts"; + +export function canNotOverflow$05izqtt(radix: rs.u32, isSignedTy: boolean, _digitsData: Uint8Array, _digitsStart: number, digitsLen: number): boolean { + if (!(radix <= 16)) { + return false; + } + return digitsLen <= rs.usizeSub(rs.usizeMul(16, 2), isSignedTy ? 1 : 0); +} + +export function fromAsciiBytesRadixPanic(radix: rs.u32): never { + doPanic$1tugyvz(radix); +} + +export function doPanic$1tugyvz(radix: rs.u32): never { + runtime$09k496w(radix); +} + +export function runtime$09k496w(radix: rs.u32): never { + rs.panicFmt(["from_ascii_bytes_radix: radix must lie in the range `[2, 36]` - found ", (f: rs.Formatter) => rs.u32Display(radix, f)]); +} + +export function fromAsciiBytesRadixImpl$0paozdl(src: rs.ByteSlice, radix: rs.u32): rs.Result { + let _12_0!: boolean; + let _12_1Data!: Uint8Array; + let _12_1Start!: number; + let _12_1Len!: number; + if (!(2 > radix)) { + if (!(radix > 36)) { + if (!(src.length === 0)) { + const isSignedTy: boolean = rs.u128Gt(0, 0); + if (src.length === 1) { + const value7 = src.data[src.start]; + if (value7 === 43 || value7 === 45) { + return rs.Err({ kind: "InvalidDigit" as rs.IntErrorKind }); + } + } + b3: { + if (src.length >= 1) { + const value8 = src.data[src.start]; + if (value8 === 43) { + _12_0 = true; + _12_1Data = src.data; + _12_1Start = src.start + 1; + _12_1Len = src.length - 1; + break b3; + } + if (value8 === 45) { + const rest2: rs.ByteSlice = rs.byteSliceRange(src, 1); + if (isSignedTy) { + _12_0 = false; + _12_1Data = rest2.data; + _12_1Start = rest2.start; + _12_1Len = rest2.length; + break b3; + } + } + } + _12_0 = true; + _12_1Data = src.data; + _12_1Start = src.start; + _12_1Len = src.length; + } + const isPositive: boolean = _12_0; + let digitsData: Uint8Array = _12_1Data; + let digitsStart: number = _12_1Start; + let digitsLen: number = _12_1Len; + let result: rs.u128 = 0; + b5: { + if (!canNotOverflow$05izqtt(radix, isSignedTy, digitsData, digitsStart, digitsLen)) { + if (!isPositive) { + while (true) { + if (!(digitsLen >= 1)) { + break b5; + } + const c4: rs.u8 = digitsData[digitsStart]; + const restData5: Uint8Array = digitsData; + const restStart5: number = digitsStart + 1; + const restLen5: number = digitsLen - 1; + const mul2: rs.u128 | undefined = rs.u128CheckedMul(result, radix as rs.u128); + const _92: rs.u32 | undefined = toDigit$14z95r4(c4 as rs.char, radix); + if (_92 === undefined) { + return rs.Err({ kind: "InvalidDigit" as rs.IntErrorKind }); + } + const x4: rs.u128 = _92 as rs.u128; + if (mul2 === undefined) { + return rs.Err({ kind: "NegOverflow" as rs.IntErrorKind }); + } + result = mul2; + const _103: rs.u128 | undefined = rs.u128CheckedSub(result, x4); + if (_103 === undefined) { + return rs.Err({ kind: "NegOverflow" as rs.IntErrorKind }); + } + result = _103; + digitsData = restData5; + digitsStart = restStart5; + digitsLen = restLen5; + } + } + while (true) { + if (!(digitsLen >= 1)) { + break b5; + } + const c3: rs.u8 = digitsData[digitsStart]; + const restData4: Uint8Array = digitsData; + const restStart4: number = digitsStart + 1; + const restLen4: number = digitsLen - 1; + const mul: rs.u128 | undefined = rs.u128CheckedMul(result, radix as rs.u128); + const _67: rs.u32 | undefined = toDigit$14z95r4(c3 as rs.char, radix); + if (_67 === undefined) { + return rs.Err({ kind: "InvalidDigit" as rs.IntErrorKind }); + } + const x3: rs.u128 = _67 as rs.u128; + if (mul === undefined) { + return rs.Err({ kind: "PosOverflow" as rs.IntErrorKind }); + } + result = mul; + const _78: rs.u128 | undefined = rs.u128CheckedAdd(result, x3); + if (_78 === undefined) { + return rs.Err({ kind: "PosOverflow" as rs.IntErrorKind }); + } + result = _78; + digitsData = restData4; + digitsStart = restStart4; + digitsLen = restLen4; + } + } + if (!isPositive) { + while (true) { + if (!(digitsLen >= 1)) { + break b5; + } + const c2: rs.u8 = digitsData[digitsStart]; + const restData3: Uint8Array = digitsData; + const restStart3: number = digitsStart + 1; + const restLen3: number = digitsLen - 1; + result = rs.u128WrappingMul(result, radix as rs.u128); + const _49: rs.u32 | undefined = toDigit$14z95r4(c2 as rs.char, radix); + if (_49 === undefined) { + return rs.Err({ kind: "InvalidDigit" as rs.IntErrorKind }); + } + result = rs.u128WrappingSub(result, _49 as rs.u128); + digitsData = restData3; + digitsStart = restStart3; + digitsLen = restLen3; + } + } + while (true) { + if (!(digitsLen >= 1)) { + break b5; + } + const c: rs.u8 = digitsData[digitsStart]; + const restData2: Uint8Array = digitsData; + const restStart2: number = digitsStart + 1; + const restLen2: number = digitsLen - 1; + result = rs.u128WrappingMul(result, radix as rs.u128); + const _32: rs.u32 | undefined = toDigit$14z95r4(c as rs.char, radix); + if (_32 === undefined) { + return rs.Err({ kind: "InvalidDigit" as rs.IntErrorKind }); + } + result = rs.u128WrappingAdd(result, _32 as rs.u128); + digitsData = restData2; + digitsStart = restStart2; + digitsLen = restLen2; + } + } + return rs.Ok(result); + } + return rs.Err({ kind: "Empty" as rs.IntErrorKind }); + } + } + fromAsciiBytesRadixPanic(radix); +} + +export function fromStrRadix$09i2k2a(src: string, radix: rs.u32): rs.Result { + return fromAsciiBytesRadixImpl$0paozdl(rs.stringAsBytes(src), radix); +} + +//# sourceMappingURL=num.ts.map diff --git a/ts/fixtures/lang/dyns/expected/core/option.ts b/ts/fixtures/lang/dyns/expected/core/option.ts index 9c9419eb5..e4a26f2dd 100644 --- a/ts/fixtures/lang/dyns/expected/core/option.ts +++ b/ts/fixtures/lang/dyns/expected/core/option.ts @@ -27,6 +27,13 @@ export function unwrapOr$1fr498u(self: rs.u8 | undefined, default_: rs.u8): rs.u return self; } +export function unwrapOrDefault$15dio94(self: rs.u128 | undefined): rs.u128 { + if (self === undefined) { + return 0; + } + return self; +} + export function map$1rkcfqi(self: rs.Arc> | undefined, f: (a0: rs.Arc>) => rs.u32): rs.u32 | undefined { if (self === undefined) { return undefined; @@ -97,6 +104,13 @@ export function map$1aeqf1p(self: [Tagged.Tagged, Tagged.Tagged] | undefined, f: return f(self); } +export function map$0v3tpx3(self: rs.u128 | undefined, f: (a0: rs.u128) => rs.String): rs.String | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + export function map$070fw7m(self: rs.u32 | undefined, f: (a0: rs.u32) => rs.u32): rs.u32 | undefined { if (self === undefined) { return undefined; diff --git a/ts/fixtures/lang/dyns/expected/core/result.ts b/ts/fixtures/lang/dyns/expected/core/result.ts index 05b48d5ae..71c299dee 100644 --- a/ts/fixtures/lang/dyns/expected/core/result.ts +++ b/ts/fixtures/lang/dyns/expected/core/result.ts @@ -48,6 +48,13 @@ export function ok$1lmsnde(self: rs.Result<[rs.usize, rs.u8], rs.Arc>): return _0; } +export function ok$03r6cuf(self: rs.Result): rs.u128 | undefined { + if (self.ok) { + return self.value; + } + return undefined; +} + export function ok$1wwd922(self: rs.Result): rs.u16 | undefined { let _0!: rs.u16 | undefined; if (self.ok) { diff --git a/ts/fixtures/lang/dyns/expected/dyns.Keyed.ts b/ts/fixtures/lang/dyns/expected/dyns.Keyed.ts index 0aff1ddb4..6833a5fcb 100644 --- a/ts/fixtures/lang/dyns/expected/dyns.Keyed.ts +++ b/ts/fixtures/lang/dyns/expected/dyns.Keyed.ts @@ -3,6 +3,6 @@ import * as rs from "./rs.ts"; /** A trait over `Any`, whose objects upcast to `dyn Any`. */ -export type Keyed = { key$Keyed: () => rs.u32 }; +export type Keyed = { key$Keyed: () => rs.u32; typeId$Any: () => rs.TypeId }; //# sourceMappingURL=dyns.Keyed.ts.map diff --git a/ts/fixtures/lang/dyns/expected/ids.Byte.ts b/ts/fixtures/lang/dyns/expected/ids.Byte.ts new file mode 100644 index 000000000..b9e8b04a8 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/ids.Byte.ts @@ -0,0 +1,14 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +/** A value of a type of its own, which only its `TypeId` tells from a `u8`. */ +export interface Byte { + _0: rs.u8; +} + +export function tag$ids_Marked(self: Byte): rs.u8 { + return self._0; +} + +//# sourceMappingURL=ids.Byte.ts.map diff --git a/ts/fixtures/lang/dyns/expected/ids.Capped.ts b/ts/fixtures/lang/dyns/expected/ids.Capped.ts new file mode 100644 index 000000000..7682e8b03 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/ids.Capped.ts @@ -0,0 +1,28 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +/** + * Takes at most `cap` bytes, logging each `write_str` it takes, and fails the call that + * would pass `cap`. + */ +export interface Capped { + log: rs.Vec; + len: rs.usize; + calls: rs.usize; + cap: rs.usize; +} + +export function writeStr$fmt_Write(self: Capped, s: string): rs.Result { + self.calls = rs.usizeAdd(self.calls, 1); + if (!(rs.usizeAdd(self.len, rs.stringLen(s)) > self.cap)) { + const _10: rs.usize = rs.stringLen(s); + self.len = rs.usizeAdd(self.len, _10); + const _14: rs.String = rs.stringFrom(s); + self.log.items.push(_14); + return rs.OK; + } + return rs.Err(new rs.FmtError()); +} + +//# sourceMappingURL=ids.Capped.ts.map diff --git a/ts/fixtures/lang/dyns/expected/ids.Log.ts b/ts/fixtures/lang/dyns/expected/ids.Log.ts new file mode 100644 index 000000000..b6ce55e57 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/ids.Log.ts @@ -0,0 +1,38 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +export function default$Default(): Log { + return { _0: new rs.Vec() }; +} + +/** Logs each call it is given, and how many bytes `write` is. */ +export interface Log { + _0: rs.Vec; +} + +export function write$hash_Hasher(self: Log, _bytesData: Uint8Array, _bytesStart: number, bytesLen: number): void { + const _5: rs.String = rs.fmtFormat(["write ", (f: rs.Formatter) => rs.usizeDisplay(bytesLen, f)]); + self._0.items.push(_5); +} + +export function writeU8$hash_Hasher(self: Log, x: rs.u8): void { + const _5: rs.String = rs.fmtFormat(["u8 ", (f: rs.Formatter) => rs.u8Display(x, f)]); + self._0.items.push(_5); +} + +export function writeU64$hash_Hasher(self: Log, x: rs.u64): void { + const _5: rs.String = rs.fmtFormat(["u64 ", (f: rs.Formatter) => rs.u64LowerHex(x, f)]); + self._0.items.push(_5); +} + +export function finish$hash_Hasher(_self: Log): rs.u64 { + return 0; +} + +/** The methods of `ids::Log`'s impl, which the runtime's hashing calls. */ +export const hasher$hash_Hasher: rs.HasherImpl = { write: (a0: Log, a1: rs.ByteSlice): undefined => { + write$hash_Hasher(a0, a1.data, a1.start, a1.length); +}, finish: finish$hash_Hasher, u8: writeU8$hash_Hasher as (a0: Log, a1: rs.u8) => undefined, u64: writeU64$hash_Hasher as (a0: Log, a1: rs.u64) => undefined }; + +//# sourceMappingURL=ids.Log.ts.map diff --git a/ts/fixtures/lang/dyns/expected/ids.Marked.ts b/ts/fixtures/lang/dyns/expected/ids.Marked.ts new file mode 100644 index 000000000..9f6af73e0 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/ids.Marked.ts @@ -0,0 +1,8 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +/** A trait over `Any`, whose objects give their value's `type_id`. */ +export type Marked = { tag$Marked: () => rs.u8; typeId$Any: () => rs.TypeId }; + +//# sourceMappingURL=ids.Marked.ts.map diff --git a/ts/fixtures/lang/dyns/expected/ids.Mood.ts b/ts/fixtures/lang/dyns/expected/ids.Mood.ts new file mode 100644 index 000000000..b04e7af63 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/ids.Mood.ts @@ -0,0 +1,6 @@ +// Generated by rust2-ts. Do not edit. + +/** An enum, which JS copies, so a `&mut dyn Any` of one is of a class of its place. */ +export type Mood = "Calm" | "Loud"; + +//# sourceMappingURL=ids.Mood.ts.map diff --git a/ts/fixtures/lang/dyns/expected/ids.WithId.ts b/ts/fixtures/lang/dyns/expected/ids.WithId.ts new file mode 100644 index 000000000..007700e4a --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/ids.WithId.ts @@ -0,0 +1,32 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; +import * as Log from "./ids.Log.ts"; + +export function eq$cmp_PartialEq(self: WithId, other: WithId): boolean { + if (!(self.n === other.n)) { + return false; + } + return self.id === other.id; +} + +export function cmp$cmp_Ord(self: WithId, other: WithId): rs.Ordering { + const cmp: rs.Ordering = rs.typeIdCmp(self.id, other.id); + if (cmp === 0) { + return rs.cmp(self.n, other.n); + } + return cmp; +} + +export function hash$Hash_ids_Log(self: WithId, state: Log.Log): void { + rs.typeIdHash(self.id, new rs.OwnHasher(state, Log.hasher$hash_Hasher)); + rs.hash8(self.n, new rs.OwnHasher(state, Log.hasher$hash_Hasher)); +} + +/** A `TypeId` in a struct, whose derives compare, order, and hash it as std's. */ +export interface WithId { + id: rs.TypeId; + n: rs.u8; +} + +//# sourceMappingURL=ids.WithId.ts.map diff --git a/ts/fixtures/lang/dyns/expected/ids.ts b/ts/fixtures/lang/dyns/expected/ids.ts new file mode 100644 index 000000000..c637605f5 --- /dev/null +++ b/ts/fixtures/lang/dyns/expected/ids.ts @@ -0,0 +1,382 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; +import { of$ids_Byte, of$ids_Mood, of$ref_str, of$u32, of$u8, ofPlace$ids_Mood, ofPlace$u32 } from "./lib.ts"; +import type * as Any from "./core/any.Any.ts"; +import { fromStrRadix$09i2k2a as num_fromStrRadix$09i2k2a } from "./core/num.ts"; +import { unwrapOrDefault$15dio94 as option_unwrapOrDefault$15dio94, map$0v3tpx3 as option_map$0v3tpx3 } from "./core/option.ts"; +import { ok$03r6cuf } from "./core/result.ts"; +import { put } from "./harness.ts"; +import type * as Byte from "./ids.Byte.ts"; +import * as Capped from "./ids.Capped.ts"; +import * as Log from "./ids.Log.ts"; +import type * as Marked from "./ids.Marked.ts"; +import type * as Mood from "./ids.Mood.ts"; +import * as WithId from "./ids.WithId.ts"; + +export function tag$ids_Marked_u8(self: rs.u8): rs.u8 { + return self; +} + +/** `id`'s `Debug`, written into a `Capped` of `cap` bytes. */ +export function capped(id: rs.TypeId, cap: rs.usize): [rs.Result, Capped.Capped] { + const w: Capped.Capped = { log: new rs.Vec(), len: 0, calls: 0, cap }; + const args3 = [(f: rs.Formatter) => rs.fmtTypeIdDebug(id, f)]; + return [rs.fmtWrite((s) => Capped.writeStr$fmt_Write(w, s), (c) => Capped.writeStr$fmt_Write(w, String.fromCodePoint(c)), [args3[0]]), w] as [rs.Result, Capped.Capped]; +} + +/** `T`'s `TypeId`, through a type parameter. */ +export function idOf$Option_u8(): rs.TypeId { + return "3801bd64778907c7633cf473fe556a70"; +} + +/** `T`'s `TypeId`, through a type parameter. */ +export function idOf$u8(): rs.TypeId { + return "cfb1622e7734f8af272d8d182905dc25"; +} + +/** The `TypeId` of what `x` is, through a type parameter that may be a trait object. */ +export function idOfVal$dyn_Any(_1: Any.Any): rs.TypeId { + return "f48384178e2341267f2bcf8f82c035ac"; +} + +/** The `TypeId` of what `x` is, through a type parameter that may be a trait object. */ +export function idOfVal$dyn_ids_Marked(_1: Marked.Marked): rs.TypeId { + return "d356aeac368968f265dedab8a8d72ad8"; +} + +/** The `TypeId` of what `x` is, through a type parameter that may be a trait object. */ +export function idOfVal$ids_Mood(_1: Mood.Mood): rs.TypeId { + return "328e59b82c6cb0a6eb720af972265604"; +} + +/** The `TypeId` of what `x` is, through a type parameter that may be a trait object. */ +export function idOfVal$str(_1: string): rs.TypeId { + return "b7381ee5f3fdfc9d7fa709f37e151622"; +} + +/** The 128-bit hash `Debug` writes, if it writes `TypeId(0x…)` of 32 hex digits. */ +export function hashOf(id: rs.TypeId): rs.u128 | undefined { + let _21!: boolean; + const _13: string | undefined = rs.strStripPrefix(rs.fmtFormat([(f: rs.Formatter) => rs.fmtTypeIdDebug(id, f)]).v, "TypeId(0x"); + if (_13 !== undefined) { + const _11: string | undefined = rs.strStripSuffix(_13, ")"); + if (_11 !== undefined) { + if (!(rs.stringLen(_11) === 32)) { + _21 = false; + } else { + _21 = rs.iterAll(rs.strBytes(_11), (b: rs.u8): boolean => { + if (!rs.isAsciiHexdigit(b)) { + return false; + } + return !rs.isAsciiUppercase(b); + }); + } + if (!_21) { + return undefined; + } + return ok$03r6cuf(num_fromStrRadix$09i2k2a(_11, 16)); + } + return undefined; + } + return undefined; +} + +/** + * The pointer-sized words of `id`'s hash as it lays them out, lowest first, which its + * derived `Ord` compares. + */ +export function words(id: rs.TypeId): rs.Vec { + const hash: rs.u128 = option_unwrapOrDefault$15dio94(hashOf(id)); + const bits: rs.u32 = 32; + const mask: rs.u128 = rs.u128Shr({ hi: 0xffffffff, w2: 0xffffffff, w1: 0xffffffff, lo: 0xffffffff } as rs.u128, rs.u32Sub(128, bits)); + return rs.iterToVec(rs.iterMap(new rs.Range(0, rs.u32Div(128, bits)), (i: rs.u32): rs.u128 => { + return rs.u128And(rs.u128Shr(hash, rs.u32Mul(i, bits)), mask); + }), undefined, 16, true); +} + +export function ids(dataData: Uint8Array, dataStart: number, dataLen: number, out: rs.ByteVec): void { + let mood!: Mood.Mood; + let _215!: Any.Any; + let _439!: boolean; + let _534!: rs.TypeId; + const _628: (a0: WithId.WithId, a1: WithId.WithId) => rs.Ordering = (k: WithId.WithId, l: WithId.WithId): rs.Ordering => { + const _63: rs.Vec = words(k.id); + const _9: rs.Vec = words(l.id); + return rs.orderingThen(rs.cmpSlice(rs.cmp)(_63, _9), rs.cmp(k.n, l.n)); + }; + if (dataLen >= 2) { + const _10: [rs.u8, rs.u8] = [dataData[dataStart], dataData[dataStart + 1]]; + const a: rs.u8 = _10[0]; + const b: rs.u8 = _10[1]; + let n: rs.u32 = a as rs.u32; + if (!(rs.u8Rem(b, 2) === 0)) { + mood = "Loud" as Mood.Mood; + } else { + mood = "Calm" as Mood.Mood; + } + const byte: Byte.Byte = { _0: b }; + const ids: rs.TypeId[] = ["cfb1622e7734f8af272d8d182905dc25", "79f19461b401588885a89e43144b08f1", "8f5d7517f5a241dab4213252fd0d53e8", "328e59b82c6cb0a6eb720af972265604", "24c161f26c70c1f1b9f602dc5fe9f65c", "b7381ee5f3fdfc9d7fa709f37e151622", "05fc01408e45e7cddf9e041e3391eafc", "3801bd64778907c7633cf473fe556a70", "513ce68a38c017b0c2e73410455cb3b0", "f48384178e2341267f2bcf8f82c035ac", "37a732caafaf8e089d3a9d4c07b2ec78", "d356aeac368968f265dedab8a8d72ad8", "25ff997ce0b3a76fb5ce30b55accc57f"]; + const found: rs.TypeId[] = [idOf$u8(), "cfb1622e7734f8af272d8d182905dc25", "cfb1622e7734f8af272d8d182905dc25", "8f5d7517f5a241dab4213252fd0d53e8", "cfb1622e7734f8af272d8d182905dc25", "79f19461b401588885a89e43144b08f1", "8f5d7517f5a241dab4213252fd0d53e8", idOfVal$str("str"), idOfVal$ids_Mood(mood), idOfVal$dyn_Any(new of$u8(a)), idOf$Option_u8()]; + const iter: rs.Iter = rs.iterSlice(found); + while (true) { + const _53: rs.TypeId | rs.Done = iter.next(); + if (_53 === rs.DONE) { + break; + } + const id: rs.TypeId = _53; + const part = rs.iterPosition(rs.iterSlice(ids), (i: rs.TypeId): boolean => { + return i === id; + }); + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtOption(part, f, rs.usizeDebug), " "]).v); + } + const part2 = rs.iterPosition(rs.iterSlice(ids), (i2: rs.TypeId): boolean => { + return i2 === "cfb1622e7734f8af272d8d182905dc25"; + }); + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtOption(part2, f, rs.usizeDebug), " "]).v); + const iter2: rs.Iter = rs.iterSlice([new of$u8(a), new of$u32(n), new of$ids_Byte(byte), new of$ids_Mood(mood), new of$ref_str("str")]); + while (true) { + const _106: Any.Any | rs.Done = iter2.next(); + if (_106 === rs.DONE) { + break; + } + const part3 = rs.iterPosition(rs.iterSlice(ids), (i3: rs.TypeId): boolean => { + return i3 === _106.typeId$Any(); + }); + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtOption(part3, f, rs.usizeDebug), " "]).v); + } + const iter3: rs.Iter = rs.iterSlice([new ofPlace$u32(rs.place(() => n, (value) => n = value)), new ofPlace$ids_Mood(rs.place(() => mood, (value2) => mood = value2))]); + while (true) { + const _135: Any.Any | rs.Done = iter3.next(); + if (_135 === rs.DONE) { + break; + } + const part4 = rs.iterPosition(rs.iterSlice(ids), (i4: rs.TypeId): boolean => { + return i4 === _135.typeId$Any(); + }); + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtOption(part4, f, rs.usizeDebug), " "]).v); + } + const iter4: rs.Iter = rs.iterSlice([new of$u8(a), new of$ids_Byte(byte)]); + while (true) { + const _165: Marked.Marked | rs.Done = iter4.next(); + if (_165 === rs.DONE) { + break; + } + const any: Any.Any = _165; + const _178: rs.u8 = _165.tag$Marked(); + const _180: rs.usize | undefined = rs.iterPosition(rs.iterSlice(ids), (i5: rs.TypeId): boolean => { + return i5 === _165.typeId$Any(); + }); + const _187: boolean = _165.typeId$Any() === any.typeId$Any(); + const _193: boolean = idOfVal$dyn_ids_Marked(_165) === "d356aeac368968f265dedab8a8d72ad8"; + const args43 = [(f: rs.Formatter) => rs.u8Display(_178, f), (f: rs.Formatter) => rs.fmtOption(_180, f, rs.usizeDebug), (f: rs.Formatter) => rs.fmtBool(_187, f), (f: rs.Formatter) => rs.fmtBool(_193, f)]; + put(out, rs.fmtFormat([args43[0], " ", args43[1], " ", args43[2], " ", args43[3], " "]).v); + } + const boxes: rs.Vec = new rs.Vec(); + for (const x of rs.byteSliceView(new rs.ByteSlice(dataData, dataStart + 2, dataLen - 2))) { + const _216: rs.u8 = rs.u8Rem(x, 3); + if (_216 === 0) { + _215 = new of$u8(x); + } else if (_216 === 1) { + _215 = new of$ids_Byte({ _0: x }); + } else { + _215 = new of$ids_Mood("Loud" as Mood.Mood); + } + boxes.items.push(_215); + } + for (const _226 of boxes.items) { + const inner: rs.TypeId = _226.typeId$Any(); + const _240: rs.usize | undefined = rs.iterPosition(rs.iterSlice(ids), (i6: rs.TypeId): boolean => { + return i6 === inner; + }); + const _248: boolean = "37a732caafaf8e089d3a9d4c07b2ec78" === "37a732caafaf8e089d3a9d4c07b2ec78"; + const args44 = [(f: rs.Formatter) => rs.fmtOption(_240, f, rs.usizeDebug), (f: rs.Formatter) => rs.fmtBool(_248, f)]; + put(out, rs.fmtFormat([args44[0], " ", args44[1], " "]).v); + } + const _259: rs.Arc = new rs.Arc(byte); + const shared: rs.Arc = new rs.ArcDyn(_259, new of$ids_Byte(_259.value)); + const boxed: Marked.Marked = new of$u8(a); + const _270: boolean = shared.value.typeId$Any() === "8f5d7517f5a241dab4213252fd0d53e8"; + const _278: boolean = "6864f1ad0ca168ae5e502d6898f6f9c3" === "6864f1ad0ca168ae5e502d6898f6f9c3"; + const _285: boolean = boxed.typeId$Any() === "cfb1622e7734f8af272d8d182905dc25"; + const args45 = [(f: rs.Formatter) => rs.fmtBool(_270, f), (f: rs.Formatter) => rs.fmtBool(_278, f), (f: rs.Formatter) => rs.fmtBool(_285, f)]; + put(out, rs.fmtFormat([args45[0], " ", args45[1], " ", args45[2], " "]).v); + const _298: rs.Slice = rs.sliceFrom(ids); + const pairs: () => rs.Iter<[rs.usize, rs.TypeId, rs.usize, rs.TypeId]> = (): rs.Iter<[rs.usize, rs.TypeId, rs.usize, rs.TypeId]> => { + return rs.iterFlatMap(rs.iterEnumerate(rs.iterSlice(rs.sliceItems(_298))), (_210: [rs.usize, rs.TypeId]): rs.Iter<[rs.usize, rs.TypeId, rs.usize, rs.TypeId]> => { + const i7: rs.usize = _210[0]; + const x3: rs.TypeId = _210[1]; + const _52: rs.Iter<[rs.usize, rs.TypeId]> = rs.iterEnumerate(rs.iterSlice(rs.sliceItems(_298))); + const captured = i7; + const captured2 = x3; + return rs.iterMap(_52, (_211: [rs.usize, rs.TypeId]): [rs.usize, rs.TypeId, rs.usize, rs.TypeId] => { + return [captured, captured2, _211[0], _211[1]] as [rs.usize, rs.TypeId, rs.usize, rs.TypeId]; + }); + }, undefined, true); + }; + const _307: boolean = rs.iterAll(pairs(), (_212: [rs.usize, rs.TypeId, rs.usize, rs.TypeId]): boolean => { + const i8: rs.usize = _212[0]; + const x4: rs.TypeId = _212[1]; + const j2: rs.usize = _212[2]; + const y3: rs.TypeId = _212[3]; + if (!(x4 === y3 === (i8 === j2))) { + return false; + } + return x4 !== y3 === (i8 !== j2); + }); + const _312: boolean = rs.iterAll(pairs(), (_213: [rs.usize, rs.TypeId, rs.usize, rs.TypeId]): boolean => { + const x5: rs.TypeId = _213[1]; + const y4: rs.TypeId = _213[3]; + const _613: rs.Ordering = rs.typeIdCmp(x5, y4); + const _1211: rs.Vec = words(x5); + const _145: rs.Vec = words(y4); + return _613 === rs.cmpSlice(rs.cmp)(_1211, _145); + }); + const _317: boolean = rs.iterAll(pairs(), (_224: [rs.usize, rs.TypeId, rs.usize, rs.TypeId]): boolean => { + const x6: rs.TypeId = _224[1]; + const y5: rs.TypeId = _224[3]; + const _614: rs.Ordering | undefined = rs.typeIdCmp(x6, y5); + const _136: rs.Vec = words(x6); + const _154: rs.Vec = words(y5); + return _614 === rs.cmpSlice(rs.cmp)(_136, _154) as rs.Ordering | undefined; + }); + const _322: boolean = rs.iterAll(pairs(), (_225: [rs.usize, rs.TypeId, rs.usize, rs.TypeId]): boolean => { + const x7: rs.TypeId = _225[1]; + const y6: rs.TypeId = _225[3]; + const _616: boolean = rs.typeIdCmp(x7, y6) === -1; + const _1112: rs.Vec = words(x7); + const _138: rs.Vec = words(y6); + if (!(_616 === (rs.cmpSlice(rs.cmp)(_1112, _138) === -1))) { + return false; + } + const order2 = rs.typeIdCmp(x7, y6); + const _146: boolean = order2 !== undefined && order2 >= 0; + const _192: rs.Vec = words(x7); + const _2110: rs.Vec = words(y6); + const order3 = rs.cmpSlice(rs.cmp)(_192, _2110); + return _146 === (order3 !== undefined && order3 >= 0); + }); + const args46 = [(f: rs.Formatter) => rs.fmtBool(_307, f), (f: rs.Formatter) => rs.fmtBool(_312, f), (f: rs.Formatter) => rs.fmtBool(_317, f), (f: rs.Formatter) => rs.fmtBool(_322, f)]; + put(out, rs.fmtFormat([args46[0], " ", args46[1], " ", args46[2], " ", args46[3], " "]).v); + const _337: rs.usize = rs.usizeRem(a as rs.usize, rs.sliceFrom(ids).length); + if (!(_337 < 13)) { + rs.panic("index out of bounds: the len is " + 13 + " but the index is " + _337); + } + const _336: rs.TypeId = ids[_337]; + const _345: rs.usize = rs.usizeRem(b as rs.usize, rs.sliceFrom(ids).length); + if (!(_345 < 13)) { + rs.panic("index out of bounds: the len is " + 13 + " but the index is " + _345); + } + const _335: [rs.TypeId, rs.TypeId] = [_336, ids[_345]]; + const x2: rs.TypeId = _336; + const y: rs.TypeId = _335[1]; + const part5 = rs.iterAll(rs.iterSlice(ids), (_227: rs.TypeId): boolean => { + return hashOf(_227) !== undefined; + }); + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtBool(part5, f), " "]).v); + const _371: [rs.Result, Capped.Capped] = capped(x2, rs.u8Rem(a, 10) as rs.usize); + const result: rs.Result = _371[0]; + const w: Capped.Capped = _371[1]; + const _386: rs.Fmt = (f: rs.Formatter) => rs.fmtResult(result, f, rs.fmtUnit, rs.fmtErrorDebug); + const arg = w.calls; + const _387: rs.Fmt = (f: rs.Formatter) => rs.usizeDisplay(arg, f); + const arg2 = w.log; + const args48 = [_386, _387, (f: rs.Formatter) => rs.fmtList(arg2.items, f, (value: rs.String, f: rs.Formatter) => rs.fmtStr(value.v, f))]; + put(out, rs.fmtFormat([args48[0], " ", args48[1], " ", args48[2], " "]).v); + const debug: rs.String = rs.fmtFormat([(f: rs.Formatter) => rs.fmtTypeIdDebug(x2, f)]); + const zeros: rs.usize = rs.min(rs.iterCount(rs.iterTakeWhile(rs.strBytes(rs.strSlice(debug.v, 9)), (_228: rs.u8): boolean => { + return _228 === 48; + })), 31); + const _409: [rs.Result, Capped.Capped] = capped(x2, 4294967295); + const result2: rs.Result = _409[0]; + const w2: Capped.Capped = _409[1]; + const part6 = rs.usizeSub(w2.log.items.length, 1); + const _413: rs.Slice = rs.sliceRange(rs.sliceFrom(w2.log.items), 2, part6); + const n2: rs.usize = w2.log.items.length; + const _431: rs.Slice = rs.sliceRange(rs.sliceFrom(w2.log.items), 0, 2); + const _435: rs.String | undefined = rs.sliceLast(rs.sliceFrom(w2.log.items)); + if (!(n2 === rs.usizeAdd(4, zeros))) { + _439 = false; + } else { + _439 = rs.iterAll(rs.iterSlice(rs.sliceItems(rs.sliceRange(_413, 0, zeros))), (d: rs.String): boolean => { + return rs.eqStr(d, "0"); + }); + } + const _438: boolean = _439; + const _448: boolean = rs.eqStr(rs.strsJoin(rs.sliceItems(_413), ""), rs.strSlice(debug.v, 9, 41)); + const args50 = [(f: rs.Formatter) => rs.fmtResult(result2, f, rs.fmtUnit, rs.fmtErrorDebug), (f: rs.Formatter) => rs.fmtList(rs.sliceItems(_431), f, (value: rs.String, f: rs.Formatter) => rs.fmtStr(value.v, f)), (f: rs.Formatter) => rs.fmtOption(_435, f, (value: rs.String, f: rs.Formatter) => rs.fmtStr(value.v, f)), (f: rs.Formatter) => rs.fmtBool(_438, f), (f: rs.Formatter) => rs.fmtBool(_448, f)]; + put(out, rs.fmtFormat([args50[0], " ", args50[1], " ", args50[2], " ", args50[3], " ", args50[4], " "]).v); + const _465: [rs.Result, Capped.Capped] = capped(x2, rs.usizeSub(debug.len, 1)); + const result3: rs.Result = _465[0]; + const _478: boolean = rs.eqStr(rs.stringAdd(rs.strsJoin(_465[1].log.items, ""), ")"), debug); + const args51 = [(f: rs.Formatter) => rs.fmtResult(result3, f, rs.fmtUnit, rs.fmtErrorDebug), (f: rs.Formatter) => rs.fmtBool(_478, f)]; + put(out, rs.fmtFormat([args51[0], " ", args51[1], " "]).v); + const log: Log.Log = Log.default$Default(); + rs.typeIdHash(x2, new rs.OwnHasher(log, Log.hasher$hash_Hasher)); + const high: rs.String | undefined = option_map$0v3tpx3(hashOf(x2), (h: rs.u128): rs.String => { + const part7 = rs.u128Shr(h, 64); + return rs.fmtFormat(["u64 ", (f: rs.Formatter) => rs.u128LowerHex(part7, f)]); + }); + const _506: rs.usize = log._0.items.length; + const _511: rs.String | undefined = rs.sliceFirst(rs.sliceFrom(log._0.items)); + const _509: boolean = rs.eqOption(rs.eqStr)(_511, high); + const args55 = [(f: rs.Formatter) => rs.usizeDisplay(_506, f), (f: rs.Formatter) => rs.fmtBool(_509, f)]; + put(out, rs.fmtFormat([args55[0], " ", args55[1], " "]).v); + const _532: rs.TypeId = rs.ordMax(x2, y, rs.cmpLt(rs.typeIdCmp)); + const _537: rs.Vec = words(x2); + const _539: rs.Vec = words(y); + if (!(rs.cmpSlice(rs.cmp)(_537, _539) === -1)) { + _534 = x2; + } else { + _534 = y; + } + const arg3 = _532 === _534; + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtBool(arg3, f), " "]).v); + const sorted: rs.Vec = rs.vecFrom(ids.slice()); + rs.sliceSort(rs.sliceFrom(sorted.items), rs.cmpLt(rs.typeIdCmp), 16, true); + const ordered: boolean = rs.iterAll(rs.sliceWindows(rs.sliceFrom(sorted.items), 2), (w4: rs.Slice): boolean => { + rs.idx(w4, 0); + const _422: rs.Vec = words(w4.data[w4.start]); + rs.idx(w4, 1); + const _109: rs.Vec = words(w4.data[w4.start + 1]); + return rs.cmpSlice(rs.cmp)(_422, _109) === -1; + }); + const _556: rs.Iter = rs.iterChain(rs.iterSlice(ids), rs.iterSlice(found)); + const set: rs.BTreeSet = rs.btreeExtendKeys(rs.btreeNew(rs.typeIdCmp) as rs.BTreeSet, _556, true, undefined, undefined, 16, true); + const _563: rs.Iter = rs.iterChain(rs.iterSlice(ids), rs.iterSlice(found)); + const hashed: rs.HashSet = rs.hashExtendKeys(rs.hashNew(rs.hashing(rs.typeIdHash, rs.same)) as rs.HashSet, _563, undefined, undefined, 58720256); + const _577: rs.usize = rs.btreeLen(set); + const _580: rs.usize = rs.hashLen(hashed); + const args57 = [(f: rs.Formatter) => rs.fmtBool(ordered, f), (f: rs.Formatter) => rs.usizeDisplay(_577, f), (f: rs.Formatter) => rs.usizeDisplay(_580, f)]; + put(out, rs.fmtFormat([args57[0], " ", args57[1], " ", args57[2], " "]).v); + const _590: rs.Iter<[rs.TypeId, string]> = rs.iterSlice([["cfb1622e7734f8af272d8d182905dc25", "u8"] as [rs.TypeId, string], ["8f5d7517f5a241dab4213252fd0d53e8", "byte"] as [rs.TypeId, string], ["328e59b82c6cb0a6eb720af972265604", "mood"] as [rs.TypeId, string]]); + const names: rs.HashMap = rs.hashExtend(rs.hashNew(rs.hashing(rs.typeIdHash, rs.same)) as rs.HashMap, _590, undefined, undefined, undefined, 58720256); + for (const _603 of boxes.items) { + const part8 = rs.hashGet(names, _603.typeId$Any()); + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtOption(part8, f, rs.fmtStr), " "]).v); + } + const keys: WithId.WithId[] = [{ id: x2, n: a }, { id: y, n: b }, { id: x2, n: b }]; + const _636: rs.Slice = rs.sliceFrom(keys); + const part11 = rs.iterToVec(rs.iterMap(rs.iterFlatMap(rs.iterSlice(keys), (k2: WithId.WithId): rs.Iter<[WithId.WithId, WithId.WithId]> => { + return rs.iterMap(rs.iterSlice(rs.sliceItems(_636)), (l2: WithId.WithId): [WithId.WithId, WithId.WithId] => { + return [k2, l2] as [WithId.WithId, WithId.WithId]; + }); + }, undefined, true), (_229: [WithId.WithId, WithId.WithId]): [boolean, boolean] => { + const k3: WithId.WithId = _229[0]; + const l3: WithId.WithId = _229[1]; + return [WithId.eq$cmp_PartialEq(k3, l3), WithId.cmp$cmp_Ord(k3, l3) === _628(k3, l3)] as [boolean, boolean]; + }), "next", 2); + put(out, rs.fmtFormat([(f: rs.Formatter) => rs.fmtList(part11.items, f, (value: [boolean, boolean], f: rs.Formatter) => rs.fmtTuple([(f: rs.Formatter) => rs.fmtBool(value[0], f), (f: rs.Formatter) => rs.fmtBool(value[1], f)], f)), " "]).v); + const log2: Log.Log = Log.default$Default(); + WithId.hash$Hash_ids_Log(keys[0], log2); + const _665: rs.usize = log2._0.items.length; + const _668: rs.String | undefined = rs.sliceGet(rs.sliceFrom(log2._0.items), 1); + const args60 = [(f: rs.Formatter) => rs.usizeDisplay(_665, f), (f: rs.Formatter) => rs.fmtOption(_668, f, (value: rs.String, f: rs.Formatter) => rs.fmtStr(value.v, f))]; + put(out, rs.fmtFormat([args60[0], " ", args60[1]]).v); + rs.drop(boxed); + shared[Symbol.dispose](); + rs.dropVec(boxes); + } +} + +//# sourceMappingURL=ids.ts.map diff --git a/ts/fixtures/lang/dyns/expected/lib.ts b/ts/fixtures/lang/dyns/expected/lib.ts index b27915d96..28cc47b27 100644 --- a/ts/fixtures/lang/dyns/expected/lib.ts +++ b/ts/fixtures/lang/dyns/expected/lib.ts @@ -22,6 +22,9 @@ import * as Square from "./dyns.Square.ts"; import * as Steps from "./dyns.Steps.ts"; import type * as Tagged from "./dyns.Tagged.ts"; import * as Tri from "./dyns.Tri.ts"; +import { tag$ids_Marked_u8, ids } from "./ids.ts"; +import * as Byte from "./ids.Byte.ts"; +import type * as Mood from "./ids.Mood.ts"; import { dynHash$stds_DynHash_ref_str, dynHash$stds_DynHash_u8, stds } from "./stds.ts"; import * as Counter from "./stds.Counter.ts"; import * as Fnv from "./stds.Fnv.ts"; @@ -332,6 +335,10 @@ export class of$Option_u8 { constructor(v: rs.u8 | undefined) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "3801bd64778907c7633cf473fe556a70"; + } } export class of$String { @@ -574,6 +581,10 @@ export class of$dyns_Noisy { tally$Shape(a: rs.Place): void { return Noisy.tally$dyns_Shape(this.v, a); } + + typeId$Any(): rs.TypeId { + return "8abb48f5c9ad521e17c8b05ce0f1d679"; + } } export class of$dyns_Skips { @@ -646,6 +657,10 @@ export class of$dyns_Square { tally$Shape(a: rs.Place): void { return Square.tally$dyns_Shape(this.v, a); } + + typeId$Any(): rs.TypeId { + return "0ba6291209e997830eaef6cade61afd2"; + } } export class of$dyns_Steps { @@ -734,6 +749,10 @@ export class of$dyns_Tri { tally$Shape(a: rs.Place): void { return Tri.tally$dyns_Shape(this.v, a); } + + typeId$Any(): rs.TypeId { + return "66772eb3554749a10355c03efd41f5ad"; + } } export class of$fmt_Formatter { @@ -828,6 +847,34 @@ export class of$hash_DefaultHasher { } } +export class of$ids_Byte { + v: Byte.Byte; + + constructor(v: Byte.Byte) { + this.v = v; + } + + tag$Marked(): rs.u8 { + return Byte.tag$ids_Marked(this.v); + } + + typeId$Any(): rs.TypeId { + return "8f5d7517f5a241dab4213252fd0d53e8"; + } +} + +export class of$ids_Mood { + v: Mood.Mood; + + constructor(v: Mood.Mood) { + this.v = v; + } + + typeId$Any(): rs.TypeId { + return "328e59b82c6cb0a6eb720af972265604"; + } +} + export class of$ref_str { v: string; @@ -838,6 +885,10 @@ export class of$ref_str { dynHash$DynHash(a: Hasher.Hasher): void { return dynHash$stds_DynHash_ref_str(this.v, a); } + + typeId$Any(): rs.TypeId { + return "24c161f26c70c1f1b9f602dc5fe9f65c"; + } } export class of$stds_Counter { @@ -1076,6 +1127,10 @@ export class of$tuple2_u8_u8 { constructor(v: [rs.u8, rs.u8]) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "7d311253455a5117c9570c9455e07dcc"; + } } export class of$u16 { @@ -1148,6 +1203,10 @@ export class of$u32 { tally$Shape(a: rs.Place): void { return tally$dyns_Shape_u32(this.v, a); } + + typeId$Any(): rs.TypeId { + return "79f19461b401588885a89e43144b08f1"; + } } export class of$u8 { @@ -1169,9 +1228,17 @@ export class of$u8 { return peek$dyns_Peek_unit_u8(this.v) as rs.OptionOf; } + tag$Marked(): rs.u8 { + return tag$ids_Marked_u8(this.v); + } + take$Source(): rs.OptionOf { return dyns_take$dyns_Source_u8(this) as rs.OptionOf; } + + typeId$Any(): rs.TypeId { + return "cfb1622e7734f8af272d8d182905dc25"; + } } export class of$unit { @@ -1180,6 +1247,10 @@ export class of$unit { constructor(v: undefined) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "05fc01408e45e7cddf9e041e3391eafc"; + } } export class ofPlace$Option_u8 { @@ -1188,6 +1259,10 @@ export class ofPlace$Option_u8 { constructor(v: rs.Place) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "3801bd64778907c7633cf473fe556a70"; + } } export class ofPlace$dyns_Held { @@ -1264,6 +1339,22 @@ export class ofPlace$dyns_Tri { tally$Shape(a: rs.Place): void { return Tri.tally$dyns_Shape(this.v.v, a); } + + typeId$Any(): rs.TypeId { + return "66772eb3554749a10355c03efd41f5ad"; + } +} + +export class ofPlace$ids_Mood { + v: rs.Place; + + constructor(v: rs.Place) { + this.v = v; + } + + typeId$Any(): rs.TypeId { + return "328e59b82c6cb0a6eb720af972265604"; + } } export class ofPlace$mut_String { @@ -1330,6 +1421,10 @@ export class ofPlace$u32 { tally$Shape(a: rs.Place): void { return tally$dyns_Shape_u32(this.v.v, a); } + + typeId$Any(): rs.TypeId { + return "79f19461b401588885a89e43144b08f1"; + } } export class ofPlace$u8 { @@ -1346,6 +1441,10 @@ export class ofPlace$u8 { take$Source(): rs.OptionOf { return dyns_take$dyns_Source_u8(this.v) as rs.OptionOf; } + + typeId$Any(): rs.TypeId { + return "cfb1622e7734f8af272d8d182905dc25"; + } } export class ofPlace$unit { @@ -1354,6 +1453,10 @@ export class ofPlace$unit { constructor(v: rs.Place) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "05fc01408e45e7cddf9e041e3391eafc"; + } } /** @@ -1385,6 +1488,8 @@ export function cases(): rs.Vec<[string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.By stds(data11, out11); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["dyns::options", (_221: rs.u8, data12: rs.ByteSlice, out12: rs.ByteVec): undefined => { options(data12, out12); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["ids::ids", (_222: rs.u8, data13: rs.ByteSlice, out13: rs.ByteVec): undefined => { + ids(data13.data, data13.start, data13.length, out13); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]]); } diff --git a/ts/fixtures/lang/dyns/src/ids.rs b/ts/fixtures/lang/dyns/src/ids.rs new file mode 100644 index 000000000..18ca24550 --- /dev/null +++ b/ts/fixtures/lang/dyns/src/ids.rs @@ -0,0 +1,313 @@ +//! `TypeId`s, of `TypeId::of`, of a value's `type_id`, and of a trait object's, which is +//! its value's type's: borrowed, changed in place, boxed, shared, and upcast from the +//! crate's own trait over `Any`, but for a box or an `Rc`'s own `type_id`, which is the +//! pointer's type's. Each is equal to another only for the same type, in a struct's +//! derives, a hash map, and a `BTreeSet` too. Its value is the crate's metadata's, so each +//! check compares within a run: `Debug` writes `TypeId(0x…)` of its 128-bit hash, `Ord` +//! orders by that hash's pointer-sized words, lowest first, and `Hash` writes one `u64`, +//! the hash's high half. + +use std::any::{Any, TypeId}; +use std::collections::{BTreeSet, HashMap, HashSet}; +use std::fmt::{self, Write}; +use std::hash::{Hash, Hasher}; +use std::rc::Rc; + +use crate::harness::put; + +/// A value of a type of its own, which only its `TypeId` tells from a `u8`. +#[derive(Debug, Clone, Copy)] +struct Byte(u8); + +/// An enum, which JS copies, so a `&mut dyn Any` of one is of a class of its place. +enum Mood { + Calm, + Loud, +} + +/// A trait over `Any`, whose objects give their value's `type_id`. +trait Marked: Any { + fn tag(&self) -> u8; +} + +impl Marked for u8 { + fn tag(&self) -> u8 { + *self + } +} + +impl Marked for Byte { + fn tag(&self) -> u8 { + self.0 + } +} + +/// A `TypeId` in a struct, whose derives compare, order, and hash it as std's. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct WithId { + id: TypeId, + n: u8, +} + +/// Logs each call it is given, and how many bytes `write` is. +#[derive(Default)] +struct Log(Vec); + +impl Hasher for Log { + fn write(&mut self, bytes: &[u8]) { + self.0.push(format!("write {}", bytes.len())); + } + fn write_u8(&mut self, x: u8) { + self.0.push(format!("u8 {x}")); + } + fn write_u64(&mut self, x: u64) { + self.0.push(format!("u64 {x:x}")); + } + fn finish(&self) -> u64 { + 0 + } +} + +/// Takes at most `cap` bytes, logging each `write_str` it takes, and fails the call that +/// would pass `cap`. +struct Capped { + log: Vec, + len: usize, + calls: usize, + cap: usize, +} + +impl Write for Capped { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.calls += 1; + if self.len + s.len() > self.cap { + return Err(fmt::Error); + } + self.len += s.len(); + self.log.push(s.to_string()); + Ok(()) + } +} + +/// `id`'s `Debug`, written into a `Capped` of `cap` bytes. +fn capped(id: TypeId, cap: usize) -> (fmt::Result, Capped) { + let mut w = Capped { + log: Vec::new(), + len: 0, + calls: 0, + cap, + }; + let result = write!(w, "{id:?}"); + (result, w) +} + +/// A `TypeId` the compiler evaluates. +const U8: TypeId = TypeId::of::(); + +/// A reference to one, which the compiler places in memory. +const U8_REF: &TypeId = &TypeId::of::(); + +/// `T`'s `TypeId`, through a type parameter. +fn id_of() -> TypeId { + TypeId::of::() +} + +/// The `TypeId` of what `x` is, through a type parameter that may be a trait object. +fn id_of_val(_: &T) -> TypeId { + TypeId::of::() +} + +/// The 128-bit hash `Debug` writes, if it writes `TypeId(0x…)` of 32 hex digits. +fn hash_of(id: TypeId) -> Option { + let s = format!("{id:?}"); + let digits = s.strip_prefix("TypeId(0x")?.strip_suffix(')')?; + match digits.len() == 32 && digits.bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()) { + true => u128::from_str_radix(digits, 16).ok(), + false => None, + } +} + +/// The pointer-sized words of `id`'s hash as it lays them out, lowest first, which its +/// derived `Ord` compares. +fn words(id: TypeId) -> Vec { + let hash = hash_of(id).unwrap_or_default(); + let bits = usize::BITS; + let mask = u128::MAX >> (128 - bits); + (0..128 / bits).map(|i| (hash >> (i * bits)) & mask).collect() +} + +pub fn ids(data: &[u8], out: &mut Vec) { + let [a, b, rest @ ..] = data else { + return; + }; + let (a, b) = (*a, *b); + + // A type's own, through a type parameter, of a value, and of a trait object of a value. + let mut n = u32::from(a); + let mut mood = if b % 2 == 0 { Mood::Calm } else { Mood::Loud }; + let byte = Byte(b); + let ids = [ + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::(), + TypeId::of::<&'static str>(), + TypeId::of::(), + TypeId::of::<()>(), + TypeId::of::>(), + TypeId::of::<[u8]>(), + TypeId::of::(), + TypeId::of::>(), + TypeId::of::(), + TypeId::of:: u8>(), + ]; + let found = [ + id_of::(), + U8, + *U8_REF, + const { TypeId::of::() }, + a.type_id(), + n.type_id(), + byte.type_id(), + id_of_val("str"), + id_of_val(&mood), + id_of_val::(&a), + id_of::>(), + ]; + for id in found { + put(out, &format!("{:?} ", ids.iter().position(|i| *i == id))); + } + put(out, &format!("{:?} ", ids.iter().position(|i| i == U8_REF))); + + // A trait object's is its value's, borrowed, changed in place, which JS copies for a + // number and an enum, boxed, shared, and upcast, but a box's and an `Rc`'s own are theirs. + let objects: [&dyn Any; 5] = [&a, &n, &byte, &mood, &"str"]; + for x in objects { + put(out, &format!("{:?} ", ids.iter().position(|i| *i == x.type_id()))); + } + let places: [&mut dyn Any; 2] = [&mut n, &mut mood]; + for x in places { + put(out, &format!("{:?} ", ids.iter().position(|i| *i == (*x).type_id()))); + } + let marked: [&dyn Marked; 2] = [&a, &byte]; + for t in marked { + let any: &dyn Any = t; + put( + out, + &format!( + "{} {:?} {} {} ", + t.tag(), + ids.iter().position(|i| *i == t.type_id()), + t.type_id() == any.type_id(), + id_of_val(t) == TypeId::of::(), + ), + ); + } + let mut boxes: Vec> = Vec::new(); + for &x in rest { + boxes.push(match x % 3 { + 0 => Box::new(x), + 1 => Box::new(Byte(x)), + _ => Box::new(Mood::Loud), + }); + } + for x in &boxes { + let inner = (**x).type_id(); + put( + out, + &format!( + "{:?} {} ", + ids.iter().position(|i| *i == inner), + x.type_id() == TypeId::of::>() + ), + ); + } + let shared: Rc = Rc::new(byte); + let boxed: Box = Box::new(a); + put( + out, + &format!( + "{} {} {} ", + (*shared).type_id() == TypeId::of::(), + shared.type_id() == TypeId::of::>(), + (*boxed).type_id() == TypeId::of::(), + ), + ); + + // Equal only for the same type, ordered by its hash's words lowest first, and hashed as + // one `u64`, its hash's high half, written alone. + let pairs = || { + ids.iter() + .enumerate() + .flat_map(|(i, &x)| ids.iter().enumerate().map(move |(j, &y)| (i, x, j, y))) + }; + put( + out, + &format!( + "{} {} {} {} ", + pairs().all(|(i, x, j, y)| (x == y) == (i == j) && (x != y) == (i != j)), + pairs().all(|(_, x, _, y)| x.cmp(&y) == words(x).cmp(&words(y))), + pairs().all(|(_, x, _, y)| x.partial_cmp(&y) == Some(words(x).cmp(&words(y)))), + pairs().all(|(_, x, _, y)| (x < y) == (words(x) < words(y)) && (x >= y) == (words(x) >= words(y))), + ), + ); + let (x, y) = (ids[usize::from(a) % ids.len()], ids[usize::from(b) % ids.len()]); + put(out, &format!("{} ", ids.iter().all(|&id| hash_of(id).is_some()))); + // `Debug` writes as std's `write!` does: `TypeId(`, `0x`, a `write_char` of each leading + // zero, the digits, and `)`, stopping at the first write the writer fails. + let (result, w) = capped(x, usize::from(a % 10)); + put(out, &format!("{result:?} {} {:?} ", w.calls, w.log)); + let debug = format!("{x:?}"); + let zeros = debug[9..].bytes().take_while(|&b| b == b'0').count().min(31); + let (result, w) = capped(x, usize::MAX); + let (digits, n) = (&w.log[2..w.log.len() - 1], w.log.len()); + put( + out, + &format!( + "{result:?} {:?} {:?} {} {} ", + &w.log[..2], + w.log.last(), + n == 4 + zeros && digits[..zeros].iter().all(|d| d == "0"), + digits.concat() == debug[9..41], + ), + ); + let (result, w) = capped(x, debug.len() - 1); + put(out, &format!("{result:?} {} ", w.log.concat() + ")" == debug)); + let mut log = Log::default(); + x.hash(&mut log); + let high = hash_of(x).map(|h| format!("u64 {:x}", h >> 64)); + put(out, &format!("{} {} ", log.0.len(), log.0.first() == high.as_ref())); + put( + out, + &format!("{} ", x.max(y) == if words(x) < words(y) { y } else { x }), + ); + + // Sorted, in sets and maps, and in a struct's derives. + let mut sorted = ids.to_vec(); + sorted.sort(); + let ordered = sorted.windows(2).all(|w| words(w[0]) < words(w[1])); + let set: BTreeSet = ids.iter().chain(&found).copied().collect(); + let hashed: HashSet = ids.iter().chain(&found).copied().collect(); + put(out, &format!("{ordered} {} {} ", set.len(), hashed.len())); + let names: HashMap = [ + (TypeId::of::(), "u8"), + (TypeId::of::(), "byte"), + (TypeId::of::(), "mood"), + ] + .into_iter() + .collect(); + for x in &boxes { + put(out, &format!("{:?} ", names.get(&(**x).type_id()))); + } + let keys = [WithId { id: x, n: a }, WithId { id: y, n: b }, WithId { id: x, n: b }]; + let by_id = |k: &WithId, l: &WithId| words(k.id).cmp(&words(l.id)).then(k.n.cmp(&l.n)); + let order: Vec<(bool, bool)> = keys + .iter() + .flat_map(|k| keys.iter().map(move |l| (k, l))) + .map(|(k, l)| (k == l, k.cmp(l) == by_id(k, l))) + .collect(); + put(out, &format!("{order:?} ")); + let mut log = Log::default(); + keys[0].hash(&mut log); + put(out, &format!("{} {:?}", log.0.len(), log.0.get(1))); +} diff --git a/ts/fixtures/lang/dyns/src/lib.rs b/ts/fixtures/lang/dyns/src/lib.rs index 6a427c08e..b924de979 100644 --- a/ts/fixtures/lang/dyns/src/lib.rs +++ b/ts/fixtures/lang/dyns/src/lib.rs @@ -4,6 +4,7 @@ mod dyns; #[path = "../../harness.rs"] pub mod harness; +mod ids; mod stds; #[cfg(target_arch = "wasm32")] #[path = "../../../wasm.rs"] @@ -29,5 +30,6 @@ fn cases() -> Vec<(&'static str, Case)> { ("dyns::generics", |_, data, out| dyns::generics(data, out)), ("stds::stds", |_, data, out| stds::stds(data, out)), ("dyns::options", |_, data, out| dyns::options(data, out)), + ("ids::ids", |_, data, out| ids::ids(data, out)), ] } diff --git a/ts/fixtures/lang/panics/hook/expected/core/any.Any.ts b/ts/fixtures/lang/panics/hook/expected/core/any.Any.ts index ba72bb625..fd88dfe56 100644 --- a/ts/fixtures/lang/panics/hook/expected/core/any.Any.ts +++ b/ts/fixtures/lang/panics/hook/expected/core/any.Any.ts @@ -1,5 +1,7 @@ // Generated by rust2-ts. Do not edit. -export type Any = object; +import * as rs from "../rs.ts"; + +export type Any = { typeId$Any: () => rs.TypeId }; //# sourceMappingURL=any.Any.ts.map diff --git a/ts/fixtures/lang/panics/hook/expected/hooked.ts b/ts/fixtures/lang/panics/hook/expected/hooked.ts index 7e835f1da..d7c1e0dd3 100644 --- a/ts/fixtures/lang/panics/hook/expected/hooked.ts +++ b/ts/fixtures/lang/panics/hook/expected/hooked.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { drop$Result_sync_MutexGuard_u8_sync_PoisonError_sync_MutexGuard_u8, drop$bytes_buf_Take_bytes_Bytes, of$String, of$ref_str, of$u32, of$u8, ofPlace$ref_str, ofPlace$u32, ofPlace$u8, NOTE } from "./lib.ts"; +import { drop$Result_sync_MutexGuard_u8_sync_PoisonError_sync_MutexGuard_u8, drop$bytes_buf_Take_bytes_Bytes, of$String, of$ref_str, of$u32, of$u8, ofPlace$u32, ofPlace$u8, NOTE } from "./lib.ts"; import { take$bytes_Buf_bytes_Bytes as buf_impl_take$bytes_Buf_bytes_Bytes } from "./bytes/buf/buf_impl.ts"; import * as Take from "./bytes/buf/take.Take.ts"; import * as UninitSlice from "./bytes/buf/uninit_slice.UninitSlice.ts"; @@ -20,15 +20,15 @@ import { withBorrow$097wfl1 } from "./std/thread/local.ts"; export function record(info: rs.PanicHookInfo): void { let kind!: rs.String; const payload: Any.Any = rs.hookPayload(info, of$ref_str, of$String); - if (!(payload instanceof of$ref_str || payload instanceof ofPlace$ref_str)) { + if (!(payload.typeId$Any() === "24c161f26c70c1f1b9f602dc5fe9f65c")) { if (!(payload instanceof of$String)) { - const _8: rs.u32 | undefined = payload instanceof of$u32 ? payload.v : payload instanceof ofPlace$u32 ? payload.v.v : undefined; - if (_8 !== undefined) { - kind = rs.fmtFormat(["u32 ", (f: rs.Formatter) => rs.u32Display(_8, f)]); + const _12: rs.u32 | undefined = payload instanceof of$u32 ? payload.v : payload instanceof ofPlace$u32 ? payload.v.v : undefined; + if (_12 !== undefined) { + kind = rs.fmtFormat(["u32 ", (f: rs.Formatter) => rs.u32Display(_12, f)]); } else { - const _19: rs.u8 | undefined = payload instanceof of$u8 ? payload.v : payload instanceof ofPlace$u8 ? payload.v.v : undefined; - if (_19 !== undefined) { - kind = rs.fmtFormat(["u8 ", (f: rs.Formatter) => rs.u8Display(_19, f)]); + const _23: rs.u8 | undefined = payload instanceof of$u8 ? payload.v : payload instanceof ofPlace$u8 ? payload.v.v : undefined; + if (_23 !== undefined) { + kind = rs.fmtFormat(["u8 ", (f: rs.Formatter) => rs.u8Display(_23, f)]); } else { kind = rs.stringFrom("other"); } @@ -40,13 +40,13 @@ export function record(info: rs.PanicHookInfo): void { kind = rs.stringFrom("&str"); } const at: rs.Location = rs.optionUnwrap(info.location, undefined, { file: "fixtures/lang/panics/src/hooked.rs", line: 42, col: 30 }); - const _38: rs.String = rs.strReplace(rs.fmtToString((f: rs.Formatter) => rs.fmtPanicHookInfoDisplay(info, f)).v, at.file, stdNamed(at.file).v, true); - const _48: string | undefined = info.text; - const _50: rs.String = stdNamed(at.file); - const _53: rs.u32 = at.line; - const _55: rs.u32 = at.col; - const _57: boolean = rs.panicking(); - const args9 = [(f: rs.Formatter) => rs.fmtPad(f, _38.v), (f: rs.Formatter) => rs.fmtPad(f, kind.v), (f: rs.Formatter) => rs.fmtOption(_48, f, rs.fmtStr), (f: rs.Formatter) => rs.fmtPad(f, _50.v), (f: rs.Formatter) => rs.u32Display(_53, f), (f: rs.Formatter) => rs.u32Display(_55, f), (f: rs.Formatter) => rs.fmtBool(_57, f)]; + const _42: rs.String = rs.strReplace(rs.fmtToString((f: rs.Formatter) => rs.fmtPanicHookInfoDisplay(info, f)).v, at.file, stdNamed(at.file).v, true); + const _52: string | undefined = info.text; + const _54: rs.String = stdNamed(at.file); + const _57: rs.u32 = at.line; + const _59: rs.u32 = at.col; + const _61: boolean = rs.panicking(); + const args9 = [(f: rs.Formatter) => rs.fmtPad(f, _42.v), (f: rs.Formatter) => rs.fmtPad(f, kind.v), (f: rs.Formatter) => rs.fmtOption(_52, f, rs.fmtStr), (f: rs.Formatter) => rs.fmtPad(f, _54.v), (f: rs.Formatter) => rs.u32Display(_57, f), (f: rs.Formatter) => rs.u32Display(_59, f), (f: rs.Formatter) => rs.fmtBool(_61, f)]; const line: rs.String = rs.fmtFormat([args9[0], "\n", args9[1], " ", args9[2], " ", args9[3], " ", args9[4], " ", args9[5], " ", args9[6], "\n"]); rs.localKeyWithBorrowMut(NOTE, (note: rs.String): undefined => { rs.stringPushStr(note, line.v); diff --git a/ts/fixtures/lang/panics/hook/expected/lib.ts b/ts/fixtures/lang/panics/hook/expected/lib.ts index 863f070fe..b72d52964 100644 --- a/ts/fixtures/lang/panics/hook/expected/lib.ts +++ b/ts/fixtures/lang/panics/hook/expected/lib.ts @@ -39,6 +39,10 @@ export class of$String { constructor(v: rs.String) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "1430db79e63c4bb92bf94c26df7c977d"; + } } export class of$located_Inherits { @@ -75,6 +79,10 @@ export class of$panics_Point { constructor(v: Point.Point) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "8e48006f0a085ab0ca68bcd6079f396f"; + } } export class of$ref_str { @@ -83,6 +91,10 @@ export class of$ref_str { constructor(v: string) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "24c161f26c70c1f1b9f602dc5fe9f65c"; + } } export class of$u32 { @@ -91,6 +103,10 @@ export class of$u32 { constructor(v: rs.u32) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "79f19461b401588885a89e43144b08f1"; + } } export class of$u8 { @@ -104,16 +120,12 @@ export class of$u8 { return at$located_Tracked_u8(this.v, caller); } - via$Tracked(): rs.Location { - return via$located_Tracked_u8(this.v); + typeId$Any(): rs.TypeId { + return "cfb1622e7734f8af272d8d182905dc25"; } -} - -export class ofPlace$ref_str { - v: rs.Place; - constructor(v: rs.Place) { - this.v = v; + via$Tracked(): rs.Location { + return via$located_Tracked_u8(this.v); } } @@ -123,6 +135,10 @@ export class ofPlace$u32 { constructor(v: rs.Place) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "79f19461b401588885a89e43144b08f1"; + } } export class ofPlace$u8 { @@ -131,6 +147,10 @@ export class ofPlace$u8 { constructor(v: rs.Place) { this.v = v; } + + typeId$Any(): rs.TypeId { + return "cfb1622e7734f8af272d8d182905dc25"; + } } /** diff --git a/ts/fixtures/lang/panics/src/hooked.rs b/ts/fixtures/lang/panics/src/hooked.rs index 92adaba61..1624db508 100644 --- a/ts/fixtures/lang/panics/src/hooked.rs +++ b/ts/fixtures/lang/panics/src/hooked.rs @@ -28,7 +28,7 @@ use crate::NOTE; /// parts, and whether the thread is panicking. fn record(info: &PanicHookInfo<'_>) { let payload = info.payload(); - let kind = if payload.is::<&'static str>() { + let kind = if payload.type_id() == std::any::TypeId::of::<&'static str>() { "&str".to_string() } else if payload.is::() { "String".to_string() diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index c528f2e62..99c3e8ca2 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -90,9 +90,6 @@ fixtures/reject/src/keys.rs:place_bounds+1:9: unsupported call to `std::ops::Ran fixtures/reject/src/keys.rs:places_sorted+1:15: unsupported call to ` as std::iter::Iterator>::is_sorted` fixtures/reject/src/keys.rs:set_keys+1:75: comparing `std::collections::BTreeSet` as a key, which is not supported yet fixtures/reject/src/keys.rs:set_keys+1:75: hashing `std::collections::BTreeSet` as a key, which is not supported yet -fixtures/reject/src/objects.rs:any_type_id+1:2: unsupported call to `::eq` -fixtures/reject/src/objects.rs:any_type_id+1:4: `std::any::Any::type_id` of `dyn std::any::Any`, which is not supported yet -fixtures/reject/src/objects.rs:any_type_id+1:17: unsupported call to `std::any::TypeId::of::` fixtures/reject/src/objects.rs:array_object+1:2: a trait object of `[u16; 2]`, which is not supported yet fixtures/reject/src/objects.rs:dyn_compared+3:2: comparing pointers to `dyn objects::Reading`, each an object JS makes of its own as it unsizes fixtures/reject/src/objects.rs:dyn_size+1:2: unsupported call to `std::mem::size_of_val::` diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index d3a5d8ec1..ec2dc6cd3 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -92,9 +92,6 @@ fixtures/reject/src/keys.rs:place_bounds+1:9: unsupported call to `std::ops::Ran fixtures/reject/src/keys.rs:places_sorted+1:15: unsupported call to ` as std::iter::Iterator>::is_sorted` fixtures/reject/src/keys.rs:set_keys+1:75: comparing `std::collections::BTreeSet` as a key, which is not supported yet fixtures/reject/src/keys.rs:set_keys+1:75: hashing `std::collections::BTreeSet` as a key, which is not supported yet -fixtures/reject/src/objects.rs:any_type_id+1:2: unsupported call to `::eq` -fixtures/reject/src/objects.rs:any_type_id+1:4: `std::any::Any::type_id` of `dyn std::any::Any`, which is not supported yet -fixtures/reject/src/objects.rs:any_type_id+1:17: unsupported call to `std::any::TypeId::of::` fixtures/reject/src/objects.rs:array_object+1:2: a trait object of `[u16; 2]`, which is not supported yet fixtures/reject/src/objects.rs:dyn_compared+3:2: comparing pointers to `dyn objects::Reading`, each an object JS makes of its own as it unsizes fixtures/reject/src/objects.rs:dyn_size+1:2: unsupported call to `std::mem::size_of_val::` diff --git a/ts/fixtures/reject/src/objects.rs b/ts/fixtures/reject/src/objects.rs index b848f63f5..8dc1d3577 100644 --- a/ts/fixtures/reject/src/objects.rs +++ b/ts/fixtures/reject/src/objects.rs @@ -109,11 +109,6 @@ pub fn same_named(x: &u8) -> u8 { a.name().wrapping_add(b.name()) } -/// A `TypeId`, which no value stands for yet. -pub fn any_type_id(a: &dyn std::any::Any) -> bool { - a.type_id() == std::any::TypeId::of::() -} - pub trait Source { type Item: Reading; fn item(&self) -> &Self::Item; diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index 99304f296..d299da1a1 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -76,7 +76,6 @@ roots = [ "keys::set_keys", "lopsided", "missing", - "objects::any_type_id", "objects::array_object", "objects::dyn_compared", "objects::dyn_size", diff --git a/ts/runtime/rs.ts b/ts/runtime/rs.ts index f43eeecb1..8daf39233 100644 --- a/ts/runtime/rs.ts +++ b/ts/runtime/rs.ts @@ -18,6 +18,7 @@ import { AtIndex } from "./rs/collections.ts"; import { escapeDebug } from "./rs/char.ts"; import { byteSliceUtf8Chunks, String as RsString, strFromUtf8Unchecked, stringClone, stringCloneFrom, stringFrom, stringLen, stringNew, stringOf, stringPush, stringPushStr, stringWithCapacity } from "./rs/str.ts"; export * from "./rs/alloc.ts"; +export * from "./rs/any.ts"; export * from "./rs/ascii.ts"; export * from "./rs/bytes.ts"; export * from "./rs/char.ts"; diff --git a/ts/runtime/rs/any.ts b/ts/runtime/rs/any.ts new file mode 100644 index 000000000..13be31976 --- /dev/null +++ b/ts/runtime/rs/any.ts @@ -0,0 +1,41 @@ +// `core::any::TypeId`, for rust2-ts output. `rs.ts` re-exports all of it. + +import { type FmtResult, type Formatter, fmtPadIntegral, fmtSpec, fmtWriteFmt, type Spec } from "../rs.ts"; +import { cmpStr, type Ordering } from "./collections.ts"; +import { type Hasher, hash64 } from "./hash.ts"; +import { u64FromHalves } from "./int.ts"; + +/** + * `TypeId`: rustc's 128-bit hash of the type, which std's holds at run time, as 32 + * lowercase hex digits, so two are equal by `===`. + */ +export type TypeId = string; + +/** + * `Ord::cmp`, derived over the pointer-sized words std lays the hash out in, lowest first, + * as wasm32's are: each is 8 hex digits, which compare as their numbers do. + */ +export function typeIdCmp(a: TypeId, b: TypeId): Ordering { + for (let end = 32; end > 0; end -= 8) { + const order = cmpStr(a.slice(end - 8, end), b.slice(end - 8, end)); + if (order !== 0) return order; + } + return 0; +} + +/** `Hash::hash`: the hash's high 64 bits, the second half of its bytes, as one `u64`. */ +export function typeIdHash(id: TypeId, s: Hasher): void { + hash64(u64FromHalves(parseInt(id.slice(0, 8), 16), parseInt(id.slice(8, 16), 16)), s); +} + +/** + * `Debug`, as std's `write!(f, "TypeId({:#034x})", ..)`: `TypeId(`, `0x`, a zero for each + * leading zero digit, the rest, and `)`, each its own write. + */ +export function fmtTypeIdDebug(id: TypeId, f: Formatter): FmtResult { + const digits = id.replace(/^0+(?=.)/, ""); + return fmtWriteFmt(f, ["TypeId(", fmtSpec(HASH, (g) => fmtPadIntegral(g, true, "0x", digits)), ")"]); +} + +/** `{:#034x}`. */ +const HASH: Spec = { alternate: true, zero: true, width: 34 }; diff --git a/ts/runtime/rs/hook.ts b/ts/runtime/rs/hook.ts index f197c1dd2..b6b324f6a 100644 --- a/ts/runtime/rs/hook.ts +++ b/ts/runtime/rs/hook.ts @@ -107,8 +107,9 @@ export function panicAny(payload: object, at?: Location): never { * `PanicHookInfo::payload`: what `panic_any` was given, or else the text, as a `str` or a * `String` object, whose classes translation makes. */ -export function hookPayload(info: PanicHookInfo, str: new (text: string) => object, string: new (text: RsString) => object): object { - if (info.payload !== undefined) return info.payload; +export function hookPayload(info: PanicHookInfo, str: new (text: string) => S, string: new (text: RsString) => O): S | O { + // `panic_any` made its payload an object of the same `dyn Any` the classes are. + if (info.payload !== undefined) return info.payload as S | O; return info.string !== undefined ? new string(info.string) : new str(info.text ?? ""); } diff --git a/ts/src/lower/collections.rs b/ts/src/lower/collections.rs index ff5323bad..197a0e188 100644 --- a/ts/src/lower/collections.rs +++ b/ts/src/lower/collections.rs @@ -2812,7 +2812,7 @@ fn std_key<'tcx>(b: &Body<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool { || library::owned_string(b.t.tcx, ty) || matches!( b.t.std_of(ty), - Some(Std::Ordering | Std::Duration | Std::Location | Std::TryReserveError) + Some(Std::Ordering | Std::Duration | Std::TypeId | Std::Location | Std::TryReserveError) ) } _ => false, @@ -3060,6 +3060,7 @@ pub fn cmp_fn<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, partial: bool, spa ty::Adt(..) if library::string(tcx, ty) || library::owned_string(tcx, ty) => Expr::Rs("cmpStr".into()), ty::Adt(..) if b.t.std_of(ty) == Some(Std::Ordering) => Expr::Rs("cmp".into()), ty::Adt(..) if b.t.std_of(ty) == Some(Std::Duration) => Expr::Rs("durationCmp".into()), + ty::Adt(..) if b.t.std_of(ty) == Some(Std::TypeId) => Expr::Rs("typeIdCmp".into()), ty::Adt(..) if b.t.std_of(ty) == Some(Std::Location) => Expr::Rs("locationCmp".into()), _ if library::nothing(tcx, ty) => Expr::Rs("cmpNothing".into()), ty::Tuple(tys) => { @@ -3135,7 +3136,10 @@ pub fn eq_fn<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, span: Span) -> Expr } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => Expr::Rs("same".into()), _ if library::nothing(tcx, ty) => Expr::Rs("same".into()), - ty::Adt(..) if library::string(tcx, ty) || matches!(b.t.std_of(ty), Some(Std::Ordering | Std::ErrorKind)) => { + ty::Adt(..) + if library::string(tcx, ty) + || matches!(b.t.std_of(ty), Some(Std::Ordering | Std::ErrorKind | Std::TypeId)) => + { Expr::Rs("same".into()) } ty::Adt(..) if library::owned_string(tcx, ty) => Expr::Rs("eqStr".into()), @@ -3375,6 +3379,7 @@ fn hash_fn<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, hasher: Ty<'tcx>, spa // Its derived `Hash` writes its `i8` discriminant. ty::Adt(..) if b.t.std_of(ty) == Some(Std::Ordering) => Expr::Rs("hashI8".into()), ty::Adt(..) if b.t.std_of(ty) == Some(Std::Duration) => Expr::Rs("durationHash".into()), + ty::Adt(..) if b.t.std_of(ty) == Some(Std::TypeId) => Expr::Rs("typeIdHash".into()), ty::Adt(..) if b.t.std_of(ty) == Some(Std::Location) => Expr::Rs("locationHash".into()), _ if library::nothing(tcx, ty) => Expr::Rs("hashNothing".into()), ty::Tuple(tys) => { diff --git a/ts/src/lower/dynamic.rs b/ts/src/lower/dynamic.rs index 6944579eb..4487d5569 100644 --- a/ts/src/lower/dynamic.rs +++ b/ts/src/lower/dynamic.rs @@ -11,8 +11,8 @@ //! struct whose last field unsizes to one, such as a `RefCell`, is an //! `rs.tailDyn` view of the struct it was made from, whose last field is that field's //! object. A raw pointer unsizes as a `&mut` to what it points at does. A -//! `dyn Any` has no methods, and a downcast, of it or of a `dyn Error`, checks for the -//! class of the type it names. A `dyn Fn`, +//! `dyn Any`'s one method is `type_id`, a constant of its class's type's `TypeId`, and a +//! downcast, of it or of a `dyn Error`, checks for the class of the type it names. A `dyn Fn`, //! `dyn FnMut`, or `dyn FnOnce` is the function it was made of, which a call calls as it //! would the closure itself. @@ -117,6 +117,15 @@ impl<'tcx> Translator<'_, 'tcx> { }) } + /// `ty`'s `TypeId`: rustc's hash of it, as 32 hex digits, or `undefined` in a census over + /// type parameters, where only an instance decides what `ty` is. + pub fn type_id(&self, ty: Ty<'tcx>) -> Expr { + match self.polymorphic() && ty.has_param() { + true => Expr::undefined(), + false => Expr::Str(format!("{:032x}", self.tcx.type_id_hash(ty).as_u128())), + } + } + /// The function trait that calls the closure, async closure, or `dyn Fn*` that `ty` is, /// or boxes. pub fn owned_fn(&self, ty: Ty<'tcx>) -> Option { @@ -223,8 +232,7 @@ impl<'tcx> Translator<'_, 'tcx> { alias: None, }); let mut methods = Vec::new(); - // `Any`'s object is its class, which a downcast checks for, so it needs no method. - let traits = tcx.supertrait_def_ids(principal).filter(|&tr| !is_any(tcx, tr)); + let traits = tcx.supertrait_def_ids(principal); for trait_ref in traits.flat_map(|tr| refs.iter().filter(move |r| r.def_id == tr)) { let tr = trait_ref.def_id; for &def_id in tcx.own_existential_vtable_entries(tr) { @@ -266,7 +274,8 @@ impl<'tcx> Translator<'_, 'tcx> { self.program.items.push(Item::Union { sym, params: names, - // Any object, as a downcast checks for the class of the value's own type. + // Any object, as a downcast checks for the class of the value's own type, until it + // holds `type_id`. ty: match is_any(tcx, principal) { true => Type::Builtin("object"), false => Type::Object(Vec::new()), @@ -384,11 +393,13 @@ impl<'tcx> Translator<'_, 'tcx> { .filter_map(|m| Some((m.name.clone(), m.ty.clone()?))) .collect(); let classes = object.classes.clone(); + // `dyn Any`'s, which is any object until it holds `type_id`. if let Item::Union { - ty: Type::Object(held), .. + ty: ty @ (Type::Object(_) | Type::Builtin("object")), + .. } = &mut self.program.items[object.item] { - *held = members; + *ty = Type::Object(members); } for (from, held, object) in classes { self.class_method(principal, index, from, held, object, span); @@ -878,14 +889,6 @@ impl<'tcx> Body<'_, '_, 'tcx> { let principal = principal(object).expect("an object's trait"); let methods = &self.t.objects[&principal].methods; let Some(index) = methods.iter().position(|m| m.def_id == def_id) else { - let tcx = self.t.tcx; - if tcx.trait_of_assoc(def_id).is_some_and(|tr| is_any(tcx, tr)) { - let msg = format!( - "`{}` of `{object}`, which is not supported yet", - tcx.def_path_str(def_id) - ); - return self.err(span, msg); - } return self.err( span, format!("`{}` is not a method of `{object}`", self.t.tcx.def_path_str(def_id)), @@ -1198,6 +1201,10 @@ pub fn call<'tcx>( out: &mut Vec, ) -> Result, Vec> { let tcx = b.t.tcx; + // `TypeId::of`, and `Any::type_id` of a type's own impl, which a trait object's class calls. + if let ("TypeId", "of") | ("Any", "type_id") = (owner, name) { + return Ok(Some(b.t.type_id(call.args.type_at(0)))); + } if !matches!( (owner, name), ("dyn Any" | "dyn Error", "is" | "downcast_ref" | "downcast_mut") diff --git a/ts/src/lower/fmt.rs b/ts/src/lower/fmt.rs index e381a0a8c..df09ca554 100644 --- a/ts/src/lower/fmt.rs +++ b/ts/src/lower/fmt.rs @@ -663,6 +663,7 @@ pub fn std_impl<'tcx>( (Std::Utf8Error, Trait::Display) => rs("utf8ErrorDisplay", vec![value, f]), (Std::Utf8Chunk, Trait::Debug) => rs("utf8ChunkDebug", vec![value, f]), (Std::Duration, Trait::Debug) => Expr::rs("durationDebug", vec![value, f]), + (Std::TypeId, Trait::Debug) => rs("typeIdDebug", vec![value, f]), (Std::Layout, Trait::Debug) => rs("layoutDebug", vec![value, f]), (Std::Location, Trait::Debug) => rs("locationDebug", vec![value, f]), (Std::Location, Trait::Display) => rs("locationDisplay", vec![value, f]), diff --git a/ts/src/lower/library.rs b/ts/src/lower/library.rs index ec302b1d9..044aa5b15 100644 --- a/ts/src/lower/library.rs +++ b/ts/src/lower/library.rs @@ -190,6 +190,9 @@ pub enum Std { BuildHasherDefault, /// `rs.Duration`: its seconds and nanoseconds, never changed once made. Duration, + /// `rs.TypeId`: rustc's 128-bit hash of the type, which std's `TypeId` holds at run time, + /// as 32 hex digits. + TypeId, /// `rs.Layout`: its size and alignment, never changed once made. Layout, /// `rs.Location`: a panic's file, line, and column, never changed once made. @@ -297,6 +300,7 @@ impl Std { } } for (item, std) in [ + (rustc_attr_ir::LangItem::TypeId, Self::TypeId), (rustc_attr_ir::LangItem::UnsafeCell, Self::Cell), (rustc_attr_ir::LangItem::Context, Self::Context), (rustc_attr_ir::LangItem::ResumeTy, Self::Context), diff --git a/ts/src/lower/mir/place.rs b/ts/src/lower/mir/place.rs index f0b7e266b..7de8a4a69 100644 --- a/ts/src/lower/mir/place.rs +++ b/ts/src/lower/mir/place.rs @@ -1581,6 +1581,12 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { if let Some(value) = self.cell_value(val, ty, span) { return value; } + if self.b.t.std_of(ty) == Some(Std::TypeId) { + return match self.type_id_const(val, ty) { + Some(of) => self.b.t.type_id(of), + None => self.err(span, format!("a constant of type `{ty}` that names no type")), + }; + } // An aggregate, field by field. if let ty::Adt(..) | ty::Tuple(_) | ty::Array(..) = ty.kind() && !matches!(ty.kind(), ty::Tuple(tys) if tys.is_empty()) @@ -1664,6 +1670,14 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { ); }; let unnamed = format!("a constant of type `{ty}`, a pointer into memory translation can't name"); + // A `&TypeId`, or a reference to one, which translation holds as the `TypeId`. + if self.b.t.std_of(ty.peel_refs()) == Some(Std::TypeId) { + let val = ConstValue::Indirect { + alloc_id: prov.alloc_id(), + offset, + }; + return self.const_value(val, *inner, span); + } // A `Location`, as `Location::caller` gives a constant. if self.b.t.std_of(*inner) == Some(Std::Location) { return match self.location_const(alloc, offset, *inner) { @@ -1704,6 +1718,22 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } } + /// The type the constant `TypeId` `val` is of, which the allocation its first word points + /// into names, as const evaluation writes it. + fn type_id_const(&self, val: ConstValue, ty: Ty<'tcx>) -> Option> { + let tcx = self.tcx; + let id = tcx.try_destructure_mir_constant_for_user_output(val, ty)?; + let &(words, words_ty) = id.fields.first()?; + let words = tcx.try_destructure_mir_constant_for_user_output(words, words_ty)?; + let &(ConstValue::Scalar(Scalar::Ptr(ptr, _)), _) = words.fields.first()? else { + return None; + }; + match tcx.global_alloc(ptr.provenance.alloc_id()) { + GlobalAlloc::TypeId { ty } => Some(ty), + _ => None, + } + } + /// The `Location` of type `ty` at `at` in `alloc`: its file, the `str` another /// allocation holds, and its line and column. fn location_const(&self, alloc: ConstAllocation<'tcx>, at: Size, ty: Ty<'tcx>) -> Option { diff --git a/ts/src/lower/mod.rs b/ts/src/lower/mod.rs index a12bf1804..693817dff 100644 --- a/ts/src/lower/mod.rs +++ b/ts/src/lower/mod.rs @@ -1836,6 +1836,7 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { library::Std::Hasher => Type::Rs("Hasher", vec![]), library::Std::RandomState => Type::Rs("RandomState", vec![]), library::Std::Duration => Type::Rs("Duration", vec![]), + library::Std::TypeId => Type::Rs("TypeId", vec![]), library::Std::Layout => Type::Rs("Layout", vec![]), library::Std::Location => Type::Rs("Location", vec![]), library::Std::PanicHookInfo => Type::Rs("PanicHookInfo", vec![]), diff --git a/ts/src/program.rs b/ts/src/program.rs index 202face44..e7d7baf65 100644 --- a/ts/src/program.rs +++ b/ts/src/program.rs @@ -16,6 +16,7 @@ use crate::ts::{self, FieldId, Function, Item, SymId}; pub const RUNTIME: &[(&str, &str)] = &[ ("rs.ts", include_str!("../runtime/rs.ts")), ("rs/alloc.ts", include_str!("../runtime/rs/alloc.ts")), + ("rs/any.ts", include_str!("../runtime/rs/any.ts")), ("rs/ascii.ts", include_str!("../runtime/rs/ascii.ts")), ("rs/bytes.ts", include_str!("../runtime/rs/bytes.ts")), ("rs/char.ts", include_str!("../runtime/rs/char.ts")),