@@ -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) ?;
@@ -3172,7 +3187,12 @@ impl Compiler {
31723187 let end_block = self . new_block ( ) ;
31733188 let reraise_star_block = self . new_block ( ) ;
31743189 let reraise_block = self . new_block ( ) ;
3175- let _cleanup_block = self . new_block ( ) ;
3190+ let finally_cleanup_block = if !finalbody. is_empty ( ) {
3191+ Some ( self . new_block ( ) )
3192+ } else {
3193+ None
3194+ } ;
3195+ let exit_block = self . new_block ( ) ;
31763196
31773197 // Push fblock with handler info for exception table generation
31783198 if !finalbody. is_empty ( ) {
@@ -3182,7 +3202,12 @@ impl Compiler {
31823202 target: finally_block
31833203 }
31843204 ) ;
3185- self . push_fblock ( FBlockType :: FinallyTry , finally_block, finally_block) ?;
3205+ self . push_fblock_full (
3206+ FBlockType :: FinallyTry ,
3207+ finally_block,
3208+ finally_block,
3209+ FBlockDatum :: FinallyBody ( finalbody. to_vec ( ) ) ,
3210+ ) ?;
31863211 }
31873212
31883213 // SETUP_FINALLY for try body
@@ -3211,7 +3236,26 @@ impl Compiler {
32113236 let eg_dummy2 = self . new_block ( ) ;
32123237 self . push_fblock ( FBlockType :: ExceptionGroupHandler , eg_dummy1, eg_dummy2) ?;
32133238
3239+ // Initialize handler stack before the loop
3240+ // BUILD_LIST 0 + COPY 2 to set up [prev_exc, orig, list, rest]
3241+ emit ! ( self , Instruction :: BuildList { size: 0 } ) ;
3242+ // Stack: [prev_exc, exc, []]
3243+ emit ! ( self , Instruction :: Copy { index: 2 } ) ;
3244+ // Stack: [prev_exc, orig, list, rest]
3245+
32143246 let n = handlers. len ( ) ;
3247+ if n == 0 {
3248+ // Empty handlers (invalid AST) - append rest to list and proceed
3249+ // Stack: [prev_exc, orig, list, rest]
3250+ emit ! ( self , Instruction :: ListAppend { i: 0 } ) ;
3251+ // Stack: [prev_exc, orig, list]
3252+ emit ! (
3253+ self ,
3254+ PseudoInstruction :: Jump {
3255+ target: reraise_star_block
3256+ }
3257+ ) ;
3258+ }
32153259 for ( i, handler) in handlers. iter ( ) . enumerate ( ) {
32163260 let ast:: ExceptHandler :: ExceptHandler ( ast:: ExceptHandlerExceptHandler {
32173261 type_,
@@ -3223,17 +3267,6 @@ impl Compiler {
32233267 let no_match_block = self . new_block ( ) ;
32243268 let next_block = self . new_block ( ) ;
32253269
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-
32373270 // Compile exception type
32383271 if let Some ( exc_type) = type_ {
32393272 // Check for unparenthesized tuple
@@ -3438,7 +3471,12 @@ impl Compiler {
34383471 target: finally_block
34393472 }
34403473 ) ;
3441- self . push_fblock ( FBlockType :: FinallyTry , finally_block, finally_block) ?;
3474+ self . push_fblock_full (
3475+ FBlockType :: FinallyTry ,
3476+ finally_block,
3477+ finally_block,
3478+ FBlockDatum :: FinallyBody ( finalbody. to_vec ( ) ) ,
3479+ ) ?;
34423480 }
34433481 self . switch_to_block ( else_block) ;
34443482 self . compile_statements ( orelse) ?;
@@ -3453,11 +3491,60 @@ impl Compiler {
34533491
34543492 self . switch_to_block ( end_block) ;
34553493 if !finalbody. is_empty ( ) {
3494+ // Snapshot sub_tables before first finally compilation
3495+ let sub_table_cursor = self . symbol_table_stack . last ( ) . map ( |t| t. next_sub_table ) ;
3496+
3497+ // Compile finally body inline for normal path
3498+ self . compile_statements ( finalbody) ?;
3499+ emit ! ( self , PseudoInstruction :: Jump { target: exit_block } ) ;
3500+
3501+ // Restore sub_tables for exception path compilation
3502+ if let Some ( cursor) = sub_table_cursor
3503+ && let Some ( current_table) = self . symbol_table_stack . last_mut ( )
3504+ {
3505+ current_table. next_sub_table = cursor;
3506+ }
3507+
3508+ // Exception handler path
34563509 self . switch_to_block ( finally_block) ;
3510+ emit ! ( self , Instruction :: PushExcInfo ) ;
3511+
3512+ if let Some ( cleanup) = finally_cleanup_block {
3513+ emit ! ( self , PseudoInstruction :: SetupCleanup { target: cleanup } ) ;
3514+ self . push_fblock ( FBlockType :: FinallyEnd , cleanup, cleanup) ?;
3515+ }
3516+
34573517 self . compile_statements ( finalbody) ?;
3458- // No EndFinally emit - exception table handles this
3518+
3519+ if finally_cleanup_block. is_some ( ) {
3520+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
3521+ self . pop_fblock ( FBlockType :: FinallyEnd ) ;
3522+ }
3523+
3524+ emit ! ( self , Instruction :: Copy { index: 2_u32 } ) ;
3525+ emit ! ( self , Instruction :: PopExcept ) ;
3526+ emit ! (
3527+ self ,
3528+ Instruction :: RaiseVarargs {
3529+ kind: bytecode:: RaiseKind :: ReraiseFromStack
3530+ }
3531+ ) ;
3532+
3533+ if let Some ( cleanup) = finally_cleanup_block {
3534+ self . switch_to_block ( cleanup) ;
3535+ emit ! ( self , Instruction :: Copy { index: 3_u32 } ) ;
3536+ emit ! ( self , Instruction :: PopExcept ) ;
3537+ emit ! (
3538+ self ,
3539+ Instruction :: RaiseVarargs {
3540+ kind: bytecode:: RaiseKind :: ReraiseFromStack
3541+ }
3542+ ) ;
3543+ }
34593544 }
34603545
3546+ self . switch_to_block ( exit_block) ;
3547+
34613548 Ok ( ( ) )
34623549 }
34633550
@@ -4866,16 +4953,19 @@ impl Compiler {
48664953
48674954 self . switch_to_block ( for_block) ;
48684955
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 } ) ;
4956+ // codegen_async_for: push fblock BEFORE SETUP_FINALLY
48724957 self . push_fblock ( FBlockType :: ForLoop , for_block, after_block) ?;
48734958
4959+ // SETUP_FINALLY to guard the __anext__ call
4960+ emit ! ( self , PseudoInstruction :: SetupFinally { target: else_block } ) ;
48744961 emit ! ( self , Instruction :: GetANext ) ;
48754962 self . emit_load_const ( ConstantData :: None ) ;
48764963 self . compile_yield_from_sequence ( true ) ?;
4964+ // POP_BLOCK for SETUP_FINALLY - only GetANext/yield_from are protected
4965+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
4966+
4967+ // Success block for __anext__
48774968 self . compile_store ( target) ?;
4878- // Note: PopBlock is no longer emitted (exception table handles this)
48794969 } else {
48804970 // Retrieve Iterator
48814971 emit ! ( self , Instruction :: GetIter ) ;
@@ -4898,10 +4988,8 @@ impl Compiler {
48984988
48994989 self . switch_to_block ( else_block) ;
49004990
4901- // Pop fblock (PopBlock only for async for which has SETUP_FINALLY)
4902- if is_async {
4903- emit ! ( self , PseudoInstruction :: PopBlock ) ;
4904- }
4991+ // Except block for __anext__ / end of sync for
4992+ // No PopBlock here - for async, POP_BLOCK is already in for_block
49054993 self . pop_fblock ( FBlockType :: ForLoop ) ;
49064994
49074995 if is_async {
@@ -7798,7 +7886,10 @@ impl Compiler {
77987886 With {
77997887 is_async : bool ,
78007888 } ,
7801- HandlerCleanup ,
7889+ HandlerCleanup {
7890+ name : Option < String > ,
7891+ } ,
7892+ TryExcept ,
78027893 FinallyTry {
78037894 body : Vec < ruff_python_ast:: Stmt > ,
78047895 fblock_idx : usize ,
@@ -7819,7 +7910,14 @@ impl Compiler {
78197910 unwind_actions. push ( UnwindAction :: With { is_async : true } ) ;
78207911 }
78217912 FBlockType :: HandlerCleanup => {
7822- unwind_actions. push ( UnwindAction :: HandlerCleanup ) ;
7913+ let name = match & code. fblock [ i] . fb_datum {
7914+ FBlockDatum :: ExceptionName ( name) => Some ( name. clone ( ) ) ,
7915+ _ => None ,
7916+ } ;
7917+ unwind_actions. push ( UnwindAction :: HandlerCleanup { name } ) ;
7918+ }
7919+ FBlockType :: TryExcept => {
7920+ unwind_actions. push ( UnwindAction :: TryExcept ) ;
78237921 }
78247922 FBlockType :: FinallyTry => {
78257923 // Need to execute finally body before break/continue
@@ -7847,6 +7945,8 @@ impl Compiler {
78477945 for action in unwind_actions {
78487946 match action {
78497947 UnwindAction :: With { is_async } => {
7948+ // codegen_unwind_fblock(WITH/ASYNC_WITH)
7949+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
78507950 // compiler_call_exit_with_nones
78517951 emit ! ( self , Instruction :: PushNull ) ;
78527952 self . emit_load_const ( ConstantData :: None ) ;
@@ -7862,10 +7962,29 @@ impl Compiler {
78627962
78637963 emit ! ( self , Instruction :: PopTop ) ;
78647964 }
7865- UnwindAction :: HandlerCleanup => {
7965+ UnwindAction :: HandlerCleanup { ref name } => {
7966+ // codegen_unwind_fblock(HANDLER_CLEANUP)
7967+ if name. is_some ( ) {
7968+ // Named handler: PopBlock for inner SETUP_CLEANUP
7969+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
7970+ }
7971+ // PopBlock for outer SETUP_CLEANUP (ExceptionHandler)
7972+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
78667973 emit ! ( self , Instruction :: PopExcept ) ;
7974+ if let Some ( name) = name {
7975+ self . emit_load_const ( ConstantData :: None ) ;
7976+ self . store_name ( name) ?;
7977+ self . compile_name ( name, NameUsage :: Delete ) ?;
7978+ }
7979+ }
7980+ UnwindAction :: TryExcept => {
7981+ // codegen_unwind_fblock(TRY_EXCEPT)
7982+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
78677983 }
78687984 UnwindAction :: FinallyTry { body, fblock_idx } => {
7985+ // codegen_unwind_fblock(FINALLY_TRY)
7986+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
7987+
78697988 // compile finally body inline
78707989 // Temporarily pop the FinallyTry fblock so nested break/continue
78717990 // in the finally body won't see it again.
@@ -7880,11 +7999,10 @@ impl Compiler {
78807999 code. fblock . insert ( fblock_idx, saved_fblock) ;
78818000 }
78828001 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
8002+ // codegen_unwind_fblock(FINALLY_END)
8003+ emit ! ( self , Instruction :: PopTop ) ; // exc_value
8004+ emit ! ( self , PseudoInstruction :: PopBlock ) ;
8005+ emit ! ( self , Instruction :: PopExcept ) ;
78888006 }
78898007 UnwindAction :: PopValue => {
78908008 // Pop the return value - continue/break cancels the pending return
0 commit comments