Skip to content

Commit c975125

Browse files
committed
co_branches(), _cache_format fix, getattro slot inheritance, branch offset fix
- Clear _cache_format in opcode.py (no inline caches in RustPython) - Fix getattro slot inheritance: preserve native slot from inherit_slots() when update_slot finds a non-wrapper __getattribute__ in MRO - Implement co_branches() on code objects - Fix BRANCH_RIGHT src_offset in InstrumentedPopJumpIf* (compute before jump) - Add Frame::set_lasti()
1 parent 8077ad0 commit c975125

File tree

14 files changed

+255
-287
lines changed

14 files changed

+255
-287
lines changed

.cspell.dict/cpython.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ CLASSDEREF
3333
classdict
3434
cmpop
3535
codedepth
36-
constevaluator
3736
CODEUNIT
3837
CONIN
3938
CONOUT
39+
constevaluator
4040
CONVFUNC
4141
convparam
4242
copyslot
@@ -62,6 +62,7 @@ fieldlist
6262
fileutils
6363
finalbody
6464
finalizers
65+
firsttraceable
6566
flowgraph
6667
formatfloat
6768
freevar

Lib/opcode.py

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -46,75 +46,6 @@
4646
hascompare = [opmap["COMPARE_OP"]]
4747

4848
_cache_format = {
49-
"LOAD_GLOBAL": {
50-
"counter": 1,
51-
"index": 1,
52-
"module_keys_version": 1,
53-
"builtin_keys_version": 1,
54-
},
55-
"BINARY_OP": {
56-
"counter": 1,
57-
"descr": 4,
58-
},
59-
"UNPACK_SEQUENCE": {
60-
"counter": 1,
61-
},
62-
"COMPARE_OP": {
63-
"counter": 1,
64-
},
65-
"CONTAINS_OP": {
66-
"counter": 1,
67-
},
68-
"FOR_ITER": {
69-
"counter": 1,
70-
},
71-
"LOAD_SUPER_ATTR": {
72-
"counter": 1,
73-
},
74-
"LOAD_ATTR": {
75-
"counter": 1,
76-
"version": 2,
77-
"keys_version": 2,
78-
"descr": 4,
79-
},
80-
"STORE_ATTR": {
81-
"counter": 1,
82-
"version": 2,
83-
"index": 1,
84-
},
85-
"CALL": {
86-
"counter": 1,
87-
"func_version": 2,
88-
},
89-
"CALL_KW": {
90-
"counter": 1,
91-
"func_version": 2,
92-
},
93-
"STORE_SUBSCR": {
94-
"counter": 1,
95-
},
96-
"SEND": {
97-
"counter": 1,
98-
},
99-
"JUMP_BACKWARD": {
100-
"counter": 1,
101-
},
102-
"TO_BOOL": {
103-
"counter": 1,
104-
"version": 2,
105-
},
106-
"POP_JUMP_IF_TRUE": {
107-
"counter": 1,
108-
},
109-
"POP_JUMP_IF_FALSE": {
110-
"counter": 1,
111-
},
112-
"POP_JUMP_IF_NONE": {
113-
"counter": 1,
114-
},
115-
"POP_JUMP_IF_NOT_NONE": {
116-
"counter": 1,
117-
},
11849
}
11950

12051
_inline_cache_entries = {

Lib/test/dis_module.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
# A simple module for testing the dis module.
3+
4+
def f(): pass
5+
def g(): pass

Lib/test/test_dis.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,6 @@ def test_intrinsic_2(self):
11471147
self.assertIn("CALL_INTRINSIC_2 1 (INTRINSIC_PREP_RERAISE_STAR)",
11481148
self.get_disassembly("try: pass\nexcept* Exception: x"))
11491149

1150-
@unittest.expectedFailure # TODO: RUSTPYTHON
11511150
def test_big_linenos(self):
11521151
def func(count):
11531152
namespace = {}
@@ -2119,7 +2118,6 @@ def test_oparg_alias(self):
21192118
positions=None)
21202119
self.assertEqual(instruction.arg, instruction.oparg)
21212120

2122-
@unittest.expectedFailure # TODO: RUSTPYTHON
21232121
def test_show_caches_with_label(self):
21242122
def f(x, y, z):
21252123
if x:
@@ -2238,6 +2236,7 @@ def f(opcode, oparg, offset, *init_args):
22382236
def get_instructions(self, code):
22392237
return dis._get_instructions_bytes(code)
22402238

2239+
@unittest.expectedFailure # TODO: RUSTPYTHON; no inline caches
22412240
def test_start_offset(self):
22422241
# When no extended args are present,
22432242
# start_offset should be equal to offset
@@ -2290,6 +2289,7 @@ def last_item(iterable):
22902289
self.assertEqual(14, instructions[6].offset)
22912290
self.assertEqual(8, instructions[6].start_offset)
22922291

2292+
@unittest.expectedFailure # TODO: RUSTPYTHON; no inline caches
22932293
def test_cache_offset_and_end_offset(self):
22942294
code = bytes([
22952295
opcode.opmap["LOAD_GLOBAL"], 0x01,
@@ -2422,7 +2422,6 @@ def test__find_imports(self):
24222422
self.assertEqual(len(res), 1)
24232423
self.assertEqual(res[0], expected)
24242424

2425-
@unittest.expectedFailure # TODO: RUSTPYTHON
24262425
def test__find_store_names(self):
24272426
cases = [
24282427
("x+y", ()),

Lib/test/test_monitoring.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,7 +1744,6 @@ def func(v=1):
17441744
('branch right', 'func', 6, 8),
17451745
('branch right', 'func', 2, 10)])
17461746

1747-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: attribute 'f_lineno' of 'frame' objects is not writable
17481747
def test_callback_set_frame_lineno(self):
17491748
def func(s: str) -> int:
17501749
if s.startswith("t"):
@@ -1802,7 +1801,6 @@ def check_branches(self, run_func, test_func=None, tool=TEST_TOOL, recorders=BRA
18021801
for recorder in recorders:
18031802
sys.monitoring.register_callback(tool, recorder.event_type, None)
18041803

1805-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'code' object has no attribute 'co_branches'
18061804
def test_simple(self):
18071805

18081806
def func():
@@ -1823,7 +1821,6 @@ def whilefunc(n=0):
18231821

18241822
self.check_branches(whilefunc)
18251823

1826-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'code' object has no attribute 'co_branches'
18271824
def test_except_star(self):
18281825

18291826
class Foo:
@@ -1842,7 +1839,6 @@ def func():
18421839

18431840
self.check_branches(func)
18441841

1845-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'code' object has no attribute 'co_branches'
18461842
def test4(self):
18471843

18481844
def foo(n=0):
@@ -1853,7 +1849,6 @@ def foo(n=0):
18531849

18541850
self.check_branches(foo)
18551851

1856-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'code' object has no attribute 'co_branches'
18571852
def test_async_for(self):
18581853

18591854
async def gen():
@@ -1942,7 +1937,7 @@ def f():
19421937
]
19431938
return d["f"], expected
19441939

1945-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: False != True
1940+
@unittest.expectedFailure # TODO: RUSTPYTHON; line number differences in multi-line super() calls
19461941
def test_method_call(self):
19471942
nonopt_func, nonopt_expected = self._super_method_call(optimized=False)
19481943
opt_func, opt_expected = self._super_method_call(optimized=True)
@@ -1994,7 +1989,7 @@ def f():
19941989
]
19951990
return d["f"], expected
19961991

1997-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: False != True
1992+
@unittest.expectedFailure # TODO: RUSTPYTHON; line number differences in multi-line super() calls
19981993
def test_method_call_error(self):
19991994
nonopt_func, nonopt_expected = self._super_method_call_error(optimized=False)
20001995
opt_func, opt_expected = self._super_method_call_error(optimized=True)
@@ -2032,15 +2027,15 @@ def f():
20322027
]
20332028
return d["f"], expected
20342029

2035-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: False != True
2030+
@unittest.expectedFailure # TODO: RUSTPYTHON; line number differences in multi-line super() calls
20362031
def test_attr(self):
20372032
nonopt_func, nonopt_expected = self._super_attr(optimized=False)
20382033
opt_func, opt_expected = self._super_attr(optimized=True)
20392034

20402035
self.check_events(nonopt_func, recorders=self.RECORDERS, expected=nonopt_expected)
20412036
self.check_events(opt_func, recorders=self.RECORDERS, expected=opt_expected)
20422037

2043-
@unittest.expectedFailure # TODO: RUSTPYTHON
2038+
@unittest.expectedFailure # TODO: RUSTPYTHON; line number differences in multi-line super() calls
20442039
def test_vs_other_type_call(self):
20452040
code_template = textwrap.dedent("""
20462041
class C:

crates/codegen/src/compile.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,6 +2050,7 @@ impl Compiler {
20502050

20512051
fn compile_statement(&mut self, statement: &ast::Stmt) -> CompileResult<()> {
20522052
trace!("Compiling {statement:?}");
2053+
let prev_source_range = self.current_source_range;
20532054
self.set_source_range(statement.range());
20542055

20552056
match &statement {
@@ -2433,7 +2434,14 @@ impl Compiler {
24332434
value,
24342435
simple,
24352436
..
2436-
}) => self.compile_annotated_assign(target, annotation, value.as_deref(), *simple)?,
2437+
}) => {
2438+
self.compile_annotated_assign(target, annotation, value.as_deref(), *simple)?;
2439+
// Bare annotations in function scope emit no code; restore
2440+
// source range so subsequent instructions keep the correct line.
2441+
if value.is_none() && self.ctx.in_func() {
2442+
self.set_source_range(prev_source_range);
2443+
}
2444+
}
24372445
ast::Stmt::Delete(ast::StmtDelete { targets, .. }) => {
24382446
for target in targets {
24392447
self.compile_delete(target)?;
@@ -3807,7 +3815,6 @@ impl Compiler {
38073815
let code = self.exit_scope();
38083816
self.ctx = prev_ctx;
38093817

3810-
// Restore source range so MAKE_FUNCTION is attributed to the `def` line
38113818
self.set_source_range(saved_range);
38123819

38133820
// Create function object with closure
@@ -5180,23 +5187,21 @@ impl Compiler {
51805187
// No PopBlock here - for async, POP_BLOCK is already in for_block
51815188
self.pop_fblock(FBlockType::ForLoop);
51825189

5190+
// End-of-loop instructions are on the `for` line, not the body's last line
5191+
let saved_range = self.current_source_range;
5192+
self.set_source_range(iter.range());
51835193
if is_async {
51845194
emit!(self, Instruction::EndAsyncFor);
51855195
} else {
5186-
// END_FOR + POP_ITER are on the `for` line, not the body's last line
5187-
let saved_range = self.current_source_range;
5188-
self.set_source_range(iter.range());
51895196
emit!(self, Instruction::EndFor);
51905197
emit!(self, Instruction::PopIter);
5191-
self.set_source_range(saved_range);
51925198
}
5199+
self.set_source_range(saved_range);
51935200
self.compile_statements(orelse)?;
51945201

51955202
self.switch_to_block(after_block);
51965203

5197-
// Restore source range to the `for` line so any implicit return
5198-
// (LOAD_CONST None, RETURN_VALUE) is attributed to the `for` line,
5199-
// not the loop body's last line.
5204+
// Implicit return after for-loop should be attributed to the `for` line
52005205
self.set_source_range(iter.range());
52015206

52025207
self.leave_conditional_block();
@@ -6233,6 +6238,8 @@ impl Compiler {
62336238
ops: &[ast::CmpOp],
62346239
comparators: &[ast::Expr],
62356240
) -> CompileResult<()> {
6241+
// Save the full Compare expression range for COMPARE_OP positions
6242+
let compare_range = self.current_source_range;
62366243
let (last_op, mid_ops) = ops.split_last().unwrap();
62376244
let (last_comparator, mid_comparators) = comparators.split_last().unwrap();
62386245

@@ -6241,6 +6248,7 @@ impl Compiler {
62416248

62426249
if mid_comparators.is_empty() {
62436250
self.compile_expression(last_comparator)?;
6251+
self.set_source_range(compare_range);
62446252
self.compile_addcompare(last_op);
62456253

62466254
return Ok(());
@@ -6253,6 +6261,7 @@ impl Compiler {
62536261
self.compile_expression(comparator)?;
62546262

62556263
// store rhs for the next comparison in chain
6264+
self.set_source_range(compare_range);
62566265
emit!(self, Instruction::Swap { index: 2 });
62576266
emit!(self, Instruction::Copy { index: 2 });
62586267

@@ -6265,6 +6274,7 @@ impl Compiler {
62656274
}
62666275

62676276
self.compile_expression(last_comparator)?;
6277+
self.set_source_range(compare_range);
62686278
self.compile_addcompare(last_op);
62696279

62706280
let end = self.new_block();

crates/codegen/src/ir.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,8 @@ impl CodeInfo {
519519
let tuple_const = ConstantData::Tuple { elements };
520520
let (const_idx, _) = self.metadata.consts.insert_full(tuple_const);
521521

522-
// Replace preceding LOAD instructions with NOP, using the
523-
// BUILD_TUPLE location so remove_nops() treats them as
524-
// same-line and removes them (multi-line tuple literals
525-
// would otherwise leave line-introducing NOPs behind).
522+
// Replace preceding LOAD instructions with NOP at the
523+
// BUILD_TUPLE location so remove_nops() can eliminate them.
526524
let folded_loc = block.instructions[i].location;
527525
for j in start_idx..i {
528526
block.instructions[j].instr = Instruction::Nop.into();

0 commit comments

Comments
 (0)