-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Implement more except* #6545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Implement more except* #6545
Conversation
📝 WalkthroughWalkthroughImplements except* support across compiler and VM: adds SetExcInfo bytecode and frame handling, reworks codegen for multi-block except*/reraise control flow with validation, and enhances VM exception-group matching, projection, and reraise helpers. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Codegen as Codegen (compile.rs)
participant BC as Bytecode (bytecode.rs)
participant Frame as VM Frame (frame.rs)
participant Exc as Exceptions (exceptions.rs)
rect rgb(220, 235, 255)
Note over Codegen,BC: Compilation of except*
Codegen->>Codegen: Validate except* (no break/continue/return)
Codegen->>BC: Emit matching/handler blocks, SetExcInfo, PrepReraiseStar
end
rect rgb(220, 255, 220)
Note over Frame,Exc: Runtime handling
Frame->>Frame: Execute handler entry (SetExcInfo sets sys.exc_info)
Frame->>Exc: Call exception_group_match / prep_reraise_star
Exc->>Exc: Validate types, split/project exceptions, build reraise result
Exc-->>Frame: Return matched/unmatched (possibly ExceptionGroup)
Frame->>Frame: Continue to else/finally or reraise path
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ 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 |
8a2161b to
ea4e001
Compare
.github/copilot-instructions.md
Outdated
| When you modify bytecode instructions, a full clean is required: | ||
|
|
||
| ```bash | ||
| rm -rf target/debug/build/rustpython-* && find . | grep -E "(__pycache__|\.pyc$)" | xargs rm -rf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| rm -rf target/debug/build/rustpython-* && find . | grep -E "(__pycache__|\.pyc$)" | xargs rm -rf | |
| rm -r target/debug/build/rustpython-* && find . | grep -E "(__pycache__|\.pyc$)" | xargs rm -r |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/vm/src/exceptions.rs (1)
2538-2604:prep_reraise_starleaf‑ID projection matches the design; update thederive()commentThe revamped
prep_reraise_starlooks robust:
- Handles the non‑group “naked exception” case early, returning the first non‑
Noneentry.- Classifies items in
excsinto “raised” vs “reraised” by walking exception‑group leaves and comparing object IDs, which is a stable way to track identity throughsplit()/ restructuring.- Uses
exception_group_projection+split_by_leaf_idsto project reraised leaves back onto the original group’s shape, and then combines that result with any newly raised exceptions, returning either a single exception or a newExceptionGroupas appropriate.- All paths handle
Nonesafely and short‑circuit when there’s nothing to reraise.One small inconsistency is the comment in
split_by_leaf_ids:// Reconstruct using derive() to preserve the exception group typePer the existing design in
exception_group::types::PyBaseExceptionGroup::derive, the helper intentionally always constructs aBaseExceptionGrouprather than preserving the original subclass type, so this comment is misleading for future readers. Consider rephrasing to something like “reconstruct usingderive()to preserve the structure (but not necessarily the concrete group subclass).”Based on learnings,
derive()is intentionally not type‑preserving.Also applies to: 2606-2712
🧹 Nitpick comments (2)
crates/vm/src/exceptions.rs (1)
2436-2468: except match-type validation andsplit()contract look sound*The new
check_except_star_type_validhelper and its use inexception_group_matchcorrectly enforce:
- match types must derive from
BaseException, including tuple elements.ExceptionGroup/BaseExceptionGroupare rejected forexcept*in favor of plainexcept.The added runtime checks around
exc_value.split(match_type)(type must betuple, length at least 2) are defensive and prevent misbehaving custom groups from corrupting control flow. Only minor nit: the error message says “2‑tuple” but the code only rejects tuples with length< 2; not wrong in practice since builtinExceptionGroup.splitalways returns length‑2, but could be tightened if you want the contract to be exact.Also applies to: 2484-2486, 2514-2519
crates/vm/src/frame.rs (1)
4-5: VM handling ofSetExcInfocorrectly updates the active exceptionThe new arm in
execute_instruction:
- Peeks at TOS without popping and, when it is a
PyBaseException, sets it as the current exception viavm.set_exception(Some(exc.to_owned())).- No‑ops cleanly when TOS is not an exception, which keeps the VM stable even if codegen misfires.
Behavior is correct for except* handlers that want bare
raiseto re‑raise the matched exception. If you’d like stricter debugging, you could add adebug_assert!(exc.downcast_ref::<PyBaseException>().is_some())around these sites to catch invalidSetExcInfoemissions during development, but it’s not required for correctness.Also applies to: 1358-1365
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
.github/copilot-instructions.mdcrates/codegen/src/compile.rscrates/codegen/src/error.rscrates/compiler-core/src/bytecode.rscrates/vm/src/exceptions.rscrates/vm/src/frame.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.rs: Follow the default rustfmt code style by runningcargo fmtto format Rust code
Always run clippy to lint Rust code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass,pymodule,pyfunction, etc.) when implementing Python functionality in Rust
Files:
crates/codegen/src/error.rscrates/codegen/src/compile.rscrates/vm/src/frame.rscrates/compiler-core/src/bytecode.rscrates/vm/src/exceptions.rs
🧠 Learnings (10)
📓 Common learnings
Learnt from: youknowone
Repo: RustPython/RustPython PR: 6358
File: crates/vm/src/exception_group.rs:173-185
Timestamp: 2025-12-09T08:46:58.660Z
Learning: In crates/vm/src/exception_group.rs, the derive() method intentionally always creates a BaseExceptionGroup instance rather than preserving the original exception class type. This is a deliberate design decision that differs from CPython's behavior.
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/test/**/*.py : When modifying test files in `Lib/test/`, use `unittest.skip("TODO: RustPython <reason>")` or `unittest.expectedFailure` with `# TODO: RUSTPYTHON <reason>` comment to mark unsupported features
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Run `cargo test --workspace --exclude rustpython_wasm` to execute Rust unit tests
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/test/**/*.py : Only acceptable modifications to test files are: (1) Removing `unittest.expectedFailure` decorators and upper TODO comments when tests actually pass, (2) Adding `unittest.expectedFailure` decorators when tests cannot be fixed
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/**/*.py : Add a `# TODO: RUSTPYTHON` comment when modifications are made to files in the `Lib/` directory
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/test/**/*.py : NEVER comment out or delete test code lines except for removing `unittest.expectedFailure` decorators and upper TODO comments
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Use `cargo run -- script.py` instead of the standard `python` command to run and test Python code with RustPython
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to **/*.py : In most cases, Python code should not be edited; bug fixes should be made through Rust code modifications only
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/**/*.py : Minimize modifications to CPython standard library files in the `Lib/` directory; modifications should be minimal and only to work around RustPython limitations
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-12-09T08:46:58.660Z
Learnt from: youknowone
Repo: RustPython/RustPython PR: 6358
File: crates/vm/src/exception_group.rs:173-185
Timestamp: 2025-12-09T08:46:58.660Z
Learning: In crates/vm/src/exception_group.rs, the derive() method intentionally always creates a BaseExceptionGroup instance rather than preserving the original exception class type. This is a deliberate design decision that differs from CPython's behavior.
Applied to files:
crates/codegen/src/compile.rscrates/vm/src/frame.rscrates/vm/src/exceptions.rs
🧬 Code graph analysis (3)
crates/vm/src/frame.rs (1)
crates/stdlib/src/openssl.rs (1)
exc(3740-3740)
crates/compiler-core/src/bytecode.rs (1)
crates/vm/src/exceptions.rs (2)
w(1730-1730)w(1731-1731)
crates/vm/src/exceptions.rs (1)
crates/vm/src/exception_group.rs (1)
exception_group(35-40)
🔇 Additional comments (7)
.github/copilot-instructions.md (1)
76-82: Clean-build instructions for bytecode edits are clear and consistentThe added section gives a precise, reproducible clean command for bytecode changes and fits well with the surrounding Lib/ guidance. No issues from a tooling or safety perspective.
crates/codegen/src/error.rs (1)
91-92: NewBreakContinueReturnInExceptStarerror variant is wired correctlyThe enum variant and its
Displayarm are added consistently with existing error types, with a clear diagnostic string that will surface nicely at compile time. No structural or behavioral issues.Also applies to: 153-158
crates/compiler-core/src/bytecode.rs (1)
868-871:SetExcInfoopcode is cleanly integrated into the bytecode model
- Adding
SetExcInfoat the end ofInstructionand updatingLAST_INSTRUCTIONkeeps the marshaling range consistent.stack_effectreturns0, matching the “peek‑only” semantics.- Disassembly uses
SET_EXC_INFO, which aligns with the other mnemonics.No additional wiring issues spotted.
Also applies to: 875-875, 1749-1750, 1982-1983
crates/codegen/src/compile.rs (4)
1583-1617: LGTM: Break statement validation for except blocks.*The control flow correctly validates that
breakcannot occur inside anExceptionGroupHandlerblock before reaching a loop. TheResult<Option<BlockIdx>, ()>approach cleanly handles the three cases: found loop, no loop, and forbidden context.
1620-1657: LGTM: Continue statement validation mirrors break handling.The implementation correctly mirrors the break validation logic, using
fb_block(loop start) for continue vsfb_exit(loop end) for break.
1665-1676: LGTM: Return statement check in except blocks.*Unlike break/continue which need to find a loop first,
returnis unconditionally forbidden anywhere within anExceptionGroupHandler, and this implementation correctly checks for any such handler in the fblock stack.
2156-2426: The except* implementation correctly handles exception group matching, handler execution, exception accumulation, and final reraise. All ListAppend index calculations are correct and properly target the exception results list based on the documented stack states.
ea4e001 to
d8cda04
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
crates/codegen/src/compile.rs (2)
1582-1619: Break inside except now correctly rejected while preserving nested-loop behavior*The new fblock scan for
break:
- Correctly targets the innermost
WhileLoop/ForLoopwhen present, even inside anexcept*handler with inner loops.- Emits
InvalidBreakwhen no loop exists, preserving existing semantics.- Emits
BreakContinueReturnInExceptStarwhen anExceptionGroupHandleris encountered before any loop, which matches the CPython restriction for top‑levelbreakin anexcept*suite.This looks right and integrates cleanly with the existing
FBlockTypeusage. If you like, thefound_looppattern here and inContinuecould be factored into a small helper to avoid duplication, but that’s optional.
1622-1657: Continue inside except is validated symmetrically to break*The
continuehandling mirrors the newbreaklogic:
- Uses the same reverse fblock scan to find the innermost loop block.
- Yields
InvalidContinueoutside any loop.- Produces
BreakContinueReturnInExceptStarwhen the first relevant fblock is anExceptionGroupHandler, while still allowingcontinuein loops nested inside the handler.The symmetry with the
breakimplementation keeps behavior predictable and should match CPython’s constraints onexcept*blocks.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Lib/test/test_except_star.pyis excluded by!Lib/**
📒 Files selected for processing (6)
.github/copilot-instructions.mdcrates/codegen/src/compile.rscrates/codegen/src/error.rscrates/compiler-core/src/bytecode.rscrates/vm/src/exceptions.rscrates/vm/src/frame.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- crates/codegen/src/error.rs
- crates/vm/src/frame.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
**/*.rs: Follow the default rustfmt code style by runningcargo fmtto format Rust code
Always run clippy to lint Rust code (cargo clippy) before completing tasks and fix any warnings or lints introduced by changes
Follow Rust best practices for error handling and memory management
Use the macro system (pyclass,pymodule,pyfunction, etc.) when implementing Python functionality in Rust
Files:
crates/compiler-core/src/bytecode.rscrates/codegen/src/compile.rscrates/vm/src/exceptions.rs
🧠 Learnings (10)
📓 Common learnings
Learnt from: youknowone
Repo: RustPython/RustPython PR: 6358
File: crates/vm/src/exception_group.rs:173-185
Timestamp: 2025-12-09T08:46:58.660Z
Learning: In crates/vm/src/exception_group.rs, the derive() method intentionally always creates a BaseExceptionGroup instance rather than preserving the original exception class type. This is a deliberate design decision that differs from CPython's behavior.
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Run `cargo test --workspace --exclude rustpython_wasm` to execute Rust unit tests
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/test/**/*.py : When modifying test files in `Lib/test/`, use `unittest.skip("TODO: RustPython <reason>")` or `unittest.expectedFailure` with `# TODO: RUSTPYTHON <reason>` comment to mark unsupported features
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/test/**/*.py : Only acceptable modifications to test files are: (1) Removing `unittest.expectedFailure` decorators and upper TODO comments when tests actually pass, (2) Adding `unittest.expectedFailure` decorators when tests cannot be fixed
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/**/*.py : Add a `# TODO: RUSTPYTHON` comment when modifications are made to files in the `Lib/` directory
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/test/**/*.py : NEVER comment out or delete test code lines except for removing `unittest.expectedFailure` decorators and upper TODO comments
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Use `cargo run -- script.py` instead of the standard `python` command to run and test Python code with RustPython
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to **/*.py : In most cases, Python code should not be edited; bug fixes should be made through Rust code modifications only
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-11-29T12:17:28.606Z
Learnt from: CR
Repo: RustPython/RustPython PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T12:17:28.606Z
Learning: Applies to Lib/**/*.py : Minimize modifications to CPython standard library files in the `Lib/` directory; modifications should be minimal and only to work around RustPython limitations
Applied to files:
.github/copilot-instructions.md
📚 Learning: 2025-12-09T08:46:58.660Z
Learnt from: youknowone
Repo: RustPython/RustPython PR: 6358
File: crates/vm/src/exception_group.rs:173-185
Timestamp: 2025-12-09T08:46:58.660Z
Learning: In crates/vm/src/exception_group.rs, the derive() method intentionally always creates a BaseExceptionGroup instance rather than preserving the original exception class type. This is a deliberate design decision that differs from CPython's behavior.
Applied to files:
crates/codegen/src/compile.rscrates/vm/src/exceptions.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: Run snippets and cpython tests on wasm-wasi
- GitHub Check: Run snippets and cpython tests (windows-latest)
- GitHub Check: Run snippets and cpython tests (ubuntu-latest)
- GitHub Check: Run snippets and cpython tests (macos-latest)
- GitHub Check: Run rust tests (windows-latest)
- GitHub Check: Run rust tests (macos-latest)
- GitHub Check: Run tests under miri
- GitHub Check: Run rust tests (ubuntu-latest)
- GitHub Check: Check the WASM package and demo
- GitHub Check: Check Rust code with clippy
- GitHub Check: Ensure compilation on various targets
🔇 Additional comments (14)
crates/vm/src/exceptions.rs (10)
2436-2468: LGTM!The validation logic correctly enforces except* semantics: match types must be BaseException subclasses but cannot be ExceptionGroup/BaseExceptionGroup. Error messages are clear and match Python's behavior.
2484-2485: Good addition of validation.The early validation ensures that except* handlers reject ExceptionGroup types, which correctly implements Python 3.11+ semantics.
2513-2519: Defensive type checking added.This validation ensures that user-defined split() methods return the correct type, catching errors early with a clear message.
2548-2559: Correct handling of edge cases.The early return for empty lists and special handling for naked (non-group) exceptions correctly implements the except* semantics where only one except* clause can execute for a naked exception.
2569-2575: Good use of leaf ID comparison.Using
is_exception_from_origto distinguish raised from reraised exceptions is a robust approach that relies on object identity rather than mutable metadata.
2582-2603: Correct projection and combination logic.The projection preserves the original nested structure for reraised exceptions, and the combination logic correctly handles single vs. multiple exceptions, matching Python's except* behavior.
2606-2624: LGTM!The leaf ID comparison approach correctly identifies reraised exceptions by checking if all of the exception's leaf IDs are present in the original group, relying on the fact that
split()preserves leaf exception identity.
2626-2650: LGTM!The recursive collection correctly identifies leaf exceptions by checking if an exception is not an exception group, and safely traverses nested structures using if-let chains.
2652-2672: LGTM!The projection function correctly collects leaf IDs from the keep list and delegates to
split_by_leaf_idsfor the actual recursive splitting, maintaining a clean separation of concerns.
2674-2712: LGTM!The recursive splitting correctly preserves the nested structure using the
derive()method. Per the learnings,derive()intentionally creates BaseExceptionGroup instances rather than preserving the original exception class type, which is a deliberate design decision.Based on learnings, this design differs from CPython but is intentional in RustPython.
.github/copilot-instructions.md (1)
76-82: Clean-build instructions look good and match prior suggested workflowThe new “Clean Build” snippet is accurate for forcing a rebuild after bytecode instruction changes (wiping rustpython build artifacts and Python bytecode). No issues from a tooling or safety perspective; this should help avoid stale-instruction failures.
crates/compiler-core/src/bytecode.rs (1)
868-876: SetExcInfo opcode is wired consistently (enum, marshaling, stack effect, disassembly)The new
SetExcInfoinstruction is correctly integrated:
- Added as the last
Instructionvariant andLAST_INSTRUCTIONupdated, keepingTryFrom<u8>/marshaling valid.stack_effectreturns0, matching the “does not pop the value” behavior.- Disassembler prints
SET_EXC_INFO, consistent with other op names.No issues spotted here.
Also applies to: 1749-1750, 1982-1983
crates/codegen/src/compile.rs (2)
1665-1676: Return is correctly disallowed anywhere within an except handler frame*The added check for
FBlockType::ExceptionGroupHandlerbefore emitting areturn:
- Ensures
returnis rejected anywhere in the dynamic extent of anexcept*handler (including inside nested loops), which aligns with CPython’s semantics.- Still permits
returnin the try body,else,finally, and in nested functions defined inside anexcept*since those have their ownCodeInfo/fblock stacks.This is a good, minimal way to enforce the restriction using the existing fblock mechanism.
2156-2423: except compilation flow matches CPython’s multi-block design and maintains stack invariants*The reworked
compile_try_star_exceptlooks internally consistent and closely follows CPython’s strategy:
Stack protocol:
- At handler entry,
[exc] → [orig, list, rest]viaBUILD_LIST(0)andCOPY(2), giving a stableorigand mutablerestplus an aggregation list.- For each handler,
CHECK_EG_MATCHtransforms[orig, list, rest, type]into[orig, list, new_rest, match], and all subsequent paths (no match, normal handler completion, and handler raising) end with[orig, list, new_rest], ready for the next handler.- After the last handler,
ListAppend { i: 0 }appends the finalrestto the list, andPrepReraiseStar(orig, list)is invoked with exactly two arguments, returning a singleresult.Handler semantics:
- Unparenthesized tuple types and
except*without a type are rejected with clearSyntaxErrors during codegen.SetExcInfois issued before running the handler body so bareraisere-raises the correctmatch.- Handlers are wrapped in an inner
SetupExceptwith a dedicatedhandler_except_block:
- Normal completion: no new exception;
matchis dropped andNoneis appended withListAppend { i: 1 }.- Raised in handler: the raised exception is appended with
ListAppend { i: 2 }andmatchis dropped.- Name binding (
as name) is cleaned up in both normal and exceptional paths by resetting toNoneand deleting the variable, matching standardexceptbehavior.FBlockType::ExceptionGroupHandleris pushed only around the handler body and popped immediately afterward, givingbreak/continue/returnthe necessary context while not leaking into surrounding code.Reraise / no‑reraise paths:
- The
result is not Nonetest uses identity (is not None) viaIsOp(Invert::Yes), and if true branches to aRaise { kind: Raise }withresultstill on the stack—effectivelyraise result.- When
result is None,resultis popped and the original try* exception is cleaned up withPopException, after which (if afinallyexists) the code performsPopBlock+EnterFinallyand jumps to a sharedend_block.Else / finally integration:
- The try-
elsepath is unchanged structurally: on success,SetupExceptis popped,else_blockis executed, and for a presentfinallywe again usePopBlock+EnterFinallybefore jumping toend_block.end_blockserves as a common join point for “no re‑raise” and “else” paths;finally_blockis chained after it and runsfinalbodyfollowed byEndFinally.Given how subtle these flows are, especially with nested
except*,finally, and different ways of raising (bareraise,raise e, new exceptions from handlers), it’s worth making sure tests cover:
try/except*with and withoutelse/finally,- handlers that both handle and re-raise parts of an exception group,
- bare
raiseinsideexcept*,- and combinations of multiple
except*handlers.Structurally, though, this implementation looks sound and matches the expected stack behavior.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.