From 21c7e159ec5e4f931508309257fcd4c1d94564ca Mon Sep 17 00:00:00 2001 From: Yash Suthar Date: Mon, 27 Oct 2025 21:57:52 +0530 Subject: [PATCH 1/3] Resolve number slots via MRO in PyNumber and operator, ensure inherited and dynamically added methods are found. Use class().mro_find_map() to mimic the same behaviour as CPython. Signed-off-by: Yash Suthar --- vm/src/protocol/number.rs | 192 +++++++++++++++++++++----------------- vm/src/vm/vm_ops.rs | 22 +++-- 2 files changed, 120 insertions(+), 94 deletions(-) diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index 671877057d..250f64147f 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -443,117 +443,135 @@ impl<'a> PyNumber<'a> { // PyNumber_Check pub fn check(obj: &PyObject) -> bool { - let methods = &obj.class().slots.as_number; - methods.int.load().is_some() - || methods.index.load().is_some() - || methods.float.load().is_some() - || obj.downcastable::() + let cls = &obj.class(); + let has_number = cls + .mro_find_map(|x| { + let methods = &x.slots.as_number; + if methods.int.load().is_some() + || methods.index.load().is_some() + || methods.float.load().is_some() + { + Some(()) + } else { + None + } + }) + .is_some(); + has_number || obj.downcastable::() } } impl PyNumber<'_> { // PyIndex_Check pub fn is_index(self) -> bool { - self.class().slots.as_number.index.load().is_some() + self.class() + .mro_find_map(|x| x.slots.as_number.index.load()) + .is_some() } #[inline] pub fn int(self, vm: &VirtualMachine) -> Option> { - self.class().slots.as_number.int.load().map(|f| { - let ret = f(self, vm)?; - - if let Some(ret) = ret.downcast_ref_if_exact::(vm) { - return Ok(ret.to_owned()); - } - - let ret_class = ret.class().to_owned(); - if let Some(ret) = ret.downcast_ref::() { - warnings::warn( - vm.ctx.exceptions.deprecation_warning, - format!( - "__int__ returned non-int (type {ret_class}). \ + self.class() + .mro_find_map(|x| x.slots.as_number.int.load()) + .map(|f| { + let ret = f(self, vm)?; + + if let Some(ret) = ret.downcast_ref_if_exact::(vm) { + return Ok(ret.to_owned()); + } + + let ret_class = ret.class().to_owned(); + if let Some(ret) = ret.downcast_ref::() { + warnings::warn( + vm.ctx.exceptions.deprecation_warning, + format!( + "__int__ returned non-int (type {ret_class}). \ The ability to return an instance of a strict subclass of int \ is deprecated, and may be removed in a future version of Python." - ), - 1, - vm, - )?; - - Ok(ret.to_owned()) - } else { - Err(vm.new_type_error(format!( - "{}.__int__ returned non-int(type {})", - self.class(), - ret_class - ))) - } - }) + ), + 1, + vm, + )?; + + Ok(ret.to_owned()) + } else { + Err(vm.new_type_error(format!( + "{}.__int__ returned non-int(type {})", + self.class(), + ret_class + ))) + } + }) } #[inline] pub fn index(self, vm: &VirtualMachine) -> Option> { - self.class().slots.as_number.index.load().map(|f| { - let ret = f(self, vm)?; - - if let Some(ret) = ret.downcast_ref_if_exact::(vm) { - return Ok(ret.to_owned()); - } - - let ret_class = ret.class().to_owned(); - if let Some(ret) = ret.downcast_ref::() { - warnings::warn( - vm.ctx.exceptions.deprecation_warning, - format!( - "__index__ returned non-int (type {ret_class}). \ + self.class() + .mro_find_map(|x| x.slots.as_number.index.load()) + .map(|f| { + let ret = f(self, vm)?; + + if let Some(ret) = ret.downcast_ref_if_exact::(vm) { + return Ok(ret.to_owned()); + } + + let ret_class = ret.class().to_owned(); + if let Some(ret) = ret.downcast_ref::() { + warnings::warn( + vm.ctx.exceptions.deprecation_warning, + format!( + "__index__ returned non-int (type {ret_class}). \ The ability to return an instance of a strict subclass of int \ is deprecated, and may be removed in a future version of Python." - ), - 1, - vm, - )?; - - Ok(ret.to_owned()) - } else { - Err(vm.new_type_error(format!( - "{}.__index__ returned non-int(type {})", - self.class(), - ret_class - ))) - } - }) + ), + 1, + vm, + )?; + + Ok(ret.to_owned()) + } else { + Err(vm.new_type_error(format!( + "{}.__index__ returned non-int(type {})", + self.class(), + ret_class + ))) + } + }) } #[inline] pub fn float(self, vm: &VirtualMachine) -> Option>> { - self.class().slots.as_number.float.load().map(|f| { - let ret = f(self, vm)?; - - if let Some(ret) = ret.downcast_ref_if_exact::(vm) { - return Ok(ret.to_owned()); - } - - let ret_class = ret.class().to_owned(); - if let Some(ret) = ret.downcast_ref::() { - warnings::warn( - vm.ctx.exceptions.deprecation_warning, - format!( - "__float__ returned non-float (type {ret_class}). \ + self.class() + .mro_find_map(|x| x.slots.as_number.float.load()) + .map(|f| { + let ret = f(self, vm)?; + + if let Some(ret) = ret.downcast_ref_if_exact::(vm) { + return Ok(ret.to_owned()); + } + + let ret_class = ret.class().to_owned(); + if let Some(ret) = ret.downcast_ref::() { + warnings::warn( + vm.ctx.exceptions.deprecation_warning, + format!( + "__float__ returned non-float (type {ret_class}). \ The ability to return an instance of a strict subclass of float \ is deprecated, and may be removed in a future version of Python." - ), - 1, - vm, - )?; - - Ok(ret.to_owned()) - } else { - Err(vm.new_type_error(format!( - "{}.__float__ returned non-float(type {})", - self.class(), - ret_class - ))) - } - }) + ), + 1, + vm, + )?; + + Ok(ret.to_owned()) + } else { + Err(vm.new_type_error(format!( + "{}.__float__ returned non-float(type {})", + self.class(), + ret_class + ))) + } + }) } } diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index 840b79c258..a5656250c3 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -160,11 +160,12 @@ impl VirtualMachine { let class_a = a.class(); let class_b = b.class(); - let slot_a = class_a.slots.as_number.left_binary_op(op_slot); + // Look up number slots across MRO for inheritance + let slot_a = class_a.mro_find_map(|x| x.slots.as_number.left_binary_op(op_slot)); let mut slot_b = None; if !class_a.is(class_b) { - let slot_bb = class_b.slots.as_number.right_binary_op(op_slot); + let slot_bb = class_b.mro_find_map(|x| x.slots.as_number.right_binary_op(op_slot)); if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) { slot_b = slot_bb; } @@ -230,7 +231,10 @@ impl VirtualMachine { iop_slot: PyNumberBinaryOp, op_slot: PyNumberBinaryOp, ) -> PyResult { - if let Some(slot) = a.class().slots.as_number.left_binary_op(iop_slot) { + if let Some(slot) = a + .class() + .mro_find_map(|x| x.slots.as_number.left_binary_op(iop_slot)) + { let x = slot(a, b, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); @@ -266,11 +270,12 @@ impl VirtualMachine { let class_b = b.class(); let class_c = c.class(); - let slot_a = class_a.slots.as_number.left_ternary_op(op_slot); + // Look up number slots across MRO for inheritance + let slot_a = class_a.mro_find_map(|x| x.slots.as_number.left_ternary_op(op_slot)); let mut slot_b = None; if !class_a.is(class_b) { - let slot_bb = class_b.slots.as_number.right_ternary_op(op_slot); + let slot_bb = class_b.mro_find_map(|x| x.slots.as_number.right_ternary_op(op_slot)); if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) { slot_b = slot_bb; } @@ -299,7 +304,7 @@ impl VirtualMachine { } } - if let Some(slot_c) = class_c.slots.as_number.left_ternary_op(op_slot) + if let Some(slot_c) = class_c.mro_find_map(|x| x.slots.as_number.left_ternary_op(op_slot)) && slot_a.is_some_and(|slot_a| !std::ptr::fn_addr_eq(slot_a, slot_c)) && slot_b.is_some_and(|slot_b| !std::ptr::fn_addr_eq(slot_b, slot_c)) { @@ -338,7 +343,10 @@ impl VirtualMachine { op_slot: PyNumberTernaryOp, op_str: &str, ) -> PyResult { - if let Some(slot) = a.class().slots.as_number.left_ternary_op(iop_slot) { + if let Some(slot) = a + .class() + .mro_find_map(|x| x.slots.as_number.left_ternary_op(iop_slot)) + { let x = slot(a, b, c, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); From 355604d08a9f78e8a54454058102fef3d3f8897f Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 28 Oct 2025 11:03:07 +0900 Subject: [PATCH 2/3] Revert temp __mul__ fix --- Lib/ctypes/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index b8b005061f..a5d27daff0 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -299,9 +299,7 @@ def create_unicode_buffer(init, size=None): return buf elif isinstance(init, int): _sys.audit("ctypes.create_unicode_buffer", None, init) - # XXX: RUSTPYTHON - # buftype = c_wchar * init - buftype = c_wchar.__mul__(init) + buftype = c_wchar * init buf = buftype() return buf raise TypeError(init) From 914bd74fa0c511db03b968b637e6ea3871a394e5 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:00:03 +0900 Subject: [PATCH 3/3] TODO comment --- vm/src/protocol/number.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index 250f64147f..1242ee5279 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -444,6 +444,11 @@ impl<'a> PyNumber<'a> { // PyNumber_Check pub fn check(obj: &PyObject) -> bool { let cls = &obj.class(); + // TODO: when we finally have a proper slot inheritance, mro_find_map can be removed + // methods.int.load().is_some() + // || methods.index.load().is_some() + // || methods.float.load().is_some() + // || obj.downcastable::() let has_number = cls .mro_find_map(|x| { let methods = &x.slots.as_number;