From 9848dc208d0d87268e0d36e66f070d129993ea99 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 14:39:09 -0800 Subject: [PATCH 1/6] chore(quest): claim quest/m0/checks/stable-names.md Quest-Claim: 910a69f3f4a3c085 From ff778d64bf2630ce84519abce23b034aec3f0b7f Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:30:29 -0800 Subject: [PATCH 2/6] chore(ts): check stable names on a small fixture of their own stable_names translated all of lang a second time. The names fixture keeps each case lang's fmt::added reached: another instance of a generic method, a closure, an impl of another trait, a merged std function, and an impl over a deeper &mut, each sorting first, beside a trait object's module named as an imported type's. lang drops fmt::added and refs::deeper, and translates on its own again. Co-Authored-By: Claude Opus 5.5 --- quest/m0/checks/README.md | 1 - quest/m0/checks/stable-names.md | 15 -------- ts/Cargo.lock | 4 ++ ts/README.md | 5 +++ ts/fixtures/lang/src/fmt.rs | 18 +-------- ts/fixtures/lang/src/refs.rs | 7 ---- ts/fixtures/names/Cargo.toml | 10 +++++ ts/fixtures/names/expected/core/option.ts | 19 ++++++++++ ts/fixtures/names/expected/lib.ts | 38 +++++++++++++++++++ ts/fixtures/names/expected/shapes.Shape.ts | 15 ++++++++ ts/fixtures/names/expected/tally.ts | 38 +++++++++++++++++++ ts/fixtures/names/expected/wrap.Pair.ts | 19 ++++++++++ ts/fixtures/names/expected/wrap.Wrap.ts | 37 ++++++++++++++++++ ts/fixtures/names/src/dyns.rs | 10 +++++ ts/fixtures/names/src/lib.rs | 37 ++++++++++++++++++ ts/fixtures/names/src/shapes.rs | 14 +++++++ ts/fixtures/names/src/tally.rs | 44 ++++++++++++++++++++++ ts/fixtures/names/src/wrap.rs | 35 +++++++++++++++++ ts/fixtures/names/ts.toml | 2 + ts/tests/fixtures.rs | 22 ++++------- 20 files changed, 336 insertions(+), 54 deletions(-) delete mode 100644 quest/m0/checks/stable-names.md create mode 100644 ts/fixtures/names/Cargo.toml create mode 100644 ts/fixtures/names/expected/core/option.ts create mode 100644 ts/fixtures/names/expected/lib.ts create mode 100644 ts/fixtures/names/expected/shapes.Shape.ts create mode 100644 ts/fixtures/names/expected/tally.ts create mode 100644 ts/fixtures/names/expected/wrap.Pair.ts create mode 100644 ts/fixtures/names/expected/wrap.Wrap.ts create mode 100644 ts/fixtures/names/src/dyns.rs create mode 100644 ts/fixtures/names/src/lib.rs create mode 100644 ts/fixtures/names/src/shapes.rs create mode 100644 ts/fixtures/names/src/tally.rs create mode 100644 ts/fixtures/names/src/wrap.rs create mode 100644 ts/fixtures/names/ts.toml diff --git a/quest/m0/checks/README.md b/quest/m0/checks/README.md index e00ed00e9..1989eb940 100644 --- a/quest/m0/checks/README.md +++ b/quest/m0/checks/README.md @@ -19,6 +19,5 @@ Each child reports its CPU saved, cold and warm, against `origin/main`. ## Required - [A warm gate](/quest/m0/checks/cache.md) - the land gate builds through mbx, as moq's does -- [Stable names on a small fixture](/quest/m0/checks/stable-names.md) - `stable_names` runs on a small fixture with generic instances and trait impls - [Fixtures that merge](/quest/m0/checks/fixture-layout.md) - `lang` splits by module, and `reject`'s snapshots key by item, not line - [The stock fixture's target dir race](/quest/m0/checks/stock-cachedir.md) - the `stock` fixture never fails creating its target dir diff --git a/quest/m0/checks/stable-names.md b/quest/m0/checks/stable-names.md deleted file mode 100644 index 3eb63a0da..000000000 --- a/quest/m0/checks/stable-names.md +++ /dev/null @@ -1,15 +0,0 @@ -# [S] Stable names on a small fixture - -## Goal - -`stable_names` checks that output names stay stable on a small fixture of its own that still -has generic instances and trait impls, instead of translating all of `lang`. - -## Plan - -Decided with the maintainer on 2026-11-01: it caught 3 failures nothing else did, so it -stays in the gate, but on a fixture that costs a fraction of `lang`'s (about 300 -CPU-seconds saved now that it reuses `lang`'s translation). Cover each naming -rule it relies on in `lang` today. - -Public API: none. diff --git a/ts/Cargo.lock b/ts/Cargo.lock index 9921d2068..77eb53a88 100644 --- a/ts/Cargo.lock +++ b/ts/Cargo.lock @@ -172,6 +172,10 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "names-fixture" +version = "0.0.0" + [[package]] name = "once_cell" version = "1.21.4" diff --git a/ts/README.md b/ts/README.md index 1c6f6b2d5..e9edbd1ec 100644 --- a/ts/README.md +++ b/ts/README.md @@ -941,6 +941,11 @@ built natively for wasm32 and run beside its translation on the same inputs by with products of those values at 128 bits, 128-bit quotients by divisors below 2^32, and their halves joined back. `brands.ts`, another caller, mixes integer types where `tsc` must refuse them. +- `names` is translated from `run`, then with `added` as its first root, which reaches + another instance of what `run` reaches one of, each sorting before it: a generic method's, + a closure's, an impl's of another trait, a std function's that merges with `run`'s, and + an impl's over a deeper `&mut`, beside a trait object's module named as a type's that + `run` imports. Every line of the first must stay, in order. - `share` holds a `Copy` aggregate whose only write in the crate makes it copied, where `lang`'s other writes into the same type would hide a write the analysis misses: a byte array overwritten whole through a type parameter, or, under its features, by std diff --git a/ts/fixtures/lang/src/fmt.rs b/ts/fixtures/lang/src/fmt.rs index 0a700239b..899201cd2 100644 --- a/ts/fixtures/lang/src/fmt.rs +++ b/ts/fixtures/lang/src/fmt.rs @@ -319,8 +319,7 @@ impl fmt::Debug for Hex { } } -/// `Display` through `pad`, which keeps the options. Only `added` prints its `Debug`. -#[derive(Debug)] +/// `Display` through `pad`, which keeps the options. pub struct Padded(&'static str); impl fmt::Display for Padded { @@ -992,21 +991,6 @@ pub fn printed(data: &[u8]) -> Vec { format!("{fault}|{fault:>16}|{fault:?}|{fault:#?}").into_bytes() } -/// A root only the `stable_names` test adds, reaching another instance of what `run` -/// reaches one of: a generic method's, a closure's, an impl's of another trait, a std -/// function's that merges with one `run` reaches and sorts before it, and an impl's over a -/// deeper `&mut` than `run`'s, which sorts before its others. Its trait object's module shares its name with a type's this -/// module imports, and sorts first. -pub fn added(out: &mut Vec) { - out.push(Wrap::new(1u8).into_inner()); - out.push(1u8.partial_cmp(&2).map_or(7, |o| o as i8 as u8)); - let mapped = Wrap::new(2u8).map(|b| Pair(b, 0)); - put(out, &format!("{mapped:?} {:?}", Padded("added"))); - let shape: &dyn crate::dyns::Shape = &3u32; - out.push(shape.sides()); - out.extend_from_slice(&crate::refs::deeper(4).to_be_bytes()); -} - /// A writer of the crate's own that fails a `write_str` past `cap` bytes, and counts each. pub struct Capped { text: String, diff --git a/ts/fixtures/lang/src/refs.rs b/ts/fixtures/lang/src/refs.rs index 1f0f94f4f..31aae4b1b 100644 --- a/ts/fixtures/lang/src/refs.rs +++ b/ts/fixtures/lang/src/refs.rs @@ -777,13 +777,6 @@ fn tally_all(t: &mut T, data: &[u8]) -> u32 { t.total() } -/// A tally through more `&mut`s than `run`'s, which only the `stable_names` test reaches: -/// its impls' instances spell their suffixes alike but for the references, and sort first. -pub(crate) fn deeper(n: u32) -> u32 { - let mut count = n; - tally_all(&mut &mut &mut &mut count, &[1]) -} - /// A reader keeping a `&mut` to the slice it reads, which it lends on. struct Reader<'a, 'b> { rest: &'b mut &'a [u8], diff --git a/ts/fixtures/names/Cargo.toml b/ts/fixtures/names/Cargo.toml new file mode 100644 index 000000000..858d5dce8 --- /dev/null +++ b/ts/fixtures/names/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "names-fixture" +description = "Names rust2-ts keeps as translation reaches more instances" +version = "0.0.0" +edition.workspace = true +license.workspace = true +publish = false + +[lints] +workspace = true diff --git a/ts/fixtures/names/expected/core/option.ts b/ts/fixtures/names/expected/core/option.ts new file mode 100644 index 000000000..a43f18933 --- /dev/null +++ b/ts/fixtures/names/expected/core/option.ts @@ -0,0 +1,19 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "../rt.ts"; + +export function unwrapOr$0r2bmpr(self: rt.u8 | undefined, default_: rt.u8): rt.u8 { + if (self === undefined) { + return default_; + } + return self; +} + +export function mapOr$1e4a1e0(self: rt.Ordering | undefined, default_: rt.u8, f: (a0: rt.Ordering) => rt.u8): rt.u8 { + if (self === undefined) { + return default_; + } + return f(self); +} + +//# sourceMappingURL=option.ts.map diff --git a/ts/fixtures/names/expected/lib.ts b/ts/fixtures/names/expected/lib.ts new file mode 100644 index 000000000..7ceff05db --- /dev/null +++ b/ts/fixtures/names/expected/lib.ts @@ -0,0 +1,38 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; +import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, mapOr$1e4a1e0 as option_mapOr$1e4a1e0 } from "./core/option.ts"; +import * as shapes_Shape from "./shapes.Shape.ts"; +import { run as tally_run } from "./tally.ts"; +import type * as Pair from "./wrap.Pair.ts"; +import * as Wrap from "./wrap.Wrap.ts"; + +/** One instance of each generic item it reaches. */ +export function run(data: rt.ByteSlice): rt.ByteVec { + let shape!: shapes_Shape.Shape; + const first: rt.u8 = option_unwrapOr$0r2bmpr(rt.byteSliceFirst(data), 0); + const pair: Pair.Pair = Wrap.intoInner$wrap_Pair(Wrap.new$wrap_Pair({ _0: first, _1: 1 })); + const out: rt.ByteVec = rt.byteVecFrom(Uint8Array.of(Wrap.intoInner$u8(Wrap.map$wrap_Pair_u8_run_closure1(Wrap.new$wrap_Pair(pair), (p: Pair.Pair): rt.u8 => { + return p._0; + })))); + const _15: rt.u8 = option_mapOr$1e4a1e0(rt.cmp(data.length, 4), 9, (o: rt.Ordering): rt.u8 => { + return o & 0xff; + }); + rt.byteVecPush(out, _15); + const part = Wrap.new$wrap_Pair(pair); + const args2: rt.Fmt[] = [(f: rt.Formatter) => Wrap.fmt$fmt_Display_wrap_Pair(part, f)]; + const _22: rt.ByteSlice = rt.stringAsBytes(rt.fmtFormat([args2[0]])); + rt.byteVecExtendFromSlice(out, _22); + if (!(first === 0)) { + shape = "Line" as shapes_Shape.Shape; + } else { + shape = "Dot" as shapes_Shape.Shape; + } + const _38: rt.u8 = shapes_Shape.sides(shape); + rt.byteVecPush(out, _38); + const _42: rt.ByteSlice = rt.byteSliceFrom(rt.u32ToBeBytes(tally_run(data))); + rt.byteVecExtendFromSlice(out, _42); + return out; +} + +//# sourceMappingURL=lib.ts.map diff --git a/ts/fixtures/names/expected/shapes.Shape.ts b/ts/fixtures/names/expected/shapes.Shape.ts new file mode 100644 index 000000000..5ebf67652 --- /dev/null +++ b/ts/fixtures/names/expected/shapes.Shape.ts @@ -0,0 +1,15 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; + +/** A type named as `dyns::Shape` is. */ +export type Shape = "Dot" | "Line"; + +export function sides(self: Shape): rt.u8 { + if (self === "Dot") { + return 0; + } + return 1; +} + +//# sourceMappingURL=shapes.Shape.ts.map diff --git a/ts/fixtures/names/expected/tally.ts b/ts/fixtures/names/expected/tally.ts new file mode 100644 index 000000000..939fc7e11 --- /dev/null +++ b/ts/fixtures/names/expected/tally.ts @@ -0,0 +1,38 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; + +export function tally$tally_Tally_u32(self: rt.Place, n: rt.u8): void { + self.v = rt.u32Add(self.v, n as rt.u32); +} + +export function total$tally_Tally_u32(self: rt.u32): rt.u32 { + return self; +} + +export function tally$tally_Tally_mut_u32(self: rt.Place>, n: rt.u8): void { + tally$tally_Tally_u32(self.v, n); +} + +export function total$tally_Tally_mut_mut_u32(self: rt.Place>): rt.u32 { + return total$tally_Tally_mut_u32(self.v); +} + +export function total$tally_Tally_mut_u32(self: rt.Place): rt.u32 { + return total$tally_Tally_u32(self.v); +} + +export function tallyAll$mut_u32(t: rt.Place>, data: rt.ByteSlice): rt.u32 { + for (const _5 of rt.byteSliceView(data)) { + tally$tally_Tally_mut_u32(t, _5); + } + return total$tally_Tally_mut_mut_u32(t); +} + +export function run(data: rt.ByteSlice): rt.u32 { + let count: rt.u32 = 0; + let _4: rt.Place = rt.place(() => count, (value) => count = value); + return tallyAll$mut_u32(rt.place(() => _4, (value2) => _4 = value2), data); +} + +//# sourceMappingURL=tally.ts.map diff --git a/ts/fixtures/names/expected/wrap.Pair.ts b/ts/fixtures/names/expected/wrap.Pair.ts new file mode 100644 index 000000000..10a7375f8 --- /dev/null +++ b/ts/fixtures/names/expected/wrap.Pair.ts @@ -0,0 +1,19 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; + +/** Printed by `Display` in `run`, and by `Debug` only in `added`. */ +export interface Pair { + _0: rt.u8; + _1: rt.u8; +} + +export function fmt$fmt_Display(self: Pair, f: rt.Formatter): rt.Result { + const arg = self._0; + const _8: rt.Fmt = (f: rt.Formatter) => rt.u8Display(arg, f); + const arg2 = self._1; + const args2: rt.Fmt[] = [_8, (f: rt.Formatter) => rt.u8Display(arg2, f)]; + return rt.fmtWriteFmt(f, [args2[0], "-", args2[1]]); +} + +//# sourceMappingURL=wrap.Pair.ts.map diff --git a/ts/fixtures/names/expected/wrap.Wrap.ts b/ts/fixtures/names/expected/wrap.Wrap.ts new file mode 100644 index 000000000..339f822ff --- /dev/null +++ b/ts/fixtures/names/expected/wrap.Wrap.ts @@ -0,0 +1,37 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; +import * as Pair from "./wrap.Pair.ts"; + +/** A generic type, with a method generic over a closure. */ +export class Wrap { + _0: T; + + constructor(_0: T) { + this._0 = _0; + } +} + +export function new$wrap_Pair(value: Pair.Pair): Wrap { + return new Wrap(value); +} + +export function intoInner$u8(self: Wrap): rt.u8 { + return self._0; +} + +export function intoInner$wrap_Pair(self: Wrap): Pair.Pair { + return self._0; +} + +export function map$wrap_Pair_u8_run_closure1(self: Wrap, f: (a0: Pair.Pair) => rt.u8): Wrap { + return new Wrap(f(self._0)); +} + +export function fmt$fmt_Display_wrap_Pair(self: Wrap, f: rt.Formatter): rt.Result { + const arg = self._0; + const args2: rt.Fmt[] = [(f: rt.Formatter) => Pair.fmt$fmt_Display(arg, f)]; + return rt.fmtWriteFmt(f, ["[", args2[0], "]"]); +} + +//# sourceMappingURL=wrap.Wrap.ts.map diff --git a/ts/fixtures/names/src/dyns.rs b/ts/fixtures/names/src/dyns.rs new file mode 100644 index 000000000..f299829cd --- /dev/null +++ b/ts/fixtures/names/src/dyns.rs @@ -0,0 +1,10 @@ +/// A trait named as `shapes::Shape` is, whose object only `added` makes. +pub trait Shape { + fn sides(&self) -> u8; +} + +impl Shape for u32 { + fn sides(&self) -> u8 { + 3 + } +} diff --git a/ts/fixtures/names/src/lib.rs b/ts/fixtures/names/src/lib.rs new file mode 100644 index 000000000..cb5b7cc4a --- /dev/null +++ b/ts/fixtures/names/src/lib.rs @@ -0,0 +1,37 @@ +//! Names that stay as translation reaches more: `stable_names` translates `run`, then `added` +//! and `run`, and checks that every line of the first stays, in order. + +mod dyns; +mod shapes; +mod tally; +mod wrap; + +use shapes::Shape; +use wrap::{Pair, Wrap}; + +/// One instance of each generic item it reaches. +pub fn run(data: &[u8]) -> Vec { + let first = data.first().copied().unwrap_or(0); + let pair = Wrap::new(Pair(first, 1)).into_inner(); + let mut out = vec![Wrap::new(pair).map(|p| p.0).into_inner()]; + out.push(data.len().partial_cmp(&4).map_or(9, |o| o as i8 as u8)); + out.extend_from_slice(format!("{}", Wrap::new(pair)).as_bytes()); + let shape = if first == 0 { Shape::Dot } else { Shape::Line }; + out.push(shape.sides()); + out.extend_from_slice(&tally::run(data).to_be_bytes()); + out +} + +/// Another instance of what `run` reaches one of, each sorting before it: a generic method's, +/// a closure's, an impl's of another trait, a std function's that merges with `run`'s, and an +/// impl's over a deeper `&mut`. Its trait object's module shares its name with a type's this +/// module imports, and sorts first. +pub fn added(out: &mut Vec) { + out.push(Wrap::new(1u8).into_inner()); + out.push(1u8.partial_cmp(&2).map_or(7, |o| o as i8 as u8)); + let mapped = Wrap::new(2u8).map(|b| Pair(b, 0)); + out.extend_from_slice(format!("{mapped:?}").as_bytes()); + let shape: &dyn dyns::Shape = &3u32; + out.push(shape.sides()); + out.extend_from_slice(&tally::deeper(4).to_be_bytes()); +} diff --git a/ts/fixtures/names/src/shapes.rs b/ts/fixtures/names/src/shapes.rs new file mode 100644 index 000000000..7f7d6e1f8 --- /dev/null +++ b/ts/fixtures/names/src/shapes.rs @@ -0,0 +1,14 @@ +/// A type named as `dyns::Shape` is. +pub enum Shape { + Dot, + Line, +} + +impl Shape { + pub fn sides(&self) -> u8 { + match self { + Self::Dot => 0, + Self::Line => 1, + } + } +} diff --git a/ts/fixtures/names/src/tally.rs b/ts/fixtures/names/src/tally.rs new file mode 100644 index 000000000..f562c1e03 --- /dev/null +++ b/ts/fixtures/names/src/tally.rs @@ -0,0 +1,44 @@ +/// A count through `&mut self`, and the impl that forwards it through a `&mut`. +trait Tally { + fn tally(&mut self, n: u8); + fn total(&self) -> u32; +} + +impl Tally for u32 { + fn tally(&mut self, n: u8) { + *self += u32::from(n); + } + + fn total(&self) -> u32 { + *self + } +} + +impl Tally for &mut T { + fn tally(&mut self, n: u8) { + (**self).tally(n); + } + + fn total(&self) -> u32 { + (**self).total() + } +} + +fn tally_all(t: &mut T, data: &[u8]) -> u32 { + for &b in data { + t.tally(b); + } + t.total() +} + +pub fn run(data: &[u8]) -> u32 { + let mut count = 0; + tally_all(&mut &mut count, data) +} + +/// Through more `&mut`s than `run`: its impl's instances spell their suffixes alike but for +/// the references, and sort first. +pub fn deeper(n: u32) -> u32 { + let mut count = n; + tally_all(&mut &mut &mut &mut count, &[1]) +} diff --git a/ts/fixtures/names/src/wrap.rs b/ts/fixtures/names/src/wrap.rs new file mode 100644 index 000000000..86498743d --- /dev/null +++ b/ts/fixtures/names/src/wrap.rs @@ -0,0 +1,35 @@ +use std::fmt; + +/// A generic type, with a method generic over a closure. +#[derive(Debug)] +pub struct Wrap(T); + +impl Wrap { + pub fn new(value: T) -> Self { + Self(value) + } + + pub fn into_inner(self) -> T { + self.0 + } + + pub fn map(self, f: impl FnOnce(T) -> U) -> Wrap { + Wrap(f(self.0)) + } +} + +impl fmt::Display for Wrap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{}]", self.0) + } +} + +/// Printed by `Display` in `run`, and by `Debug` only in `added`. +#[derive(Clone, Copy, Debug)] +pub struct Pair(pub u8, pub u8); + +impl fmt::Display for Pair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}-{}", self.0, self.1) + } +} diff --git a/ts/fixtures/names/ts.toml b/ts/fixtures/names/ts.toml new file mode 100644 index 000000000..3c273390e --- /dev/null +++ b/ts/fixtures/names/ts.toml @@ -0,0 +1,2 @@ +package = "names-fixture" +roots = ["run"] diff --git a/ts/tests/fixtures.rs b/ts/tests/fixtures.rs index 6bae71b5f..c5c9504a5 100644 --- a/ts/tests/fixtures.rs +++ b/ts/tests/fixtures.rs @@ -5,7 +5,6 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Output}; -use std::sync::OnceLock; const TARGET: &str = "wasm32-unknown-unknown"; @@ -304,10 +303,11 @@ fn source_order() { /// impl of a method, renames nothing the old roots reach. #[test] fn stable_names() { - // `lang`'s own roots, so its translation is the base. - let roots = r#"roots = ["harness::run", "harness::op", "fmt::Fault"]"#; - let added = r#"roots = ["fmt::added", "harness::run", "harness::op", "fmt::Fault"]"#; - kept(&sources(lang_out()), &rooted("lang", roots, "added", added), added); + let out = translated("names"); + snapshot("names", &out); + typecheck(&out); + let added = r#"roots = ["added", "run"]"#; + kept(&sources(&out), &rooted("names", r#"roots = ["run"]"#, "added", added), added); } /// Translate fixture `name` into `file` with its config's `roots` line replaced by `with`. @@ -361,15 +361,9 @@ fn lines(text: &str) -> Vec { #[test] fn lang() { - let out = lang_out(); - snapshot("lang", out); - lang_differential(out); -} - -/// `lang`'s translation, which `stable_names` shares rather than translating it again. -fn lang_out() -> &'static Path { - static OUT: OnceLock = OnceLock::new(); - OUT.get_or_init(|| translated("lang")) + let out = translated("lang"); + snapshot("lang", &out); + lang_differential(&out); } fn lang_differential(out: &Path) { From 3c2ef4bba81c5f614dfaa71084dc0c6d75239888 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:31:39 -0800 Subject: [PATCH 3/6] style(ts): keep stable_names' check on one line Co-Authored-By: Claude Opus 5.5 --- ts/tests/fixtures.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ts/tests/fixtures.rs b/ts/tests/fixtures.rs index c5c9504a5..7dc3303c9 100644 --- a/ts/tests/fixtures.rs +++ b/ts/tests/fixtures.rs @@ -307,7 +307,8 @@ fn stable_names() { snapshot("names", &out); typecheck(&out); let added = r#"roots = ["added", "run"]"#; - kept(&sources(&out), &rooted("names", r#"roots = ["run"]"#, "added", added), added); + let more = rooted("names", r#"roots = ["run"]"#, "added", added); + kept(&sources(&out), &more, added); } /// Translate fixture `name` into `file` with its config's `roots` line replaced by `with`. From db65fd8ebcebc7cfee730dccb60c4af417fca970 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:41:24 -0800 Subject: [PATCH 4/6] test(ts): regenerate lang's snapshot without fmt::added Co-Authored-By: Claude Opus 5.5 --- ts/fixtures/lang/expected/fmt.Padded.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/fixtures/lang/expected/fmt.Padded.ts b/ts/fixtures/lang/expected/fmt.Padded.ts index f0712bbdf..8996e1c26 100644 --- a/ts/fixtures/lang/expected/fmt.Padded.ts +++ b/ts/fixtures/lang/expected/fmt.Padded.ts @@ -2,7 +2,7 @@ import * as rt from "./rt.ts"; -/** `Display` through `pad`, which keeps the options. Only `added` prints its `Debug`. */ +/** `Display` through `pad`, which keeps the options. */ export interface Padded { _0: string; } From d9b3bf6aa764d9a8df09f18201cbedc803bd5ac2 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:41:24 -0800 Subject: [PATCH 5/6] chore(quest): plan a result slot's name that stays as translation reaches more Co-Authored-By: Claude Opus 5.5 --- quest/m3/README.md | 1 + quest/m3/ts-stable-slots.md | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 quest/m3/ts-stable-slots.md diff --git a/quest/m3/README.md b/quest/m3/README.md index 792c89bb2..7fad94e4c 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -38,6 +38,7 @@ generic std gap is planned here even when moq never calls it. - [A B-tree's searches call Ord as std's do](/quest/m3/ts-btree-search-order.md) - a B-tree's lookups and inserts call a key's own `Ord` in std's order and number - [Rc and Arc of a slice](/quest/m3/ts-shared-slices.md) - `Rc<[T]>` and `Arc<[T]>` from a `Vec`, a slice, or an iterator translate - [The rest of str's searches](/quest/m3/ts-str-searches.md) - `find`, `strip_prefix`, `replace`, `rsplit`, `splitn`, `matches`, `lines`, and the rest of `str`'s searches translate +- [A result slot's name stays as translation reaches more](/quest/m3/ts-stable-slots.md) - reaching another function sharing a slot renames nothing - [Adding to a String](/quest/m3/ts-string-add.md) - `String + &str` translates as `+=` does - [The rest of a vector's methods](/quest/m3/ts-vec-rest.md) - `append`, `resize`, `split_off`, `try_reserve`, and `Vec::from(VecDeque)` translate - [A reversed zip's per-pair cost](/quest/m3/ts-zip-rev-overhead.md) - a reversed by-index zip costs per pair what a forward one does, or the gap is explained diff --git a/quest/m3/ts-stable-slots.md b/quest/m3/ts-stable-slots.md new file mode 100644 index 000000000..cfdceace9 --- /dev/null +++ b/quest/m3/ts-stable-slots.md @@ -0,0 +1,18 @@ +# [S] A result slot's name stays as translation reaches more + +## Goal + +Reaching another function that shares a result slot renames neither the slot nor any line +that reads or writes it, and `stable_names` covers it on the `names` fixture. + +## Plan + +Found while moving `stable_names` to its own fixture on 2026-11-02. A slot of `Ok` parts is +named for the first function in source order among those translation reaches that share +it, so a root reaching an earlier one renames it: with `read` reached, `readOut`; once +`lead`, declared above it with the same payload type, is reached too, every `readOut` in +`read` and its callers becomes `leadOut`. `place_slot` in `ts/src/lower/mir/scalar.rs` picks +the name. Name it by what the slot holds, or by every function of the crate that could +share it as the import census does, rather than by what translation reached. + +Public API: the translator's output. From a8432b53e1162e99e79155b5191941a9cf512874 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Mon, 2 Nov 2026 16:33:01 -0800 Subject: [PATCH 6/6] chore(quest): move the result slot's stable name to m0's checks Unstable names churn snapshots and conflict merges, which the checks epic exists to cut. Co-Authored-By: Claude Opus 5.5 --- quest/m0/checks/README.md | 1 + quest/{m3 => m0/checks}/ts-stable-slots.md | 3 +++ quest/m3/README.md | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) rename quest/{m3 => m0/checks}/ts-stable-slots.md (86%) diff --git a/quest/m0/checks/README.md b/quest/m0/checks/README.md index 1989eb940..4f5b4c53f 100644 --- a/quest/m0/checks/README.md +++ b/quest/m0/checks/README.md @@ -21,3 +21,4 @@ Each child reports its CPU saved, cold and warm, against `origin/main`. - [A warm gate](/quest/m0/checks/cache.md) - the land gate builds through mbx, as moq's does - [Fixtures that merge](/quest/m0/checks/fixture-layout.md) - `lang` splits by module, and `reject`'s snapshots key by item, not line - [The stock fixture's target dir race](/quest/m0/checks/stock-cachedir.md) - the `stock` fixture never fails creating its target dir +- [A result slot's name stays as translation reaches more](/quest/m0/checks/ts-stable-slots.md) - reaching another function sharing a slot renames nothing diff --git a/quest/m3/ts-stable-slots.md b/quest/m0/checks/ts-stable-slots.md similarity index 86% rename from quest/m3/ts-stable-slots.md rename to quest/m0/checks/ts-stable-slots.md index cfdceace9..3770201c1 100644 --- a/quest/m3/ts-stable-slots.md +++ b/quest/m0/checks/ts-stable-slots.md @@ -7,6 +7,9 @@ that reads or writes it, and `stable_names` covers it on the `names` fixture. ## Plan +Decided with the maintainer on 2026-11-02: m0, since unstable names churn snapshots and +conflict merges, which this epic exists to cut. + Found while moving `stable_names` to its own fixture on 2026-11-02. A slot of `Ok` parts is named for the first function in source order among those translation reaches that share it, so a root reaching an earlier one renames it: with `read` reached, `readOut`; once diff --git a/quest/m3/README.md b/quest/m3/README.md index 50df1063b..7ab70819d 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -38,7 +38,6 @@ generic std gap is planned here even when moq never calls it. - [A vector's exact capacity as it shrinks](/quest/m3/ts-vec-exact-capacity.md) - a deque made from a `Vec` splits its slices where std's does - [A B-tree's searches call Ord as std's do](/quest/m3/ts-btree-search-order.md) - a B-tree's lookups and inserts call a key's own `Ord` in std's order and number - [Rc and Arc of a slice](/quest/m3/ts-shared-slices.md) - `Rc<[T]>` and `Arc<[T]>` from a `Vec`, a slice, or an iterator translate -- [A result slot's name stays as translation reaches more](/quest/m3/ts-stable-slots.md) - reaching another function sharing a slot renames nothing - [Adding to a String](/quest/m3/ts-string-add.md) - `String + &str` translates as `+=` does - [The rest of a vector's methods](/quest/m3/ts-vec-rest.md) - `append`, `resize`, `split_off`, `try_reserve`, and `Vec::from(VecDeque)` translate - [A reversed zip's per-pair cost](/quest/m3/ts-zip-rev-overhead.md) - a reversed by-index zip costs per pair what a forward one does, or the gap is explained