From 899fac4a9b8ac57557a47c0d4e246732198953e6 Mon Sep 17 00:00:00 2001 From: Ivan Mironov Date: Mon, 18 May 2026 15:01:07 +0200 Subject: [PATCH 1/2] Add tests for Mode::BlockExpr and VirtualMachine::run_block_expr test_block_expr_return_const() panics currently. --- crates/vm/src/vm/python_run.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/crates/vm/src/vm/python_run.rs b/crates/vm/src/vm/python_run.rs index 26000b5e11f..126bac4fd06 100644 --- a/crates/vm/src/vm/python_run.rs +++ b/crates/vm/src/vm/python_run.rs @@ -174,3 +174,35 @@ mod file_run { Ok(crate::import::check_pyc_magic_number_bytes(&buf)) } } + +#[cfg(test)] +mod tests { + use rustpython_vm::Interpreter; + + fn interpreter() -> Interpreter { + Interpreter::without_stdlib(Default::default()) + } + + #[test] + fn test_block_expr_return_const() { + interpreter().enter(|vm| { + let scope = vm.new_scope_with_builtins(); + let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "1")); + let value = vm.unwrap_pyresult(value.try_int(vm)); + let value: u32 = vm.unwrap_pyresult(value.try_to_primitive(vm)); + assert_eq!(value, 1); + }) + } + + #[test] + fn test_block_expr_return_nonconst() { + interpreter().enter(|vm| { + let scope = vm.new_scope_with_builtins(); + vm.unwrap_pyresult(scope.globals.set_item("x", vm.new_pyobj(3), vm)); + let value = vm.unwrap_pyresult(vm.run_block_expr(scope, "2 + x")); + let value = vm.unwrap_pyresult(value.try_int(vm)); + let value: u32 = vm.unwrap_pyresult(value.try_to_primitive(vm)); + assert_eq!(value, 5); + }) + } +} From 239ff886578a0930cc0612ab3ec35eb718cb8a49 Mon Sep 17 00:00:00 2001 From: Ivan Mironov Date: Sun, 17 May 2026 12:11:45 +0200 Subject: [PATCH 2/2] 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: at ??? file "", 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::>::run::{{closure}} at ./src/frame.rs:1098:44 9: rustpython_vm::frame::>::with_exec at ./src/frame.rs:1093:9 10: rustpython_vm::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::::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::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::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: 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 --- crates/codegen/src/compile.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/codegen/src/compile.rs b/crates/codegen/src/compile.rs index cef312459fa..81349c2f078 100644 --- a/crates/codegen/src/compile.rs +++ b/crates/codegen/src/compile.rs @@ -2499,8 +2499,12 @@ impl Compiler { if let Some(last_statement) = body.last() { match last_statement { - ast::Stmt::Expr(_) => { - self.current_block().instructions.pop(); // pop Instruction::PopTop + ast::Stmt::Expr(ast::StmtExpr { value, .. }) => { + if !self.interactive && Self::is_const_expression(value) { + self.compile_expression(value)?; + } else { + self.current_block().instructions.pop(); // pop Instruction::PopTop + } } ast::Stmt::FunctionDef(_) | ast::Stmt::ClassDef(_) => { let pop_instructions = self.current_block().instructions.pop();