Skip to content

Fix "tried to pop from empty stack" with BlockExpr and const#7916

Merged
youknowone merged 2 commits into
RustPython:mainfrom
im-0:fix-block-expr
May 19, 2026
Merged

Fix "tried to pop from empty stack" with BlockExpr and const#7916
youknowone merged 2 commits into
RustPython:mainfrom
im-0:fix-block-expr

Conversation

@im-0
Copy link
Copy Markdown
Contributor

@im-0 im-0 commented May 18, 2026

Constant expressions without side effects are optimized away unless in an interactive mode. 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.

---- 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

Simple test case included.

Summary by CodeRabbit

  • Refactor
    • Improved compilation efficiency for constant expressions in statement blocks.

Review Change Stack

im-0 added 2 commits May 18, 2026 20:40
test_block_expr_return_const() panics currently.
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
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: d250a60a-1e12-41bd-8b1e-a52b0b42099d

📥 Commits

Reviewing files that changed from the base of the PR and between 883ce9d and 239ff88.

📒 Files selected for processing (2)
  • crates/codegen/src/compile.rs
  • crates/vm/src/vm/python_run.rs

📝 Walkthrough

Walkthrough

The PR refines how the compiler handles trailing expressions in statement blocks. When compiling in non-interactive mode and the trailing expression qualifies as const, it now compiles the expression directly instead of unconditionally discarding it. Two unit tests validate this behavior for both const and variable-dependent block expressions.

Changes

Const Expression Optimization in Trailing Statements

Layer / File(s) Summary
Compiler handling of trailing const expressions
crates/codegen/src/compile.rs
The match arm for ast::Stmt::Expr destructures the expression value and conditionally compiles it when !self.interactive and the expression passes Self::is_const_expression(value); otherwise reverts to the prior behavior of popping the PopTop instruction.
Block expression evaluation tests
crates/vm/src/vm/python_run.rs
Unit tests verify VirtualMachine::run_block_expr correctly evaluates constant block expressions (e.g., "1") and non-constant expressions that reference globals (e.g., "2 + x" with x = 3).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • RustPython/RustPython#7541: Expands const-expression detection logic that this PR depends on to gate the new trailing statement compilation.

Poem

🐰 Const expressions hop and skip,
No more wasted PopTop trips,
Tests confirm they land just right,
Blocks compile with cleaner light!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: handling const expressions in BlockExpr mode to prevent stack underflow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@ShaharNaveh ShaharNaveh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TYSM!
welcome to the project:)

Copy link
Copy Markdown
Member

@youknowone youknowone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! good to have test for this

@youknowone youknowone merged commit 150b910 into RustPython:main May 19, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants