diff --git a/quest/m0/README.md b/quest/m0/README.md index 5963a5103..3b2be2748 100644 --- a/quest/m0/README.md +++ b/quest/m0/README.md @@ -7,4 +7,4 @@ Urgent fixes to anything a consumer already depends on. ## Required - [Checks at moq's cost](/quest/m0/checks/README.md) - `just ci` selects by the diff, builds warm, and runs the differential optimized, with a nightly full `just gate` -- [Translating moq-net panics on a dyn Buf](/quest/m0/ts-moq-dyn-panic.md) - `just ts moq` and `just ts bench` translate moq-net at the pin again +- [Gate moq-net's translation](/quest/m0/ts-moq-gate.md) - `just ci` translates moq-net at the pin, so a change that breaks it fails in the queue diff --git a/quest/m0/ts-moq-dyn-panic.md b/quest/m0/ts-moq-dyn-panic.md deleted file mode 100644 index 73ff9618f..000000000 --- a/quest/m0/ts-moq-dyn-panic.md +++ /dev/null @@ -1,19 +0,0 @@ -# [S] Translating moq-net panics on a dyn Buf - -## Goal - -`just ts moq` and `just ts bench` translate moq-net at the pinned revision again. Today -rust2-ts panics in rustc while it reads moq-net's writes: -``` has escaping bound vars``, from -`copy::BodyWrites::replace`, which `pointees` calls, asking whether a type under a binder -is sized. A fixture reaches the same shape, so `just ci` catches it from then on. - -## Plan - -Found on [moq-dev/rust2#529](https://github.com/moq-dev/rust2/pull/529), whose change -doesn't touch the analysis, so `main` likely has it too, perhaps since a trait object of `Buf` -began to translate, as in [moq-dev/rust2#519](https://github.com/moq-dev/rust2/pull/519) -or [#521](https://github.com/moq-dev/rust2/pull/521). Erase or instantiate the bound -vars before asking, rather than skipping the type. - -Public API: none. diff --git a/quest/m0/ts-moq-gate.md b/quest/m0/ts-moq-gate.md new file mode 100644 index 000000000..9341f629d --- /dev/null +++ b/quest/m0/ts-moq-gate.md @@ -0,0 +1,22 @@ +# [S] Gate moq-net's translation + +## Goal + +A change that breaks moq-net's translation fails in the merge queue, not the next nightly: +`just ci` for a `ts` change translates moq-net at the pin and type-checks it, as +`just ts moq` does. Today only `nightly.yml` runs `just ts moq`, so +[moq-dev/rust2#519](https://github.com/moq-dev/rust2/pull/519) landed a panic on moq-net +that a bench run found hours later. + +## Plan + +`just ts check` already checks out the pin for the bench's type-check, so what this adds is +building moq-net's dependencies through the driver and translating it. Measure that in CPU, +cold and warm, against [the checks budget](/quest/m0/checks/README.md) before adding it; +if it costs too much, consider running it only for changes under `ts/src` or `ts/runtime`. + +Public API: none. + +## Related + +- [Checks at moq's cost](/quest/m0/checks/README.md) - the gate's CPU budget this spends from diff --git a/ts/fixtures/stock/expected/buf.Leased.ts b/ts/fixtures/stock/expected/buf.Leased.ts index f9fb316b5..c5e0abcc8 100644 --- a/ts/fixtures/stock/expected/buf.Leased.ts +++ b/ts/fixtures/stock/expected/buf.Leased.ts @@ -1,10 +1,11 @@ // Generated by rust2-ts. Do not edit. import * as rt from "./rt.ts"; +import type * as Buf from "./bytes/buf/buf_impl.Buf.ts"; import type * as TryGetError from "./bytes.TryGetError.ts"; /** A buffer of the crate's own over `Buf`, as moq's transports lend a stream's chunks. */ export type Leased = - | { pieces$Leased: () => rt.usize; remaining$Buf: () => rt.usize; chunk$Buf: () => rt.ByteSlice; advance$Buf: (a0: rt.usize) => void; hasRemaining$Buf: () => boolean; copyToSlice$Buf: (a0: rt.ByteSlice) => void; getU8$Buf: () => rt.u8; getI8$Buf: () => rt.i8; getU16$Buf: () => rt.u16; getU16Le$Buf: () => rt.u16; getU16Ne$Buf: () => rt.u16; getI16$Buf: () => rt.i16; getI16Le$Buf: () => rt.i16; getI16Ne$Buf: () => rt.i16; getU32$Buf: () => rt.u32; getU32Le$Buf: () => rt.u32; getU32Ne$Buf: () => rt.u32; getI32$Buf: () => rt.i32; getI32Le$Buf: () => rt.i32; getI32Ne$Buf: () => rt.i32; getU64$Buf: () => rt.u64; getU64Le$Buf: () => rt.u64; getU64Ne$Buf: () => rt.u64; getI64$Buf: () => rt.i64; getI64Le$Buf: () => rt.i64; getI64Ne$Buf: () => rt.i64; getU128$Buf: () => rt.u128; getU128Le$Buf: () => rt.u128; getU128Ne$Buf: () => rt.u128; getI128$Buf: () => rt.i128; getI128Le$Buf: () => rt.i128; getI128Ne$Buf: () => rt.i128; getUint$Buf: (a0: rt.usize) => rt.u64; getUintLe$Buf: (a0: rt.usize) => rt.u64; getUintNe$Buf: (a0: rt.usize) => rt.u64; getInt$Buf: (a0: rt.usize) => rt.i64; getIntLe$Buf: (a0: rt.usize) => rt.i64; getIntNe$Buf: (a0: rt.usize) => rt.i64; getF32$Buf: () => rt.f32; getF32Le$Buf: () => rt.f32; getF32Ne$Buf: () => rt.f32; getF64$Buf: () => rt.f64; getF64Le$Buf: () => rt.f64; getF64Ne$Buf: () => rt.f64; tryCopyToSlice$Buf: (a0: rt.ByteSlice) => rt.Result; tryGetU8$Buf: () => rt.Result; tryGetI8$Buf: () => rt.Result; tryGetU16$Buf: () => rt.Result; tryGetU16Le$Buf: () => rt.Result; tryGetU16Ne$Buf: () => rt.Result; tryGetI16$Buf: () => rt.Result; tryGetI16Le$Buf: () => rt.Result; tryGetI16Ne$Buf: () => rt.Result; tryGetU32$Buf: () => rt.Result; tryGetU32Le$Buf: () => rt.Result; tryGetU32Ne$Buf: () => rt.Result; tryGetI32$Buf: () => rt.Result; tryGetI32Le$Buf: () => rt.Result; tryGetI32Ne$Buf: () => rt.Result; tryGetU64$Buf: () => rt.Result; tryGetU64Le$Buf: () => rt.Result; tryGetU64Ne$Buf: () => rt.Result; tryGetI64$Buf: () => rt.Result; tryGetI64Le$Buf: () => rt.Result; tryGetI64Ne$Buf: () => rt.Result; tryGetU128$Buf: () => rt.Result; tryGetU128Le$Buf: () => rt.Result; tryGetU128Ne$Buf: () => rt.Result; tryGetI128$Buf: () => rt.Result; tryGetI128Le$Buf: () => rt.Result; tryGetI128Ne$Buf: () => rt.Result; tryGetUint$Buf: (a0: rt.usize) => rt.Result; tryGetUintLe$Buf: (a0: rt.usize) => rt.Result; tryGetUintNe$Buf: (a0: rt.usize) => rt.Result; tryGetInt$Buf: (a0: rt.usize) => rt.Result; tryGetIntLe$Buf: (a0: rt.usize) => rt.Result; tryGetIntNe$Buf: (a0: rt.usize) => rt.Result; tryGetF32$Buf: () => rt.Result; tryGetF32Le$Buf: () => rt.Result; tryGetF32Ne$Buf: () => rt.Result; tryGetF64$Buf: () => rt.Result; tryGetF64Le$Buf: () => rt.Result; tryGetF64Ne$Buf: () => rt.Result; copyToBytes$Buf: (a0: rt.usize) => rt.ByteSlice }; + | { pieces$Leased: () => rt.usize; skip$Leased: (a0: Buf.Buf) => void; remaining$Buf: () => rt.usize; chunk$Buf: () => rt.ByteSlice; advance$Buf: (a0: rt.usize) => void; hasRemaining$Buf: () => boolean; copyToSlice$Buf: (a0: rt.ByteSlice) => void; getU8$Buf: () => rt.u8; getI8$Buf: () => rt.i8; getU16$Buf: () => rt.u16; getU16Le$Buf: () => rt.u16; getU16Ne$Buf: () => rt.u16; getI16$Buf: () => rt.i16; getI16Le$Buf: () => rt.i16; getI16Ne$Buf: () => rt.i16; getU32$Buf: () => rt.u32; getU32Le$Buf: () => rt.u32; getU32Ne$Buf: () => rt.u32; getI32$Buf: () => rt.i32; getI32Le$Buf: () => rt.i32; getI32Ne$Buf: () => rt.i32; getU64$Buf: () => rt.u64; getU64Le$Buf: () => rt.u64; getU64Ne$Buf: () => rt.u64; getI64$Buf: () => rt.i64; getI64Le$Buf: () => rt.i64; getI64Ne$Buf: () => rt.i64; getU128$Buf: () => rt.u128; getU128Le$Buf: () => rt.u128; getU128Ne$Buf: () => rt.u128; getI128$Buf: () => rt.i128; getI128Le$Buf: () => rt.i128; getI128Ne$Buf: () => rt.i128; getUint$Buf: (a0: rt.usize) => rt.u64; getUintLe$Buf: (a0: rt.usize) => rt.u64; getUintNe$Buf: (a0: rt.usize) => rt.u64; getInt$Buf: (a0: rt.usize) => rt.i64; getIntLe$Buf: (a0: rt.usize) => rt.i64; getIntNe$Buf: (a0: rt.usize) => rt.i64; getF32$Buf: () => rt.f32; getF32Le$Buf: () => rt.f32; getF32Ne$Buf: () => rt.f32; getF64$Buf: () => rt.f64; getF64Le$Buf: () => rt.f64; getF64Ne$Buf: () => rt.f64; tryCopyToSlice$Buf: (a0: rt.ByteSlice) => rt.Result; tryGetU8$Buf: () => rt.Result; tryGetI8$Buf: () => rt.Result; tryGetU16$Buf: () => rt.Result; tryGetU16Le$Buf: () => rt.Result; tryGetU16Ne$Buf: () => rt.Result; tryGetI16$Buf: () => rt.Result; tryGetI16Le$Buf: () => rt.Result; tryGetI16Ne$Buf: () => rt.Result; tryGetU32$Buf: () => rt.Result; tryGetU32Le$Buf: () => rt.Result; tryGetU32Ne$Buf: () => rt.Result; tryGetI32$Buf: () => rt.Result; tryGetI32Le$Buf: () => rt.Result; tryGetI32Ne$Buf: () => rt.Result; tryGetU64$Buf: () => rt.Result; tryGetU64Le$Buf: () => rt.Result; tryGetU64Ne$Buf: () => rt.Result; tryGetI64$Buf: () => rt.Result; tryGetI64Le$Buf: () => rt.Result; tryGetI64Ne$Buf: () => rt.Result; tryGetU128$Buf: () => rt.Result; tryGetU128Le$Buf: () => rt.Result; tryGetU128Ne$Buf: () => rt.Result; tryGetI128$Buf: () => rt.Result; tryGetI128Le$Buf: () => rt.Result; tryGetI128Ne$Buf: () => rt.Result; tryGetUint$Buf: (a0: rt.usize) => rt.Result; tryGetUintLe$Buf: (a0: rt.usize) => rt.Result; tryGetUintNe$Buf: (a0: rt.usize) => rt.Result; tryGetInt$Buf: (a0: rt.usize) => rt.Result; tryGetIntLe$Buf: (a0: rt.usize) => rt.Result; tryGetIntNe$Buf: (a0: rt.usize) => rt.Result; tryGetF32$Buf: () => rt.Result; tryGetF32Le$Buf: () => rt.Result; tryGetF32Ne$Buf: () => rt.Result; tryGetF64$Buf: () => rt.Result; tryGetF64Le$Buf: () => rt.Result; tryGetF64Ne$Buf: () => rt.Result; copyToBytes$Buf: (a0: rt.usize) => rt.ByteSlice }; //# sourceMappingURL=buf.Leased.ts.map diff --git a/ts/fixtures/stock/expected/buf.Pieces.ts b/ts/fixtures/stock/expected/buf.Pieces.ts index d727d988d..e1a8ecc35 100644 --- a/ts/fixtures/stock/expected/buf.Pieces.ts +++ b/ts/fixtures/stock/expected/buf.Pieces.ts @@ -4,7 +4,8 @@ import * as rt from "./rt.ts"; import { out$f32, out$f64, out$i64, out$i8, out$u128, out$u16, out$u64, out$u8 } from "./lib.ts"; import type * as Provided from "./buf.Provided.ts"; import { panicAdvance, panicDoesNotFit } from "./bytes.ts"; -import { signExtend } from "./bytes/buf/buf_impl.ts"; +import { signExtend, hasRemaining$bytes_Buf_mut_dyn_bytes_Buf, remaining$bytes_Buf_mut_dyn_bytes_Buf } from "./bytes/buf/buf_impl.ts"; +import type * as Buf from "./bytes/buf/buf_impl.Buf.ts"; import * as Chain from "./bytes/buf/chain.Chain.ts"; import { new_$mut_buf_Pieces as take_new_$mut_buf_Pieces } from "./bytes/buf/take.ts"; import type * as Take from "./bytes/buf/take.Take.ts"; @@ -189,6 +190,15 @@ export function pieces$buf_Leased(self: Pieces): rt.usize { return self.parts.length; } +/** Reads a byte of `other`, then skips as many bytes as it has left. */ +export function skip$buf_Leased(self: Pieces, other: Buf.Buf): void { + if (hasRemaining$bytes_Buf_mut_dyn_bytes_Buf(other)) { + other.advance$Buf(1); + } + const _7: rt.usize = rt.min(remaining$bytes_Buf_mut_dyn_bytes_Buf(other), remaining$bytes_Buf_mut(self)); + advance$bytes_Buf(self, _7); +} + /** * Returns true if there are any more bytes to consume * diff --git a/ts/fixtures/stock/expected/buf.ts b/ts/fixtures/stock/expected/buf.ts index 5a08e3fdd..3a08a7499 100644 --- a/ts/fixtures/stock/expected/buf.ts +++ b/ts/fixtures/stock/expected/buf.ts @@ -2286,7 +2286,8 @@ export function readAll$ref_slice_u8(buf: rt.Place, ops: rt.ByteSl /** * The reads of `reads` through a `dyn Buf` of each buffer: called on the object, through * the impls forwarding through a `&mut` to one and a `Box` of one, and through the crate's - * own trait over `Buf`. A box drops what it owns once. + * own trait over `Buf`, whose method takes another `dyn Buf`. A box drops what it owns + * once. */ export function dyns(dataData: Uint8Array, dataStart: number, dataLen: number, out: rt.ByteVec): void { let _30!: Buf.Buf; @@ -2345,6 +2346,9 @@ export function dyns(dataData: Uint8Array, dataStart: number, dataLen: number, o rt.byteVecPush(out, drops.value.v); const leased: Leased.Leased = new of$buf_Pieces(Pieces.new(input.data, input.start, input.length, cut)); rt.byteVecPush(out, leased.pieces$Leased() & 0xff); + let skipped: rt.ByteSlice = ops2; + leased.skip$Leased(new ofPlace$ref_slice_u8(rt.place(() => skipped, (value5) => skipped = value5))); + rt.byteVecPush(out, skipped.length & 0xff); rt.byteVecExtendFromSlice(out, rt.byteVecSlice(readAll$dyn_buf_Leased(leased, ops2, out))); rt.byteVecPush(out, leased.pieces$Leased() & 0xff); rt.drop(leased); diff --git a/ts/fixtures/stock/expected/lib.ts b/ts/fixtures/stock/expected/lib.ts index f4bb15509..c10afd9cd 100644 --- a/ts/fixtures/stock/expected/lib.ts +++ b/ts/fixtures/stock/expected/lib.ts @@ -486,6 +486,10 @@ export class of$buf_Pieces { return Pieces.remaining$bytes_Buf(this.v); } + skip$Leased(a: Buf.Buf): void { + return Pieces.skip$buf_Leased(this.v, a); + } + tryCopyToSlice$Buf(a: rt.ByteSlice): rt.Result { return Pieces.tryCopyToSlice$bytes_Buf(this.v, a); } diff --git a/ts/fixtures/stock/src/buf.rs b/ts/fixtures/stock/src/buf.rs index ca2e3fcd9..bc0c1a071 100644 --- a/ts/fixtures/stock/src/buf.rs +++ b/ts/fixtures/stock/src/buf.rs @@ -276,12 +276,21 @@ fn read_all(buf: &mut B, ops: &[u8], out: &mut Vec) -> Vec< /// A buffer of the crate's own over `Buf`, as moq's transports lend a stream's chunks. trait Leased: Buf { fn pieces(&self) -> usize; + /// Reads a byte of `other`, then skips as many bytes as it has left. + fn skip(&mut self, other: &mut dyn Buf); } impl Leased for Pieces { fn pieces(&self) -> usize { self.parts.len() } + + fn skip(&mut self, other: &mut dyn Buf) { + if other.has_remaining() { + other.advance(1); + } + self.advance(other.remaining().min(self.remaining())); + } } /// Pieces that count their drops. @@ -309,7 +318,8 @@ impl Drop for Counted { /// The reads of `reads` through a `dyn Buf` of each buffer: called on the object, through /// the impls forwarding through a `&mut` to one and a `Box` of one, and through the crate's -/// own trait over `Buf`. A box drops what it owns once. +/// own trait over `Buf`, whose method takes another `dyn Buf`. A box drops what it owns +/// once. pub fn dyns(data: &[u8], out: &mut Vec) { let [kind, cut, ops @ ..] = data else { return; @@ -351,6 +361,9 @@ pub fn dyns(data: &[u8], out: &mut Vec) { let mut leased: Box = Box::new(Pieces::new(input, cut)); out.push(leased.pieces() as u8); + let mut skipped = ops; + leased.skip(&mut skipped); + out.push(skipped.len() as u8); let rest = read_all(&mut *leased, ops, out); out.extend_from_slice(&rest); out.push(leased.pieces() as u8); diff --git a/ts/src/lower/copy.rs b/ts/src/lower/copy.rs index 288b5c5b6..23b68e7e0 100644 --- a/ts/src/lower/copy.rs +++ b/ts/src/lower/copy.rs @@ -1156,12 +1156,9 @@ impl<'tcx> Visitor<'tcx> for BodyWrites<'_, '_, '_, 'tcx> { && let ty::FnDef(def_id, args) = *constant.ty().kind() && !self.crate_fn(def_id, args.skip_binder()) { - let sig = self - .tcx - .fn_sig(def_id) - .instantiate(self.tcx, args.skip_binder()) - .skip_norm_wip(); - if let Some(&this) = sig.skip_binder().inputs().first() + let sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args.skip_binder()); + let sig = self.tcx.instantiate_bound_regions_with_erased(sig.skip_norm_wip()); + if let Some(&this) = sig.inputs().first() && let Some(value) = replaces(self.tcx, def_id, this) { self.replace(value); @@ -1181,7 +1178,7 @@ impl<'tcx> Visitor<'tcx> for BodyWrites<'_, '_, '_, 'tcx> { } _ => None, }; - for (i, &input) in sig.skip_binder().inputs().iter().enumerate() { + for (i, &input) in sig.inputs().iter().enumerate() { let fresh = replace && call.is_some_and(|(args, made)| { args.get(i)