Fix subclass right-op dispatch for Python classes#7462
Fix subclass right-op dispatch for Python classes#7462youknowone merged 4 commits intoRustPython:mainfrom
Conversation
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds explicit reflected ("right-side") numeric slots and helpers mapping ops to r* method names, and updates VM operator dispatch to consider right-side slots and subclass overloads when resolving binary and ternary operations. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Caller
participant VM as VM (vm_ops)
participant Left as LeftType.__op__
participant Right as RightType.__rop__
VM->>Left: lookup left slot (left_*)
alt left slot exists and returns value
Left-->>VM: result or NotImplemented
else classes differ or left returned NotImplemented
VM->>Right: lookup right slot (right_*)
alt subclass and method_is_overloaded -> true
Right-->>VM: result or NotImplemented
else
Right-->>VM: NotImplemented
end
end
VM-->>Caller: final result or raise TypeError
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: [x] lib: cpython/Lib/opcode.py dependencies:
dependent tests: (44 tests)
[ ] test: cpython/Lib/test/test_class.py (TODO: 15) dependencies: dependent tests: (no tests depend on class) [ ] lib: cpython/Lib/collections dependencies:
dependent tests: (302 tests)
[ ] test: cpython/Lib/test/test_descr.py (TODO: 43) dependencies: dependent tests: (no tests depend on descr) [x] lib: cpython/Lib/dis.py dependencies:
dependent tests: (70 tests)
[x] test: cpython/Lib/test/test_format.py (TODO: 6) dependencies: dependent tests: (no tests depend on format) [x] test: cpython/Lib/test/test_math.py dependencies: dependent tests: (229 tests)
[x] lib: cpython/Lib/types.py dependencies:
dependent tests: (52 tests)
Legend:
|
216006b to
131e5aa
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/vm/src/vm/vm_ops.rs`:
- Around line 13-25: Change method_is_overloaded to return PyResult<bool>
instead of bool and propagate any error from vm.identical_or_equal instead of
silencing it; specifically, in method_is_overloaded (and places calling it such
as binary_op1 and ternary_op) replace the unwrap_or(false) with the ? operator
(or explicit match to return Err) so exceptions from identical_or_equal bubble
up as PyErrs, update callers binary_op1/ternary_op to accept the PyResult<bool>
and propagate errors (or handle them) accordingly, and ensure the initial
get_attr checks still return Ok(false) when attributes are missing.
- Around line 183-197: The code is incorrectly dropping slot_bb when the
left-side slots compare equal, which loses the reflected fallback (right_*
trampoline). Always preserve slot_bb by assigning slot_b = slot_bb when slot_bb
is present, and separate that from the priority decision: use
class_b.fast_issubclass(class_a) && let Some(rop_name) =
op_slot.right_method_name(self) && method_is_overloaded(class_a, class_b,
rop_name, self) only to decide whether the RHS should run before the LHS (i.e.,
adjust ordering/priority), not to decide whether to queue the reflected slot at
all; update the logic around slot_a, slot_b, slot_bb, class_a, class_b, op_slot,
right_method_name, method_is_overloaded and fast_issubclass accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 0b5c98b5-467c-45ff-92d6-5298b5f4eb29
⛔ Files ignored due to path filters (1)
Lib/test/test_descr.pyis excluded by!Lib/**
📒 Files selected for processing (2)
crates/vm/src/protocol/number.rscrates/vm/src/vm/vm_ops.rs
94c80c5 to
b24d50e
Compare
Co-authored-by: Shahar Naveh <50263213+ShaharNaveh@users.noreply.github.com>
ShaharNaveh
left a comment
There was a problem hiding this comment.
great!
ty for your recent contributions:)
When both operands are Python-defined classes, the inherited right-op was incorrectly given priority over the base class left-op, even when the subclass didn't override anything.
The root cause is that
binary_op1andternary_opcomparedleft(A)vsright(B)slot pointers to decide whether two types share the same implementation.Since RustPython uses separate left/right slot functions (unlike CPython's single combined slot),
this comparison always returned "different" for Python classes, causing the
subclass right-op path to fire unconditionally.
Fix:
left(A)vsleft(B)instead, which correctly identifies same-implementation typesmethod_is_overloaded(similar to CPythonmethod_is_overloaded) to check whether the subclass actually overrides the right-op at the Python method levelright_method_nametoPyNumberBinaryOp/PyNumberTernaryOpfor the op-to-dunder mapping needed bymethod_is_overloadedbinary_op1andternary_opSummary by CodeRabbit
New Features
Bug Fixes