Align nested code object bytecode parity#7942
Conversation
📝 WalkthroughWalkthroughThis PR adds AST-tail predicates and tuple-nonfolding, wires those predicates into finally/try/orelse/while/for/match codegen preservation and borrow decisions, introduces a per-instruction for-loop break-cleanup flag and IR guards to prevent unsafe rewrites, refines jump sizing/duplication logic, and adds tests plus stable constant normalization for disassembly. ChangesTry/except control-flow and for-break pattern handling
IR-level flags and control-flow rewrite guards
Symbol table and tooling
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/codegen/src/ir.rs`:
- Around line 8625-8630: The current guard uses chain_start directly but must
check the first non-empty successor before calling is_for_break_cleanup_block to
avoid false negatives when empty label blocks sit between the conditional and
the real chain body; update the condition so that instead of
is_for_break_cleanup_block(blocks, chain_start) you compute the first non-empty
block by walking successors from chain_start (skipping empty/label-only blocks)
and pass that block to is_for_break_cleanup_block (keep the other checks:
is_generic_false_path_reorder and jump_targets_for_iter(blocks, jump_block)
unchanged).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 03818ab4-fb70-4ef1-a405-b00f6b23538e
📒 Files selected for processing (2)
crates/codegen/src/compile.rscrates/codegen/src/ir.rs
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
crates/codegen/src/ir.rs (1)
8757-8760: ⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse the first non-empty chain block when checking the for-break cleanup guard.
Line 8759 still checks
chain_startdirectly. If empty label blocks appear before the realPopTop; JUMP_*cleanup block, this guard is bypassed and the reorder can still fire.Suggested fix
- if is_generic_false_path_reorder - && jump_targets_for_iter(blocks, jump_block) - && is_for_break_cleanup_block(blocks, chain_start) + if is_generic_false_path_reorder + && jump_targets_for_iter(blocks, jump_block) + && is_for_break_cleanup_block(blocks, next_nonempty_block(blocks, chain_start)) { current = next; continue; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/codegen/src/ir.rs` around lines 8757 - 8760, The check uses chain_start directly which can be an empty/label block, letting the for-break-cleanup guard be bypassed; update the condition so that instead of passing chain_start into is_for_break_cleanup_block, walk the chain from chain_start to find the first non-empty block (skip empty/label-only blocks) and pass that block into is_for_break_cleanup_block while keeping the existing jump_targets_for_iter and is_generic_false_path_reorder checks intact; update references around is_generic_false_path_reorder, jump_targets_for_iter(blocks, jump_block), and is_for_break_cleanup_block(...) to use the located non-empty chain block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/codegen/src/compile.rs`:
- Around line 879-913: The visitor in statements_contain_async_comprehension
misses generator expressions: update AsyncComprehensionVisitor::visit_expr to
also match ast::Expr::Generator(ast::ExprGenerator { generators, .. }) and check
generators.iter().any(|g| g.is_async) so async generator expressions set
found=true; this ensures the predicate used by compile_match_inner (which
controls emitting PseudoInstruction::JumpNoInterrupt) detects async genexps the
same way as ListComp/SetComp/DictComp. If omission was intentional, add a
clarifying comment near statements_contain_async_comprehension explaining why
ast::Expr::Generator is excluded.
---
Duplicate comments:
In `@crates/codegen/src/ir.rs`:
- Around line 8757-8760: The check uses chain_start directly which can be an
empty/label block, letting the for-break-cleanup guard be bypassed; update the
condition so that instead of passing chain_start into
is_for_break_cleanup_block, walk the chain from chain_start to find the first
non-empty block (skip empty/label-only blocks) and pass that block into
is_for_break_cleanup_block while keeping the existing jump_targets_for_iter and
is_generic_false_path_reorder checks intact; update references around
is_generic_false_path_reorder, jump_targets_for_iter(blocks, jump_block), and
is_for_break_cleanup_block(...) to use the located non-empty chain block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: bf1f7bdc-885b-4ca7-b0f4-7f4967682d0f
📒 Files selected for processing (3)
crates/codegen/src/compile.rscrates/codegen/src/ir.rscrates/codegen/src/symboltable.rs
Sorry, something went wrong.
a91674d to
4d19a8c
Compare
May 21, 2026 16:43
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: [x] lib: cpython/Lib/pyclbr.py dependencies:
dependent tests: (1 tests)
Legend:
|
Sorry, something went wrong.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
crates/codegen/src/ir.rs (1)
8796-8801: ⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse the first non-empty chain block in this for-break guard.
chain_startis still the raw layout successor here. If there’s an empty label block before the realPopTop; JUMP_*cleanup block, this guard misses the for-break shape and the reorder still fires.Suggested fix
if is_generic_false_path_reorder && jump_targets_for_iter(blocks, jump_block) - && is_for_break_cleanup_block(blocks, chain_start) + && is_for_break_cleanup_block( + blocks, + next_nonempty_block(blocks, chain_start), + ) { current = next; continue; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/codegen/src/ir.rs` around lines 8796 - 8801, The guard uses chain_start (the raw layout successor) which can be an empty label block and thus misses the for-break cleanup pattern; change the check so you first walk from chain_start to the first non-empty chain block (skip empty/label-only blocks) and pass that resolved block into is_for_break_cleanup_block(blocks, resolved_chain_start) (keeping the surrounding conditions using is_generic_false_path_reorder, jump_targets_for_iter(blocks, jump_block), and the same current/next control flow), so the for-break shape is detected even when there's an intervening empty label block.crates/codegen/src/compile.rs (1)
895-903: ⚠️ Potential issue | 🟡 MinorHandle async generator expressions in this visitor too.
This still skips
ast::Expr::Generator, so a match arm body containing an async genexpr can leavebody_contains_async_comprehensionfalse and take the plainJumppath instead ofJumpNoInterrupt.Proposed fix
match expr { ast::Expr::ListComp(ast::ExprListComp { generators, .. }) | ast::Expr::SetComp(ast::ExprSetComp { generators, .. }) | ast::Expr::DictComp(ast::ExprDictComp { generators, .. }) + | ast::Expr::Generator(ast::ExprGenerator { generators, .. }) if generators.iter().any(|generator| generator.is_async) => { self.found = true; } _ => ast::visitor::walk_expr(self, expr),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/codegen/src/compile.rs` around lines 895 - 903, The visitor currently marks async comprehensions when matching ListComp/SetComp/DictComp but misses ast::Expr::Generator, so add a match arm for ast::Expr::Generator (or include it in the existing pattern) and set self.found = true when its generator(s) are async; update the match in compile.rs that currently checks generators.iter().any(|generator| generator.is_async) to also handle ast::Expr::Generator so body_contains_async_comprehension becomes true and the code chooses JumpNoInterrupt instead of Jump.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@crates/codegen/src/compile.rs`:
- Around line 895-903: The visitor currently marks async comprehensions when
matching ListComp/SetComp/DictComp but misses ast::Expr::Generator, so add a
match arm for ast::Expr::Generator (or include it in the existing pattern) and
set self.found = true when its generator(s) are async; update the match in
compile.rs that currently checks generators.iter().any(|generator|
generator.is_async) to also handle ast::Expr::Generator so
body_contains_async_comprehension becomes true and the code chooses
JumpNoInterrupt instead of Jump.
In `@crates/codegen/src/ir.rs`:
- Around line 8796-8801: The guard uses chain_start (the raw layout successor)
which can be an empty label block and thus misses the for-break cleanup pattern;
change the check so you first walk from chain_start to the first non-empty chain
block (skip empty/label-only blocks) and pass that resolved block into
is_for_break_cleanup_block(blocks, resolved_chain_start) (keeping the
surrounding conditions using is_generic_false_path_reorder,
jump_targets_for_iter(blocks, jump_block), and the same current/next control
flow), so the for-break shape is detected even when there's an intervening empty
label block.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 45f2ac17-9cf9-4f7a-a1be-fa0cf60bf8ea
⛔ Files ignored due to path filters (1)
Lib/test/test_pyclbr.pyis excluded by!Lib/**
📒 Files selected for processing (4)
crates/codegen/src/compile.rscrates/codegen/src/ir.rscrates/codegen/src/symboltable.rsscripts/dis_dump.py
Sorry, something went wrong.
06f73f2
into
RustPython:main
May 21, 2026
Summary by CodeRabbit
Bug Fixes
Tests
Tooling
Chores