Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix clippy, fold_unary_negative chaining, and generator line tracing
- Replace irrefutable if-let with let for ExceptHandler
- Remove folded UNARY_NEGATIVE instead of replacing with NOP,
  enabling chained negation folding
- Initialize prev_line to def line for generators/coroutines
  to suppress spurious LINE events from preamble instructions
- Remove expectedFailure markers for now-passing tests
  • Loading branch information
youknowone committed Mar 25, 2026
commit 12b405d9ab16ffe5c9dcd10b4d33ee3b04c6b6f8
4 changes: 0 additions & 4 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2486,7 +2486,6 @@ def f():

class TestStaticAttributes(unittest.TestCase):

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object 'C' has no attribute '__static_attributes__'
def test_basic(self):
class C:
def f(self):
Expand Down Expand Up @@ -2518,7 +2517,6 @@ def h(self, a):

self.assertEqual(sorted(C.__static_attributes__), ['u', 'v', 'x', 'y', 'z'])

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object 'C' has no attribute '__static_attributes__'
def test_nested_class(self):
class C:
def f(self):
Expand All @@ -2533,7 +2531,6 @@ def g(self):
self.assertEqual(sorted(C.__static_attributes__), ['x', 'y'])
self.assertEqual(sorted(C.D.__static_attributes__), ['y', 'z'])

@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object 'C' has no attribute '__static_attributes__'
def test_subclass(self):
class C:
def f(self):
Expand Down Expand Up @@ -2593,7 +2590,6 @@ def test_tuple(self):
def test_set(self):
self.check_stack_size("{" + "x, " * self.N + "x}")

@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 202 not less than or equal to 7
def test_dict(self):
self.check_stack_size("{" + "x:x, " * self.N + "x:x}")

Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,6 @@ def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(None)

@unittest.expectedFailure # TODO: RUSTPYTHON; no LOAD_FAST_BORROW_LOAD_FAST_BORROW superinstruction
def test_load_fast_known_simple(self):
def f():
x = 1
Expand Down
5 changes: 2 additions & 3 deletions crates/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4582,9 +4582,8 @@ impl Compiler {
ast::Stmt::Try(s) => {
Self::scan_store_attrs(&s.body, name, attrs);
for handler in &s.handlers {
if let ast::ExceptHandler::ExceptHandler(h) = handler {
Self::scan_store_attrs(&h.body, name, attrs);
}
let ast::ExceptHandler::ExceptHandler(h) = handler;
Self::scan_store_attrs(&h.body, name, attrs);
}
Self::scan_store_attrs(&s.orelse, name, attrs);
Self::scan_store_attrs(&s.finalbody, name, attrs);
Expand Down
6 changes: 3 additions & 3 deletions crates/codegen/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,9 +666,9 @@ impl CodeInfo {
}
.into();
block.instructions[i].arg = OpArg::new(const_idx as u32);
// Replace UNARY_NEGATIVE with NOP
block.instructions[i + 1].instr = Instruction::Nop.into();
i += 2;
// Remove UNARY_NEGATIVE so chained negation can be folded
block.instructions.remove(i + 1);
// Don't increment i - re-check new LOAD_CONST with next instruction
} else {
i += 1;
}
Expand Down
15 changes: 14 additions & 1 deletion crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,19 @@ impl Frame {
}
}

// For generators/coroutines, initialize prev_line to the def line
// so that preamble instructions (RETURN_GENERATOR, POP_TOP) don't
// fire spurious LINE events.
let prev_line = if code
.flags
.intersects(bytecode::CodeFlags::GENERATOR | bytecode::CodeFlags::COROUTINE)
{
code.first_line_number
.map_or(0, |line| line.get() as u32)
} else {
0
};
Comment on lines +708 to +718

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Don't seed prev_line with first_line_number.

This also suppresses the first real LINE event for single-line generator/coroutine bodies (def g(): yield 1, async def f(): await x), because the body shares the same source line as the RETURN_GENERATOR / POP_TOP preamble. Please suppress the preamble opcodes explicitly instead of priming prev_line to that line.

Also applies to: 734-734

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/frame.rs` around lines 708 - 718, The code incorrectly seeds
prev_line with code.first_line_number when code.flags contains
CodeFlags::GENERATOR or CodeFlags::COROUTINE, which suppresses the first real
LINE event for single-line generator/coroutine bodies; instead, remove the
priming of prev_line and explicitly suppress the preamble opcodes (e.g.,
RETURN_GENERATOR and POP_TOP) when emitting LINE events by checking the opcode
during event emission rather than setting prev_line based on
code.first_line_number; update the initialization around prev_line and the
LINE-event emission logic to skip those specific preamble opcodes while leaving
prev_line at 0 so real first-line events are not lost.


let iframe = InterpreterFrame {
localsplus,
locals: match scope.locals {
Expand All @@ -719,7 +732,7 @@ impl Frame {
code,
func_obj,
lasti: Radium::new(0),
prev_line: 0,
prev_line,
trace: PyMutex::new(vm.ctx.none()),
trace_lines: PyMutex::new(true),
trace_opcodes: PyMutex::new(false),
Expand Down