Skip to content

Commit 2ca6e5d

Browse files
committed
Implement execution of finally block. Fixes RustPython#1306.
1 parent 86fc18e commit 2ca6e5d

File tree

4 files changed

+94
-40
lines changed

4 files changed

+94
-40
lines changed

bytecode/src/bytecode.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,15 @@ pub enum Instruction {
187187
handler: Label,
188188
},
189189

190+
/// Enter a finally block, without returning, excepting, just because we are there.
191+
EnterFinally,
192+
190193
/// Marker bytecode for the end of a finally sequence.
191194
/// When this bytecode is executed, the eval loop does one of those things:
192195
/// - Continue at a certain bytecode position
193196
/// - Propagate the exception
194197
/// - Return from a function
198+
/// - Do nothing at all, just continue
195199
EndFinally,
196200

197201
SetupExcept {
@@ -493,6 +497,7 @@ impl Instruction {
493497
SetupLoop { start, end } => w!(SetupLoop, label_map[start], label_map[end]),
494498
SetupExcept { handler } => w!(SetupExcept, label_map[handler]),
495499
SetupFinally { handler } => w!(SetupFinally, label_map[handler]),
500+
EnterFinally => w!(EnterFinally),
496501
EndFinally => w!(EndFinally),
497502
SetupWith { end } => w!(SetupWith, end),
498503
CleanupWith { end } => w!(CleanupWith, end),

compiler/src/compile.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -712,13 +712,13 @@ impl<O: OutputStream> Compiler<O> {
712712
finalbody: &Option<ast::Suite>,
713713
) -> Result<(), CompileError> {
714714
let mut handler_label = self.new_label();
715-
let finally_label = self.new_label();
715+
let finally_handler_label = self.new_label();
716716
let else_label = self.new_label();
717717

718718
// Setup a finally block if we have a finally statement.
719719
if finalbody.is_some() {
720720
self.emit(Instruction::SetupFinally {
721-
handler: finally_label,
721+
handler: finally_handler_label,
722722
});
723723
}
724724

@@ -773,26 +773,28 @@ impl<O: OutputStream> Compiler<O> {
773773
// Handler code:
774774
self.compile_statements(&handler.body)?;
775775
self.emit(Instruction::PopException);
776+
777+
if finalbody.is_some() {
778+
self.emit(Instruction::PopBlock); // pop finally block
779+
// We enter the finally block, without exception.
780+
self.emit(Instruction::EnterFinally);
781+
}
782+
776783
self.emit(Instruction::Jump {
777-
target: finally_label,
784+
target: finally_handler_label,
778785
});
779786

780787
// Emit a new label for the next handler
781788
self.set_label(handler_label);
782789
handler_label = self.new_label();
783790
}
791+
784792
self.emit(Instruction::Jump {
785793
target: handler_label,
786794
});
787795
self.set_label(handler_label);
788796
// If code flows here, we have an unhandled exception,
789-
// emit finally code and raise again!
790-
// Duplicate finally code here:
791-
// TODO: this bytecode is now duplicate, could this be
792-
// improved?
793-
if let Some(statements) = finalbody {
794-
self.compile_statements(statements)?;
795-
}
797+
// raise the exception again!
796798
self.emit(Instruction::Raise { argc: 0 });
797799

798800
// We successfully ran the try block:
@@ -802,8 +804,15 @@ impl<O: OutputStream> Compiler<O> {
802804
self.compile_statements(statements)?;
803805
}
804806

807+
if finalbody.is_some() {
808+
self.emit(Instruction::PopBlock); // pop finally block
809+
810+
// We enter the finally block, without return / exception.
811+
self.emit(Instruction::EnterFinally);
812+
}
813+
805814
// finally:
806-
self.set_label(finally_label);
815+
self.set_label(finally_handler_label);
807816
if let Some(statements) = finalbody {
808817
self.compile_statements(statements)?;
809818
self.emit(Instruction::EndFinally);

tests/snippets/try_exceptions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,19 @@ def f():
187187
pass
188188
raise
189189

190+
# try-return-finally behavior:
191+
l = []
192+
def foo():
193+
try:
194+
return 33
195+
finally:
196+
l.append(1337)
197+
198+
r = foo()
199+
assert r == 33
200+
assert l == [1337]
201+
202+
190203
# Regression https://github.com/RustPython/RustPython/issues/867
191204
for _ in [1, 2]:
192205
try:

vm/src/frame.rs

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ enum BlockType {
4646
Finally {
4747
handler: bytecode::Label,
4848
},
49+
50+
/// Active finally sequence
51+
FinallyHandler {
52+
reason: Option<UnwindReason>,
53+
},
4954
With {
5055
end: bytecode::Label,
5156
context_manager: PyObjectRef,
@@ -58,6 +63,7 @@ pub type FrameRef = PyRef<Frame>;
5863
/// The reason why we might be unwinding a block.
5964
/// This could be return of function, exception being
6065
/// raised, a break or continue being hit, etc..
66+
#[derive(Clone, Debug)]
6167
enum UnwindReason {
6268
/// We are returning a value from a return statement.
6369
Returning { value: PyObjectRef },
@@ -407,10 +413,27 @@ impl Frame {
407413
self.push_block(BlockType::Finally { handler: *handler });
408414
Ok(None)
409415
}
410-
bytecode::Instruction::EndFinally => {
411-
let _block = self.pop_block().unwrap();
416+
bytecode::Instruction::EnterFinally => {
417+
self.push_block(BlockType::FinallyHandler { reason: None });
412418
Ok(None)
413419
}
420+
bytecode::Instruction::EndFinally => {
421+
// Pop the finally handler from the stack, and recall
422+
// what was the reason we were in this finally clause.
423+
let block = self.pop_block();
424+
425+
if let BlockType::FinallyHandler { reason } = block.typ {
426+
if let Some(reason) = reason {
427+
self.unwind_blocks(vm, reason)
428+
} else {
429+
Ok(None)
430+
}
431+
} else {
432+
panic!(
433+
"Block type must be finally handler when reaching EndFinally instruction!"
434+
);
435+
}
436+
}
414437
bytecode::Instruction::SetupWith { end } => {
415438
let context_manager = self.pop_value();
416439
// Call enter:
@@ -423,7 +446,7 @@ impl Frame {
423446
Ok(None)
424447
}
425448
bytecode::Instruction::CleanupWith { end: end1 } => {
426-
let block = self.pop_block().unwrap();
449+
let block = self.pop_block();
427450
if let BlockType::With {
428451
end: end2,
429452
context_manager,
@@ -438,7 +461,7 @@ impl Frame {
438461
Ok(None)
439462
}
440463
bytecode::Instruction::PopBlock => {
441-
self.pop_block().expect("no pop to block");
464+
self.pop_block();
442465
Ok(None)
443466
}
444467
bytecode::Instruction::GetIter => {
@@ -687,7 +710,7 @@ impl Frame {
687710
Ok(None)
688711
}
689712
bytecode::Instruction::PopException {} => {
690-
let block = self.pop_block().unwrap(); // this asserts that the block is_some.
713+
let block = self.pop_block();
691714
if let BlockType::ExceptHandler = block.typ {
692715
vm.pop_exception().expect("Should have exception in stack");
693716
Ok(None)
@@ -781,7 +804,7 @@ impl Frame {
781804
match block.typ {
782805
BlockType::Loop { start, end } => match &reason {
783806
UnwindReason::Break => {
784-
self.pop_block().unwrap();
807+
self.pop_block();
785808
self.jump(end);
786809
return Ok(None);
787810
}
@@ -790,33 +813,32 @@ impl Frame {
790813
return Ok(None);
791814
}
792815
_ => {
793-
self.pop_block().unwrap();
816+
self.pop_block();
794817
}
795818
},
796-
BlockType::Finally { .. } => {
797-
self.pop_block().unwrap();
798-
// TODO!
819+
BlockType::Finally { handler } => {
820+
self.pop_block();
821+
self.push_block(BlockType::FinallyHandler {
822+
reason: Some(reason.clone()),
823+
});
824+
self.jump(handler);
825+
return Ok(None);
799826
}
800827
BlockType::TryExcept { handler } => {
801-
self.pop_block().unwrap();
802-
match &reason {
803-
UnwindReason::Raising { exception } => {
804-
self.push_block(BlockType::ExceptHandler {});
805-
self.push_value(exception.clone());
806-
vm.push_exception(exception.clone());
807-
self.jump(handler);
808-
return Ok(None);
809-
}
810-
_ => {
811-
// No worries!
812-
}
828+
self.pop_block();
829+
if let UnwindReason::Raising { exception } = &reason {
830+
self.push_block(BlockType::ExceptHandler {});
831+
self.push_value(exception.clone());
832+
vm.push_exception(exception.clone());
833+
self.jump(handler);
834+
return Ok(None);
813835
}
814836
}
815837
BlockType::With {
816838
context_manager,
817839
end,
818840
} => {
819-
self.pop_block().unwrap();
841+
self.pop_block();
820842
match &reason {
821843
UnwindReason::Raising { exception } => {
822844
match self.call_context_manager_exit(
@@ -866,8 +888,11 @@ impl Frame {
866888
}
867889
}
868890
}
891+
BlockType::FinallyHandler { .. } => {
892+
self.pop_block();
893+
}
869894
BlockType::ExceptHandler => {
870-
self.pop_block().unwrap();
895+
self.pop_block();
871896
vm.pop_exception().expect("Should have exception in stack");
872897
}
873898
}
@@ -881,8 +906,6 @@ impl Frame {
881906
panic!("Internal error: break or continue must occur within a loop block.")
882907
} // UnwindReason::NoWorries => Ok(None),
883908
}
884-
885-
// None
886909
}
887910

888911
fn call_context_manager_exit(
@@ -1193,10 +1216,14 @@ impl Frame {
11931216
});
11941217
}
11951218

1196-
fn pop_block(&self) -> Option<Block> {
1197-
let block = self.blocks.borrow_mut().pop()?;
1219+
fn pop_block(&self) -> Block {
1220+
let block = self
1221+
.blocks
1222+
.borrow_mut()
1223+
.pop()
1224+
.expect("No more blocks to pop!");
11981225
self.stack.borrow_mut().truncate(block.level);
1199-
Some(block)
1226+
block
12001227
}
12011228

12021229
fn current_block(&self) -> Option<Block> {

0 commit comments

Comments
 (0)