Skip to content

Commit ee4fe41

Browse files
committed
fixsing
1 parent 5293d8e commit ee4fe41

File tree

3 files changed

+169
-52
lines changed

3 files changed

+169
-52
lines changed

crates/codegen/src/compile.rs

Lines changed: 162 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)?;
@@ -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

crates/codegen/src/ir.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,8 @@ pub(crate) fn convert_pseudo_ops(blocks: &mut [Block], varnames_len: u32) {
13421342
| PseudoInstruction::SetupWith { .. } => {
13431343
info.instr = Instruction::Nop.into();
13441344
}
1345-
// POP_BLOCK → NOP (should already be NOP from label_exception_targets)
1345+
// PopBlock in reachable blocks is converted to NOP by
1346+
// label_exception_targets. Dead blocks may still have them.
13461347
PseudoInstruction::PopBlock => {
13471348
info.instr = Instruction::Nop.into();
13481349
}
@@ -1359,9 +1360,7 @@ pub(crate) fn convert_pseudo_ops(blocks: &mut [Block], varnames_len: u32) {
13591360
| PseudoInstruction::JumpIfFalse { .. }
13601361
| PseudoInstruction::JumpIfTrue { .. }
13611362
| PseudoInstruction::StoreFastMaybeNull(_) => {
1362-
unimplemented!(
1363-
"Unexpected pseudo instruction in convert_pseudo_ops: {pseudo:?}"
1364-
)
1363+
unreachable!("Unexpected pseudo instruction in convert_pseudo_ops: {pseudo:?}")
13651364
}
13661365
}
13671366
}

0 commit comments

Comments
 (0)