Skip to content

Commit 017fbc5

Browse files
authored
Fix "tried to pop from empty stack" with BlockExpr and const (#7916)
* Add tests for Mode::BlockExpr and VirtualMachine::run_block_expr test_block_expr_return_const() panics currently. * Fix "tried to pop from empty stack" with BlockExpr and const Constant expressions without side effects are optimized away unless in an interactive mode[1]. This causes panic when last statement in a block is a constant and Mode::BlockExpr is used as this constant value must be returned to caller. Example panic: ---- vm::python_run::tests::test_block_expr_return_const stdout ---- [crates/vm/src/frame.rs:9653:9] self = ExecutingFrame { code: code: <code object <module> at ??? file "<embedded>", line 1>, stack_len: 0, } thread 'vm::python_run::tests::test_block_expr_return_const' (2640752) panicked at crates/vm/src/frame.rs:9410:18: tried to pop from empty stack stack backtrace: 0: __rustc::rust_begin_unwind at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/std/src/panicking.rs:689:5 1: core::panicking::panic_fmt at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/core/src/panicking.rs:80:14 2: rustpython_vm::frame::ExecutingFrame::fatal at ./src/frame.rs:9654:9 3: rustpython_vm::frame::ExecutingFrame::pop_stackref_opt at ./src/frame.rs:9410:18 4: rustpython_vm::frame::ExecutingFrame::pop_stackref at ./src/frame.rs:9420:18 5: rustpython_vm::frame::ExecutingFrame::pop_value at ./src/frame.rs:9435:14 6: rustpython_vm::frame::ExecutingFrame::execute_instruction at ./src/frame.rs:3391:34 7: rustpython_vm::frame::ExecutingFrame::run at ./src/frame.rs:1623:31 8: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run::{{closure}} at ./src/frame.rs:1098:44 9: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::with_exec at ./src/frame.rs:1093:9 10: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run at ./src/frame.rs:1098:14 11: rustpython_vm::vm::VirtualMachine::run_frame::{{closure}} at ./src/vm/mod.rs:1201:44 12: rustpython_vm::vm::VirtualMachine::with_frame_impl::{{closure}}::{{closure}} at ./src/vm/mod.rs:1610:60 13: rustpython_vm::vm::VirtualMachine::dispatch_traced_frame at ./src/vm/mod.rs:1689:22 14: rustpython_vm::vm::VirtualMachine::with_frame_impl::{{closure}} at ./src/vm/mod.rs:1610:22 15: rustpython_vm::vm::VirtualMachine::with_recursion at ./src/vm/mod.rs:1547:9 16: rustpython_vm::vm::VirtualMachine::with_frame_impl at ./src/vm/mod.rs:1572:14 17: rustpython_vm::vm::VirtualMachine::with_frame at ./src/vm/mod.rs:1555:14 18: rustpython_vm::vm::VirtualMachine::run_frame at ./src/vm/mod.rs:1201:20 19: rustpython_vm::vm::VirtualMachine::run_code_obj at ./src/vm/mod.rs:1120:14 20: rustpython_vm::vm::python_run::<impl rustpython_vm::vm::VirtualMachine>::run_block_expr at ./src/vm/python_run.rs:51:14 21: rustpython_vm::vm::python_run::tests::test_block_expr_return_const::{{closure}} at ./src/vm/python_run.rs:190:47 22: rustpython_vm::vm::interpreter::Interpreter::enter::{{closure}} at ./src/vm/interpreter.rs:366:39 23: rustpython_vm::vm::thread::set_current_vm::{{closure}} at ./src/vm/thread.rs:107:9 24: std::thread::local::LocalKey<T>::try_with at /home/im/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:513:12 25: std::thread::local::LocalKey<T>::with at /home/im/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:477:20 26: rustpython_vm::vm::thread::set_current_vm at ./src/vm/thread.rs:102:14 27: rustpython_vm::vm::thread::enter_vm at ./src/vm/thread.rs:132:5 28: rustpython_vm::vm::interpreter::Interpreter::enter at ./src/vm/interpreter.rs:366:9 29: rustpython_vm::vm::python_run::tests::test_block_expr_return_const at ./src/vm/python_run.rs:188:23 30: rustpython_vm::vm::python_run::tests::test_block_expr_return_const::{{closure}} at ./src/vm/python_run.rs:187:38 31: core::ops::function::FnOnce::call_once at /home/im/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5 32: <fn() -> core::result::Result<(), alloc::string::String> as core::ops::function::FnOnce<()>>::call_once at /rustc/59807616e1fa2540724bfbac14d7976d7e4a3860/library/core/src/ops/function.rs:250:5 [1] https://github.com/RustPython/RustPython/blob/883ce9d273db98b435eb3f51128925a1255ba88d/crates/codegen/src/compile.rs#L2990-L2997
1 parent 292f66e commit 017fbc5

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

‎crates/codegen/src/compile.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2499,8 +2499,12 @@ impl Compiler {
24992499

25002500
if let Some(last_statement) = body.last() {
25012501
match last_statement {
2502-
ast::Stmt::Expr(_) => {
2503-
self.current_block().instructions.pop(); // pop Instruction::PopTop
2502+
ast::Stmt::Expr(ast::StmtExpr { value, .. }) => {
2503+
if !self.interactive && Self::is_const_expression(value) {
2504+
self.compile_expression(value)?;
2505+
} else {
2506+
self.current_block().instructions.pop(); // pop Instruction::PopTop
2507+
}
25042508
}
25052509
ast::Stmt::FunctionDef(_) | ast::Stmt::ClassDef(_) => {
25062510
let pop_instructions = self.current_block().instructions.pop();

‎crates/vm/src/vm/python_run.rs‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,35 @@ mod file_run {
174174
Ok(crate::import::check_pyc_magic_number_bytes(&buf))
175175
}
176176
}
177+
178+
#[cfg(test)]
179+
mod tests {
180+
use rustpython_vm::Interpreter;
181+
182+
fn interpreter() -> Interpreter {
183+
Interpreter::without_stdlib(Default::default())
184+
}
185+
186+
#[test]
187+
fn test_block_expr_return_const() {
188+
interpreter().enter(|vm| {
189+
let scope = vm.new_scope_with_builtins();
190+
let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "1"));
191+
let value = vm.unwrap_pyresult(value.try_int(vm));
192+
let value: u32 = vm.unwrap_pyresult(value.try_to_primitive(vm));
193+
assert_eq!(value, 1);
194+
})
195+
}
196+
197+
#[test]
198+
fn test_block_expr_return_nonconst() {
199+
interpreter().enter(|vm| {
200+
let scope = vm.new_scope_with_builtins();
201+
vm.unwrap_pyresult(scope.globals.set_item("x", vm.new_pyobj(3), vm));
202+
let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "2 + x"));
203+
let value = vm.unwrap_pyresult(value.try_int(vm));
204+
let value: u32 = vm.unwrap_pyresult(value.try_to_primitive(vm));
205+
assert_eq!(value, 5);
206+
})
207+
}
208+
}

0 commit comments

Comments
 (0)