Skip to content

fix(ts): erase a called function's late-bound regions before asking what its inputs hold - #545

Merged
kixelated merged 7 commits into
mainfrom
quest/m0/ts-moq-dyn-panic
Nov 3, 2026
Merged

kixelated merged 7 commits into
mainfrom
quest/m0/ts-moq-dyn-panic

Conversation

@kixelated

@kixelated kixelated commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

just ts moq, which just ts bench runs first, panics in rustc while the copy analysis reads moq-net:
<dyn bytes::Buf as std::marker::Sized> has escaping bound vars. moq-net's ErasedSend::poll_write_buf(&mut self, ..., buf: &mut dyn Buf) is called through a trait object. BodyWrites::visit_operand took that callee's signature with skip_binder(), so the input &'^0 mut (dyn Buf + '^0) kept its late-bound regions. #519 then added an is_sized query to replace, 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 the callee's bound regions as erased (instantiate_bound_regions_with_erased), as mir/call.rs and iter.rs already do, instead of skip_binder(). The types it records are region-erased already (storage), so only the trait query sees a difference.
  • The other skip_binder() signatures in ts/src only walk types syntactically and never reach a trait query.
  • The stock fixture's Leased trait gains skip(&mut self, other: &mut dyn Buf), called through the Box<dyn Leased> in buf::dyns. This is moq's shape, and it reproduces the panic on main. It runs differentially against native Rust.

Impact

Public API: none.

Alternatives

  • Skip types with escaping bound vars in replace. Rejected: the analysis would then miss what such a callee replaces.

Follow-ups

Checks

  • just ts moq passes.
  • just ts test --test fixtures stock fails with the panic before the fix and passes after it.
  • just ci passes 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

kixelated and others added 4 commits November 3, 2026 11:26
…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>
@kixelated
kixelated marked this pull request as ready for review November 3, 2026 19:40
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Walkthrough

Operand lowering now instantiates function signatures with erased bound regions before checking the receiver and processing arguments. The Buf fixture adds and exercises a skip operation, with corresponding expected TypeScript updates. The translation notes replace the panic document with a proposal to add moq-net translation to CI; this change does not add the proposed gate.

Merge Risk: Minimal · up to b9cfe

No merge-blocking issue is established. The signature fix and regression fixture appear consistent; moq-net CI coverage remains planned follow-up work.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly describes the primary fix: erasing late-bound regions before inspecting a called function's inputs.
Description check Passed The description directly explains the panic, root cause, implementation, fixture coverage, testing, and follow-up work.
Docstring Coverage Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentati…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 5 functions across 6 files. (2 skipped: 2 unsupported.)

Finishing Touches
Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2453b816-b061-422e-b441-a1c05f7cd510
Commits

Reviewing files that changed from the base of the PR and between aff50d0 and b9cfe51.

Files selected for processing: 9
  • quest/m0/README.md
  • quest/m0/ts-moq-dyn-panic.md
  • quest/m0/ts-moq-gate.md
  • ts/fixtures/stock/expected/buf.Leased.ts
  • ts/fixtures/stock/expected/buf.Pieces.ts
  • ts/fixtures/stock/expected/buf.ts
  • ts/fixtures/stock/expected/lib.ts
  • ts/fixtures/stock/src/buf.rs
  • ts/src/lower/copy.rs
Files with no reviewable changes: 1
  • quest/m0/ts-moq-dyn-panic.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ 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 of skip_binder(), so a callee like moq-net's ErasedSend::poll_write_buf no longer hands &mut dyn Buf's late-bound regions to the is_sized query in replace. This matches the same shape in ts/src/lower/iter.rs:1658.
  • Fixture coverage: the stock fixture's Leased trait gains skip(&mut self, other: &mut dyn Buf) called through the Box<dyn Leased> in dyns, with snapshot and differential regressions under ts/fixtures/stock/.
  • Quest tree: ts-moq-dyn-panic.md is deleted as finished, and the new ts-moq-gate.md ([S]) records gating moq-net's translation in just ci behind a CPU measurement against the checks budget, linked from quest/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)

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
/// 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.

@kixelated
kixelated enabled auto-merge November 3, 2026 19:52
@kixelated
kixelated added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 8cd0281 Nov 3, 2026
5 checks passed
@kixelated
kixelated deleted the quest/m0/ts-moq-dyn-panic branch November 3, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant