pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/RustPython/RustPython/commit/79e17cb1cf9d181e0bab765f1dfc058982c02992

ss" /> Fix subclass right-op dispatch for Python classes (#7462) · RustPython/RustPython@79e17cb · GitHub
Skip to content

Commit 79e17cb

Browse files
authored
Fix subclass right-op dispatch for Python classes (#7462)
* Fix subclass right-op dispatch for Python classes * Separate fallback queueing from subclass priority in op dispatch
1 parent 2f181ef commit 79e17cb

File tree

4 files changed

+105
-6
lines changed

4 files changed

+105
-6
lines changed

Lib/test/test_collections.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ def __contains__(self, key):
262262
d = c.new_child(b=20, c=30)
263263
self.assertEqual(d.maps, [{'b': 20, 'c': 30}, {'a': 1, 'b': 2}])
264264

265-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: <class 'test.test_collections.TestChainMap.test_union_operators.<locals>.Subclass'> is not <class 'collections.ChainMap'>
266265
def test_union_operators(self):
267266
cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4))
268267
cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4))

Lib/test/test_descr.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4318,7 +4318,6 @@ class C:
43184318
C.__name__ = Nasty("abc")
43194319
C.__name__ = "normal"
43204320

4321-
@unittest.expectedFailure # TODO: RUSTPYTHON
43224321
def test_subclass_right_op(self):
43234322
# Testing correct dispatch of subclass overloading __r<op>__...
43244323

crates/vm/src/protocol/number.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,12 +229,63 @@ pub enum PyNumberBinaryOp {
229229
InplaceMatrixMultiply,
230230
}
231231

232+
impl PyNumberBinaryOp {
233+
//github.com/ Returns `None` for in-place ops which don't have right-side variants.
234+
pub fn right_method_name(
235+
self,
236+
vm: &VirtualMachine,
237+
) -> Option<&'static crate::builtins::PyStrInterned> {
238+
use PyNumberBinaryOp::*;
239+
Some(match self {
240+
Add => identifier!(vm, __radd__),
241+
Subtract => identifier!(vm, __rsub__),
242+
Multiply => identifier!(vm, __rmul__),
243+
Remainder => identifier!(vm, __rmod__),
244+
Divmod => identifier!(vm, __rdivmod__),
245+
Lshift => identifier!(vm, __rlshift__),
246+
Rshift => identifier!(vm, __rrshift__),
247+
And => identifier!(vm, __rand__),
248+
Xor => identifier!(vm, __rxor__),
249+
Or => identifier!(vm, __ror__),
250+
FloorDivide => identifier!(vm, __rfloordiv__),
251+
TrueDivide => identifier!(vm, __rtruediv__),
252+
MatrixMultiply => identifier!(vm, __rmatmul__),
253+
// In-place ops don't have right-side variants
254+
InplaceAdd
255+
| InplaceSubtract
256+
| InplaceMultiply
257+
| InplaceRemainder
258+
| InplaceLshift
259+
| InplaceRshift
260+
| InplaceAnd
261+
| InplaceXor
262+
| InplaceOr
263+
| InplaceFloorDivide
264+
| InplaceTrueDivide
265+
| InplaceMatrixMultiply => return None,
266+
})
267+
}
268+
}
269+
232270
#[derive(Copy, Clone)]
233271
pub enum PyNumberTernaryOp {
234272
Power,
235273
InplacePower,
236274
}
237275

276+
impl PyNumberTernaryOp {
277+
//github.com/ Returns `None` for in-place ops which don't have right-side variants.
278+
pub fn right_method_name(
279+
self,
280+
vm: &VirtualMachine,
281+
) -> Option<&'static crate::builtins::PyStrInterned> {
282+
Some(match self {
283+
PyNumberTernaryOp::Power => identifier!(vm, __rpow__),
284+
PyNumberTernaryOp::InplacePower => return None,
285+
})
286+
}
287+
}
288+
238289
#[derive(Default)]
239290
pub struct PyNumberSlots {
240291
pub add: AtomicCell<Option<PyNumberBinaryFunc>>,

crates/vm/src/vm/vm_ops.rs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,32 @@
11
use super::VirtualMachine;
22
use crate::stdlib::_warnings;
33
use crate::{
4-
PyRef,
5-
builtins::{PyInt, PyStr, PyStrRef, PyUtf8Str},
4+
Py, PyRef,
5+
builtins::{PyInt, PyStr, PyStrInterned, PyStrRef, PyType, PyUtf8Str},
66
object::{AsObject, PyObject, PyObjectRef, PyResult},
77
protocol::{PyNumberBinaryOp, PyNumberTernaryOp},
88
types::PyComparisonOp,
99
};
1010
use num_traits::ToPrimitive;
1111

12+
//github.com/ [CPython `method_is_overloaded`](https://github.com/python/cpython/blob/v3.14.3/Objects/typeobject.c#L9849-L9879)
13+
fn method_is_overloaded(
14+
class_a: &Py<PyType>,
15+
class_b: &Py<PyType>,
16+
rop_name: Option<&'static PyStrInterned>,
17+
vm: &VirtualMachine,
18+
) -> PyResult<bool> {
19+
let Some(rop_name) = rop_name else {
20+
return Ok(false);
21+
};
22+
let Some(method_b) = class_b.get_attr(rop_name) else {
23+
return Ok(false);
24+
};
25+
class_a.get_attr(rop_name).map_or(Ok(true), |method_a| {
26+
vm.identical_or_equal(&method_a, &method_b).map(|eq| !eq)
27+
})
28+
}
29+
1230
macro_rules! binary_func {
1331
($fn:ident, $op_slot:ident, $op:expr) => {
1432
pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult {
@@ -162,18 +180,34 @@ impl VirtualMachine {
162180

163181
// Number slots are inherited, direct access is O(1)
164182
let slot_a = class_a.slots.as_number.left_binary_op(op_slot);
183+
let slot_a_addr = slot_a.map(|x| x as usize);
165184
let mut slot_b = None;
185+
let left_b_addr;
166186

167187
if !class_a.is(class_b) {
168188
let slot_bb = class_b.slots.as_number.right_binary_op(op_slot);
169-
if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
189+
if slot_bb.map(|x| x as usize) != slot_a_addr {
170190
slot_b = slot_bb;
171191
}
192+
left_b_addr = class_b
193+
.slots
194+
.as_number
195+
.left_binary_op(op_slot)
196+
.map(|x| x as usize);
197+
} else {
198+
left_b_addr = slot_a_addr;
172199
}
173200

174201
if let Some(slot_a) = slot_a {
175202
if let Some(slot_bb) = slot_b
176203
&& class_b.fast_issubclass(class_a)
204+
&& (slot_a_addr != left_b_addr
205+
|| method_is_overloaded(
206+
class_a,
207+
class_b,
208+
op_slot.right_method_name(self),
209+
self,
210+
)?)
177211
{
178212
let ret = slot_bb(a, b, self)?;
179213
if !ret.is(&self.ctx.not_implemented) {
@@ -269,18 +303,34 @@ impl VirtualMachine {
269303

270304
// Number slots are inherited, direct access is O(1)
271305
let slot_a = class_a.slots.as_number.left_ternary_op(op_slot);
306+
let slot_a_addr = slot_a.map(|x| x as usize);
272307
let mut slot_b = None;
308+
let left_b_addr;
273309

274310
if !class_a.is(class_b) {
275311
let slot_bb = class_b.slots.as_number.right_ternary_op(op_slot);
276-
if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
312+
if slot_bb.map(|x| x as usize) != slot_a_addr {
277313
slot_b = slot_bb;
278314
}
315+
left_b_addr = class_b
316+
.slots
317+
.as_number
318+
.left_ternary_op(op_slot)
319+
.map(|x| x as usize);
320+
} else {
321+
left_b_addr = slot_a_addr;
279322
}
280323

281324
if let Some(slot_a) = slot_a {
282325
if let Some(slot_bb) = slot_b
283326
&& class_b.fast_issubclass(class_a)
327+
&& (slot_a_addr != left_b_addr
328+
|| method_is_overloaded(
329+
class_a,
330+
class_b,
331+
op_slot.right_method_name(self),
332+
self,
333+
)?)
284334
{
285335
let ret = slot_bb(a, b, c, self)?;
286336
if !ret.is(&self.ctx.not_implemented) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy