@@ -1387,18 +1387,16 @@ impl Compiler {
13871387 }
13881388
13891389 FBlockType :: FinallyEnd => {
1390- // Stack when in FinallyEnd: [..., prev_exc, exc] or
1391- // [..., prev_exc, exc, return_value] if preserve_tos
1392- // Note: No lasti here - it's only pushed for cleanup handler exceptions
1393- // We need to pop: exc, prev_exc (via PopExcept)
1390+ // codegen_unwind_fblock(FINALLY_END)
13941391 if preserve_tos {
13951392 emit ! ( self , Instruction :: Swap { index: 2 } ) ;
13961393 }
1397- emit ! ( self , Instruction :: PopTop ) ; // exc
1394+ emit ! ( self , Instruction :: PopTop ) ; // exc_value
13981395 if preserve_tos {
13991396 emit ! ( self , Instruction :: Swap { index: 2 } ) ;
14001397 }
1401- emit ! ( self , Instruction :: PopExcept ) ; // prev_exc is restored
1398+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
1399+ emit ! ( self , Instruction :: PopExcept ) ;
14021400 }
14031401
14041402 FBlockType :: With | FBlockType :: AsyncWith => {
@@ -1435,9 +1433,16 @@ impl Compiler {
14351433 }
14361434
14371435 FBlockType :: HandlerCleanup => {
1436+ // codegen_unwind_fblock(HANDLER_CLEANUP)
1437+ if let FBlockDatum :: ExceptionName ( _) = info. fb_datum {
1438+ // Named handler: PopBlock for inner SETUP_CLEANUP
1439+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
1440+ }
14381441 if preserve_tos {
14391442 emit ! ( self , Instruction :: Swap { index: 2 } ) ;
14401443 }
1444+ // PopBlock for outer SETUP_CLEANUP (ExceptionHandler)
1445+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
14411446 emit ! ( self , Instruction :: PopExcept ) ;
14421447
14431448 // If there's an exception name, clean it up
@@ -1511,6 +1516,9 @@ impl Compiler {
15111516 self . unwind_fblock ( & fblock_info, preserve_tos) ?;
15121517 }
15131518 UnwindInfo :: FinallyTry { body, fblock_idx } => {
1519+ // codegen_unwind_fblock(FINALLY_TRY)
1520+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
1521+
15141522 // Temporarily remove the FinallyTry fblock so nested return/break/continue
15151523 // in the finally body won't see it again
15161524 let code = self . current_code_info ( ) ;
@@ -2243,6 +2251,10 @@ impl Compiler {
22432251 } ;
22442252 self . set_source_range ( * range) ;
22452253 emit ! ( self , Instruction :: RaiseVarargs { kind } ) ;
2254+ // Start a new block so dead code after raise doesn't
2255+ // corrupt the except stack in label_exception_targets
2256+ let dead = self . new_block ( ) ;
2257+ self . switch_to_block ( dead) ;
22462258 }
22472259 ast:: Stmt :: Try ( ast:: StmtTry {
22482260 body,
@@ -2331,10 +2343,14 @@ impl Compiler {
23312343 ast:: Stmt :: Break ( _) => {
23322344 // Unwind fblock stack until we find a loop, emitting cleanup for each fblock
23332345 self . compile_break_continue ( statement. range ( ) , true ) ?;
2346+ let dead = self . new_block ( ) ;
2347+ self . switch_to_block ( dead) ;
23342348 }
23352349 ast:: Stmt :: Continue ( _) => {
23362350 // Unwind fblock stack until we find a loop, emitting cleanup for each fblock
23372351 self . compile_break_continue ( statement. range ( ) , false ) ?;
2352+ let dead = self . new_block ( ) ;
2353+ self . switch_to_block ( dead) ;
23382354 }
23392355 ast:: Stmt :: Return ( ast:: StmtReturn { value, .. } ) => {
23402356 if !self . ctx . in_func ( ) {
@@ -2367,6 +2383,8 @@ impl Compiler {
23672383 self . emit_return_const ( ConstantData :: None ) ;
23682384 }
23692385 }
2386+ let dead = self . new_block ( ) ;
2387+ self . switch_to_block ( dead) ;
23702388 }
23712389 ast:: Stmt :: Assign ( ast:: StmtAssign { targets, value, .. } ) => {
23722390 self . compile_expression ( value) ?;
@@ -2801,15 +2819,12 @@ impl Compiler {
28012819 }
28022820
28032821 self . switch_to_block ( finally_except) ;
2804- // PUSH_EXC_INFO first, THEN push FinallyEnd fblock
2805- // Stack after unwind (no lasti): [exc] (depth = current_depth + 1)
2806- // Stack after PUSH_EXC_INFO: [prev_exc, exc] (depth = current_depth + 2)
2807- emit ! ( self , Instruction :: PushExcInfo ) ;
2822+ // SETUP_CLEANUP before PUSH_EXC_INFO
28082823 if let Some ( cleanup) = finally_cleanup_block {
2809- // FinallyEnd fblock must be pushed AFTER PUSH_EXC_INFO
2810- // Depth = current_depth + 1 (only prev_exc remains after RERAISE pops exc)
2811- // Exception table: L4 to L5 -> L6 [2] lasti (cleanup handler DOES push lasti)
28122824 emit ! ( self , PseudoInstruction :: SetupCleanup { target: cleanup } ) ;
2825+ }
2826+ emit ! ( self , Instruction :: PushExcInfo ) ;
2827+ if let Some ( cleanup) = finally_cleanup_block {
28132828 self . push_fblock ( FBlockType :: FinallyEnd , cleanup, cleanup) ?;
28142829 }
28152830 self . compile_statements ( finalbody) ?;
@@ -3003,6 +3018,13 @@ impl Compiler {
30033018 self . compile_name ( alias. as_str ( ) , NameUsage :: Delete ) ?;
30043019 }
30053020
3021+ // Pop FinallyTry block before jumping to finally body.
3022+ // The else_block path also pops this; both paths must agree
3023+ // on the except stack when entering finally_block.
3024+ if !finalbody. is_empty ( ) {
3025+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
3026+ }
3027+
30063028 // Jump to finally block
30073029 emit ! (
30083030 self ,
@@ -3172,7 +3194,12 @@ impl Compiler {
31723194 let end_block = self . new_block ( ) ;
31733195 let reraise_star_block = self . new_block ( ) ;
31743196 let reraise_block = self . new_block ( ) ;
3175- let _cleanup_block = self . new_block ( ) ;
3197+ let finally_cleanup_block = if !finalbody. is_empty ( ) {
3198+ Some ( self . new_block ( ) )
3199+ } else {
3200+ None
3201+ } ;
3202+ let exit_block = self . new_block ( ) ;
31763203
31773204 // Push fblock with handler info for exception table generation
31783205 if !finalbody. is_empty ( ) {
@@ -3182,7 +3209,12 @@ impl Compiler {
31823209 target: finally_block
31833210 }
31843211 ) ;
3185- self . push_fblock ( FBlockType :: FinallyTry , finally_block, finally_block) ?;
3212+ self . push_fblock_full (
3213+ FBlockType :: FinallyTry ,
3214+ finally_block,
3215+ finally_block,
3216+ FBlockDatum :: FinallyBody ( finalbody. to_vec ( ) ) ,
3217+ ) ?;
31863218 }
31873219
31883220 // SETUP_FINALLY for try body
@@ -3211,7 +3243,26 @@ impl Compiler {
32113243 let eg_dummy2 = self . new_block ( ) ;
32123244 self . push_fblock ( FBlockType :: ExceptionGroupHandler , eg_dummy1, eg_dummy2) ?;
32133245
3246+ // Initialize handler stack before the loop
3247+ // BUILD_LIST 0 + COPY 2 to set up [prev_exc, orig, list, rest]
3248+ emit ! ( self , Instruction :: BuildList { size: 0 } ) ;
3249+ // Stack: [prev_exc, exc, []]
3250+ emit ! ( self , Instruction :: Copy { index: 2 } ) ;
3251+ // Stack: [prev_exc, orig, list, rest]
3252+
32143253 let n = handlers. len ( ) ;
3254+ if n == 0 {
3255+ // Empty handlers (invalid AST) - append rest to list and proceed
3256+ // Stack: [prev_exc, orig, list, rest]
3257+ emit ! ( self , Instruction :: ListAppend { i: 0 } ) ;
3258+ // Stack: [prev_exc, orig, list]
3259+ emit ! (
3260+ self ,
3261+ PseudoInstruction :: Jump {
3262+ target: reraise_star_block
3263+ }
3264+ ) ;
3265+ }
32153266 for ( i, handler) in handlers. iter ( ) . enumerate ( ) {
32163267 let ast:: ExceptHandler :: ExceptHandler ( ast:: ExceptHandlerExceptHandler {
32173268 type_,
@@ -3223,17 +3274,6 @@ impl Compiler {
32233274 let no_match_block = self . new_block ( ) ;
32243275 let next_block = self . new_block ( ) ;
32253276
3226- // first handler creates list and copies exc
3227- if i == 0 {
3228- // ADDOP_I(c, loc, BUILD_LIST, 0);
3229- emit ! ( self , Instruction :: BuildList { size: 0 } ) ;
3230- // Stack: [prev_exc, exc, []]
3231- // ADDOP_I(c, loc, COPY, 2);
3232- emit ! ( self , Instruction :: Copy { index: 2 } ) ;
3233- // Stack: [prev_exc, exc, [], exc_copy]
3234- // Now stack is: [prev_exc, orig, list, rest]
3235- }
3236-
32373277 // Compile exception type
32383278 if let Some ( exc_type) = type_ {
32393279 // Check for unparenthesized tuple
@@ -3438,7 +3478,12 @@ impl Compiler {
34383478 target: finally_block
34393479 }
34403480 ) ;
3441- self . push_fblock ( FBlockType :: FinallyTry , finally_block, finally_block) ?;
3481+ self . push_fblock_full (
3482+ FBlockType :: FinallyTry ,
3483+ finally_block,
3484+ finally_block,
3485+ FBlockDatum :: FinallyBody ( finalbody. to_vec ( ) ) ,
3486+ ) ?;
34423487 }
34433488 self . switch_to_block ( else_block) ;
34443489 self . compile_statements ( orelse) ?;
@@ -3453,11 +3498,60 @@ impl Compiler {
34533498
34543499 self . switch_to_block ( end_block) ;
34553500 if !finalbody. is_empty ( ) {
3501+ // Snapshot sub_tables before first finally compilation
3502+ let sub_table_cursor = self . symbol_table_stack . last ( ) . map ( |t| t. next_sub_table ) ;
3503+
3504+ // Compile finally body inline for normal path
3505+ self . compile_statements ( finalbody) ?;
3506+ emit ! ( self , PseudoInstruction :: Jump { target: exit_block } ) ;
3507+
3508+ // Restore sub_tables for exception path compilation
3509+ if let Some ( cursor) = sub_table_cursor
3510+ && let Some ( current_table) = self . symbol_table_stack . last_mut ( )
3511+ {
3512+ current_table. next_sub_table = cursor;
3513+ }
3514+
3515+ // Exception handler path
34563516 self . switch_to_block ( finally_block) ;
3517+ emit ! ( self , Instruction :: PushExcInfo ) ;
3518+
3519+ if let Some ( cleanup) = finally_cleanup_block {
3520+ emit ! ( self , PseudoInstruction :: SetupCleanup { target: cleanup } ) ;
3521+ self . push_fblock ( FBlockType :: FinallyEnd , cleanup, cleanup) ?;
3522+ }
3523+
34573524 self . compile_statements ( finalbody) ?;
3458- // No EndFinally emit - exception table handles this
3525+
3526+ if finally_cleanup_block. is_some ( ) {
3527+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
3528+ self . pop_fblock ( FBlockType :: FinallyEnd ) ;
3529+ }
3530+
3531+ emit ! ( self , Instruction :: Copy { index: 2_u32 } ) ;
3532+ emit ! ( self , Instruction :: PopExcept ) ;
3533+ emit ! (
3534+ self ,
3535+ Instruction :: RaiseVarargs {
3536+ kind: bytecode:: RaiseKind :: ReraiseFromStack
3537+ }
3538+ ) ;
3539+
3540+ if let Some ( cleanup) = finally_cleanup_block {
3541+ self . switch_to_block ( cleanup) ;
3542+ emit ! ( self , Instruction :: Copy { index: 3_u32 } ) ;
3543+ emit ! ( self , Instruction :: PopExcept ) ;
3544+ emit ! (
3545+ self ,
3546+ Instruction :: RaiseVarargs {
3547+ kind: bytecode:: RaiseKind :: ReraiseFromStack
3548+ }
3549+ ) ;
3550+ }
34593551 }
34603552
3553+ self . switch_to_block ( exit_block) ;
3554+
34613555 Ok ( ( ) )
34623556 }
34633557
@@ -4866,16 +4960,19 @@ impl Compiler {
48664960
48674961 self . switch_to_block ( for_block) ;
48684962
4869- // Push fblock for async for loop
4870- // Note: SetupExcept is no longer emitted (exception table handles StopAsyncIteration)
4871- emit ! ( self , PseudoInstruction :: SetupFinally { target: else_block } ) ;
4963+ // codegen_async_for: push fblock BEFORE SETUP_FINALLY
48724964 self . push_fblock ( FBlockType :: ForLoop , for_block, after_block) ?;
48734965
4966+ // SETUP_FINALLY to guard the __anext__ call
4967+ emit ! ( self , PseudoInstruction :: SetupFinally { target: else_block } ) ;
48744968 emit ! ( self , Instruction :: GetANext ) ;
48754969 self . emit_load_const ( ConstantData :: None ) ;
48764970 self . compile_yield_from_sequence ( true ) ?;
4971+ // POP_BLOCK for SETUP_FINALLY - only GetANext/yield_from are protected
4972+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
4973+
4974+ // Success block for __anext__
48774975 self . compile_store ( target) ?;
4878- // Note: PopBlock is no longer emitted (exception table handles this)
48794976 } else {
48804977 // Retrieve Iterator
48814978 emit ! ( self , Instruction :: GetIter ) ;
@@ -4898,10 +4995,8 @@ impl Compiler {
48984995
48994996 self . switch_to_block ( else_block) ;
49004997
4901- // Pop fblock (PopBlock only for async for which has SETUP_FINALLY)
4902- if is_async {
4903- emit ! ( self , PseudoInstruction :: PopBlock ) ;
4904- }
4998+ // Except block for __anext__ / end of sync for
4999+ // No PopBlock here - for async, POP_BLOCK is already in for_block
49055000 self . pop_fblock ( FBlockType :: ForLoop ) ;
49065001
49075002 if is_async {
@@ -7798,7 +7893,10 @@ impl Compiler {
77987893 With {
77997894 is_async : bool ,
78007895 } ,
7801- HandlerCleanup ,
7896+ HandlerCleanup {
7897+ name : Option < String > ,
7898+ } ,
7899+ TryExcept ,
78027900 FinallyTry {
78037901 body : Vec < ruff_python_ast:: Stmt > ,
78047902 fblock_idx : usize ,
@@ -7819,7 +7917,14 @@ impl Compiler {
78197917 unwind_actions. push ( UnwindAction :: With { is_async : true } ) ;
78207918 }
78217919 FBlockType :: HandlerCleanup => {
7822- unwind_actions. push ( UnwindAction :: HandlerCleanup ) ;
7920+ let name = match & code. fblock [ i] . fb_datum {
7921+ FBlockDatum :: ExceptionName ( name) => Some ( name. clone ( ) ) ,
7922+ _ => None ,
7923+ } ;
7924+ unwind_actions. push ( UnwindAction :: HandlerCleanup { name } ) ;
7925+ }
7926+ FBlockType :: TryExcept => {
7927+ unwind_actions. push ( UnwindAction :: TryExcept ) ;
78237928 }
78247929 FBlockType :: FinallyTry => {
78257930 // Need to execute finally body before break/continue
@@ -7847,6 +7952,8 @@ impl Compiler {
78477952 for action in unwind_actions {
78487953 match action {
78497954 UnwindAction :: With { is_async } => {
7955+ // codegen_unwind_fblock(WITH/ASYNC_WITH)
7956+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
78507957 // compiler_call_exit_with_nones
78517958 emit ! ( self , Instruction :: PushNull ) ;
78527959 self . emit_load_const ( ConstantData :: None ) ;
@@ -7862,10 +7969,29 @@ impl Compiler {
78627969
78637970 emit ! ( self , Instruction :: PopTop ) ;
78647971 }
7865- UnwindAction :: HandlerCleanup => {
7972+ UnwindAction :: HandlerCleanup { ref name } => {
7973+ // codegen_unwind_fblock(HANDLER_CLEANUP)
7974+ if name. is_some ( ) {
7975+ // Named handler: PopBlock for inner SETUP_CLEANUP
7976+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
7977+ }
7978+ // PopBlock for outer SETUP_CLEANUP (ExceptionHandler)
7979+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
78667980 emit ! ( self , Instruction :: PopExcept ) ;
7981+ if let Some ( name) = name {
7982+ self . emit_load_const ( ConstantData :: None ) ;
7983+ self . store_name ( name) ?;
7984+ self . compile_name ( name, NameUsage :: Delete ) ?;
7985+ }
7986+ }
7987+ UnwindAction :: TryExcept => {
7988+ // codegen_unwind_fblock(TRY_EXCEPT)
7989+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
78677990 }
78687991 UnwindAction :: FinallyTry { body, fblock_idx } => {
7992+ // codegen_unwind_fblock(FINALLY_TRY)
7993+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
7994+
78697995 // compile finally body inline
78707996 // Temporarily pop the FinallyTry fblock so nested break/continue
78717997 // in the finally body won't see it again.
@@ -7880,11 +8006,10 @@ impl Compiler {
78808006 code. fblock . insert ( fblock_idx, saved_fblock) ;
78818007 }
78828008 UnwindAction :: FinallyEnd => {
7883- // Stack when in FinallyEnd: [..., prev_exc, exc]
7884- // Note: No lasti here - it's only pushed for cleanup handler exceptions
7885- // We need to pop: exc, prev_exc (via PopExcept)
7886- emit ! ( self , Instruction :: PopTop ) ; // exc
7887- emit ! ( self , Instruction :: PopExcept ) ; // prev_exc is restored
8009+ // codegen_unwind_fblock(FINALLY_END)
8010+ emit ! ( self , Instruction :: PopTop ) ; // exc_value
8011+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
8012+ emit ! ( self , Instruction :: PopExcept ) ;
78888013 }
78898014 UnwindAction :: PopValue => {
78908015 // Pop the return value - continue/break cancels the pending return
0 commit comments