Skip to content

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Jan 30, 2026

Summary by CodeRabbit

  • Refactor
    • Simplified exception and unwind handling across the compiler, reducing per-block metadata and streamlining unwind paths.
  • New Features
    • Bytecode/post-generation passes added to better mark and label exception targets and convert pseudo-operations.
    • Pseudo-instructions updated to carry explicit target references and expose helpers to detect block-push/pop operations.
  • Maintenance
    • Improved stack-depth and handler propagation logic for more consistent code generation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Refactors exception handling: removes per-block handler/stack/lasti fields from FBlockInfo and push_fblock APIs, adds post-codegen CFG passes to annotate blocks (except_handler/preserve_lasti/start_depth), and converts Setup* pseudo-instructions to carry explicit target labels.

Changes

Cohort / File(s) Summary
Codegen / fblock API
crates/codegen/src/compile.rs
Removed fb_handler, fb_stack_depth, fb_preserve_lasti from FBlockInfo; simplified push_fblock / push_fblock_full signatures; removed current_except_handler() and related depth/lasti propagation.
IR / CFG passes
crates/codegen/src/ir.rs
Added except_handler, preserve_lasti, start_depth fields to Block; added mark_except_handlers(), label_exception_targets(), convert_pseudo_ops(); integrated these passes into CodeInfo::finalize_code; adjusted stack-depth computation and handler-depth fixups.
Bytecode pseudo-op refactor
crates/compiler-core/src/bytecode/instruction.rs
Changed SetupCleanup, SetupFinally, SetupWith to struct variants with target: Arg<Label>; added is_block_push() on PseudoInstruction and AnyInstruction; added is_pop_block() and updated InstructionMetadata mapping.
Manifest
Cargo.toml
Lines changed (project manifest edits indicated).

Sequence Diagram(s)

sequenceDiagram
    participant Codegen as Codegen (compile.rs)
    participant IR as IR Layer (ir.rs)
    participant CFG as CFG Passes
    participant Bytecode as Bytecode Instrs

    Codegen->>Codegen: Emit blocks (simplified fblocks)
    Codegen->>IR: create CodeInfo / finalize_code
    IR->>CFG: mark_except_handlers(blocks)
    CFG->>CFG: label_exception_targets(blocks)
    CFG->>CFG: convert_pseudo_ops(blocks, varnames_len)
    CFG->>Bytecode: annotate blocks (except_handler, start_depth, preserve_lasti)
    Bytecode->>Bytecode: emit SetupCleanup{target}, SetupFinally{target}, SetupWith{target}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ShaharNaveh

"I hop through code with whiskers bright,
Blocks lose baggage, targets gain light,
Pseudo-ops now carry their map,
CFG hums onward—no more trap,
A rabbit's cheer for tidied byte!" 🐇✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Rework exception compiler' clearly and concisely summarizes the main change: a comprehensive refactor of the exception handling system in the compiler, affecting multiple core files across codegen and bytecode instruction handling.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

Code has been automatically formatted

The code in this PR has been formatted using:

  • cargo fmt --all
    Please pull the latest changes before pushing again:
git pull origin exceptions

@youknowone youknowone marked this pull request as ready for review January 30, 2026 21:40
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/codegen/src/compile.rs`:
- Around line 3178-3186: The code emits a SetupFinally and calls
push_fblock(FBlockType::FinallyTry, ...) but stores FBlockDatum::None so the
finally body is not inlined on unwind; replace the push_fblock call in the try*
path (the block that emits PseudoInstruction::SetupFinally and uses
finally_block) with push_fblock_full and pass
FBlockDatum::FinallyBody(finalbody.to_vec()) so unwind_fblock_stack can inline
finalbody on return/break/continue; apply the same change at the other
occurrence around the lines noted (also where PseudoInstruction::SetupFinally
and FBlockType::FinallyTry are used).
🧹 Nitpick comments (3)
crates/codegen/src/ir.rs (3)

247-256: Consider consolidating NOP removal.

NOPs are removed here after convert_pseudo_ops, but there's also a separate remove_nops() call at Line 195. The early call handles NOPs from constant folding, but this creates a two-phase removal pattern that could be confusing.


1266-1269: POP_BLOCK is converted to NOP twice.

POP_BLOCK is converted to Instruction::Nop here in label_exception_targets (Line 1269) and again in convert_pseudo_ops (Lines 1348-1350). The second conversion is redundant but harmless. The comment on Line 1347 acknowledges this.

Consider removing the redundant case in convert_pseudo_ops since it's already handled:

♻️ Proposed simplification
                 // POP_BLOCK → NOP (should already be NOP from label_exception_targets)
                 PseudoInstruction::PopBlock => {
-                    info.instr = Instruction::Nop.into();
+                    // Already converted to NOP in label_exception_targets
+                    debug_assert!(matches!(info.instr.real(), Some(Instruction::Nop)));
                 }

1360-1367: Unimplemented panic for unexpected pseudo instructions.

Using unimplemented! will cause a panic if these pseudo-ops are encountered. If these represent compiler bugs rather than user errors, consider using unreachable! with a clearer message, or returning an error.

♻️ Proposed change
                 // These should have been resolved earlier
                 PseudoInstruction::AnnotationsPlaceholder
                 | PseudoInstruction::JumpIfFalse { .. }
                 | PseudoInstruction::JumpIfTrue { .. }
                 | PseudoInstruction::StoreFastMaybeNull(_) => {
-                    unimplemented!(
-                        "Unexpected pseudo instruction in convert_pseudo_ops: {pseudo:?}"
-                    )
+                    unreachable!(
+                        "Pseudo instruction should have been resolved earlier: {pseudo:?}"
+                    )
                 }

Comment on lines 3178 to 3186
if !finalbody.is_empty() {
// No SetupFinally emit - exception table handles this
self.push_fblock_with_handler(
FBlockType::FinallyTry,
finally_block,
finally_block,
Some(finally_block),
current_depth, // stack depth for exception handler
true, // preserve lasti for finally
)?;
emit!(
self,
PseudoInstruction::SetupFinally {
target: finally_block
}
);
self.push_fblock(FBlockType::FinallyTry, finally_block, finally_block)?;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

FinallyTry fblocks in try path drop the finally body on unwind.*

push_fblock stores FBlockDatum::None, so unwind_fblock_stack won’t inline finalbody on return/break/continue from try/else paths in except* constructs. Use push_fblock_full with FBlockDatum::FinallyBody(finalbody.to_vec()) in both places.

✅ Suggested fix
-            self.push_fblock(FBlockType::FinallyTry, finally_block, finally_block)?;
+            self.push_fblock_full(
+                FBlockType::FinallyTry,
+                finally_block,
+                finally_block,
+                FBlockDatum::FinallyBody(finalbody.to_vec()),
+            )?;
...
-            self.push_fblock(FBlockType::FinallyTry, finally_block, finally_block)?;
+            self.push_fblock_full(
+                FBlockType::FinallyTry,
+                finally_block,
+                finally_block,
+                FBlockDatum::FinallyBody(finalbody.to_vec()),
+            )?;

Also applies to: 3435-3442

🤖 Prompt for AI Agents
In `@crates/codegen/src/compile.rs` around lines 3178 - 3186, The code emits a
SetupFinally and calls push_fblock(FBlockType::FinallyTry, ...) but stores
FBlockDatum::None so the finally body is not inlined on unwind; replace the
push_fblock call in the try* path (the block that emits
PseudoInstruction::SetupFinally and uses finally_block) with push_fblock_full
and pass FBlockDatum::FinallyBody(finalbody.to_vec()) so unwind_fblock_stack can
inline finalbody on return/break/continue; apply the same change at the other
occurrence around the lines noted (also where PseudoInstruction::SetupFinally
and FBlockType::FinallyTry are used).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant