Repository navigation
fix(ts): erase a called function's late-bound regions before asking what its inputs hold - #545
Conversation
…hat its inputs hold A crate trait's method taking `&mut dyn Buf`, called through an object, handed `dyn Buf + '^0` with an escaping bound region to `is_sized`, which panicked on moq-net. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. WalkthroughOperand lowering now instantiates function signatures with erased bound regions before checking the receiver and processing arguments. The Buf fixture adds and exercises a Merge Risk:
|
There was a problem hiding this comment.
ℹ️ No critical issues — one doc nit inline.
Reviewed changes
- Region erasure at calls:
BodyWrites::visit_operand(ts/src/lower/copy.rs:1159) instantiates the callee's bound regions with erased instead ofskip_binder(), so a callee like moq-net'sErasedSend::poll_write_bufno longer hands&mut dyn Buf's late-bound regions to theis_sizedquery inreplace. This matches the same shape ints/src/lower/iter.rs:1658. - Fixture coverage: the stock fixture's
Leasedtrait gainsskip(&mut self, other: &mut dyn Buf)called through theBox<dyn Leased>indyns, with snapshot and differential regressions underts/fixtures/stock/. - Quest tree:
ts-moq-dyn-panic.mdis deleted as finished, and the newts-moq-gate.md([S]) records gating moq-net's translation injust cibehind a CPU measurement against the checks budget, linked fromquest/m0/README.md.
I traced every consumer of the changed sig values: recorded types go through storage() (erase_and_anonymize_regions), replaces(), fills_fresh(), and pointees() match kinds syntactically, and keeps/crate_fn are untouched, so the erasure only removes the panic path and records the same types as before. The fixture's TS output mirrors the native semantics too: bytes impls Buf for &[u8], so &mut skipped coerces to &mut dyn Buf and the translation's rt.Place models the in-place advance.
(Written by GLM-5.3-Flash)
glm-5.3-flash | 𝕏
| /// 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. |
There was a problem hiding this comment.
it most naturally binds to other, which reads as "skip discards everything other still holds", but the body advances self by min(other.remaining(), self.remaining()). "then skips as many bytes as the two have left" would say that.
| /// Reads a byte of `other`, then skips as many bytes as it has left. | |
| /// Reads a byte of `other`, then skips as many bytes as the two have left. |

Problem
just ts moq, whichjust ts benchruns first, panics in rustc while the copy analysis reads moq-net:<dyn bytes::Buf as std::marker::Sized>has escaping bound vars. moq-net'sErasedSend::poll_write_buf(&mut self, ..., buf: &mut dyn Buf)is called through a trait object.BodyWrites::visit_operandtook that callee's signature withskip_binder(), so the input&'^0 mut (dyn Buf + '^0)kept its late-bound regions. #519 then added anis_sizedquery toreplace, and that query panics on escaping bound vars.Bisected: b3c119556 (#519) panics, its parent 49b68c72b translates moq-net. #521 and #511 are not the cause.
Approach
instantiate_bound_regions_with_erased), asmir/call.rsanditer.rsalready do, instead ofskip_binder(). The types it records are region-erased already (storage), so only the trait query sees a difference.skip_binder()signatures ints/srconly walk types syntactically and never reach a trait query.Leasedtrait gainsskip(&mut self, other: &mut dyn Buf), called through theBox<dyn Leased>inbuf::dyns. This is moq's shape, and it reproduces the panic onmain. It runs differentially against native Rust.Impact
Public API: none.
Alternatives
replace. Rejected: the analysis would then miss what such a callee replaces.Follow-ups
just ts moqruns only innightly.yml, not injust cior the merge queue. That is how feat(ts): translate the rest of a trait object in a struct's last field #519 landed. The stock fixture now covers this shape injust ci. Gate moq-net's translation (quest/m0/ts-moq-gate.md) records gating the whole translation, pending a CPU measurement.quest/m0/ts-moq-dyn-panic.md, which #529 recorded, and deletes it.Checks
just ts moqpasses.just ts test --test fixtures stockfails with the panic before the fix and passes after it.just cipasses on the merged head.Review
Codex (
quest review), gpt-6.1-sol, medium effort, round 1 on head 5c90d3f: no findings.🤖 Generated with Claude Code