Skip to content

Commit a0752fd

Browse files
committed
fixing
1 parent d3e0564 commit a0752fd

File tree

3 files changed

+176
-52
lines changed

3 files changed

+176
-52
lines changed

crates/codegen/src/compile.rs

Lines changed: 169 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)