diff --git a/.cspell.dict/cpython.txt b/.cspell.dict/cpython.txt index 441e8af5b8f..cb6f774ed18 100644 --- a/.cspell.dict/cpython.txt +++ b/.cspell.dict/cpython.txt @@ -1,6 +1,7 @@ argtypes asdl asname +attro augassign badcert badsyntax @@ -50,6 +51,7 @@ prec preinitialized pythonw PYTHREAD_NAME +releasebuffer SA_ONSTACK SOABI stackdepth diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 4e04371f626..b3d973237df 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1399,7 +1399,6 @@ class Q2: __qualname__ = object() __slots__ = ["__qualname__"] - @unittest.expectedFailure # TODO: RUSTPYTHON def test_slots_descriptor(self): # Issue2115: slot descriptors did not correctly check # the type of the given object diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 353d7c2e7b2..8f87fa8571a 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -2126,8 +2126,6 @@ class DataDescriptorSub(DataDescriptorWithNoGet, self.assertFalse(inspect.ismethoddescriptor(MethodDescriptorSub)) self.assertFalse(inspect.ismethoddescriptor(DataDescriptorSub)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_builtin_descriptors(self): builtin_slot_wrapper = int.__add__ # This one is mentioned in docs. class Owner: @@ -2217,8 +2215,6 @@ class DataDescriptor2: self.assertTrue(inspect.isdatadescriptor(DataDescriptor2()), 'class with __set__ = None is a data descriptor') - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_slot(self): class Slotted: __slots__ = 'foo', diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index e52c10fe944..c13a7481520 100644 --- a/Lib/test/test_sqlite3/test_factory.py +++ b/Lib/test/test_sqlite3/test_factory.py @@ -241,8 +241,6 @@ def test_sqlite_row_hash_cmp(self): self.assertEqual(hash(row_1), hash(row_2)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_sqlite_row_as_sequence(self): """ Checks if the row object can act like a sequence """ self.con.row_factory = sqlite.Row diff --git a/Lib/test/test_unittest/testmock/testhelpers.py b/Lib/test/test_unittest/testmock/testhelpers.py index 5facac685fd..c83068beb15 100644 --- a/Lib/test/test_unittest/testmock/testhelpers.py +++ b/Lib/test/test_unittest/testmock/testhelpers.py @@ -884,8 +884,6 @@ def f(a, self): pass a.f.assert_called_with(self=10) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_autospec_data_descriptor(self): class Descriptor(object): def __init__(self, value): diff --git a/crates/vm/src/builtins/bool.rs b/crates/vm/src/builtins/bool.rs index 8fee4af3834..cfd1f136d14 100644 --- a/crates/vm/src/builtins/bool.rs +++ b/crates/vm/src/builtins/bool.rs @@ -126,10 +126,10 @@ impl PyBool { .and_then(|format_spec| format_spec.format_bool(new_bool)) .map_err(|err| err.into_pyexception(vm)) } +} - #[pymethod(name = "__ror__")] - #[pymethod] - fn __or__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { +impl PyBool { + pub(crate) fn __or__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if lhs.fast_isinstance(vm.ctx.types.bool_type) && rhs.fast_isinstance(vm.ctx.types.bool_type) { @@ -143,9 +143,7 @@ impl PyBool { } } - #[pymethod(name = "__rand__")] - #[pymethod] - fn __and__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { + pub(crate) fn __and__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if lhs.fast_isinstance(vm.ctx.types.bool_type) && rhs.fast_isinstance(vm.ctx.types.bool_type) { @@ -159,9 +157,7 @@ impl PyBool { } } - #[pymethod(name = "__rxor__")] - #[pymethod] - fn __xor__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { + pub(crate) fn __xor__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { if lhs.fast_isinstance(vm.ctx.types.bool_type) && rhs.fast_isinstance(vm.ctx.types.bool_type) { diff --git a/crates/vm/src/builtins/complex.rs b/crates/vm/src/builtins/complex.rs index 8bf46cfdd5c..ba74d5e0367 100644 --- a/crates/vm/src/builtins/complex.rs +++ b/crates/vm/src/builtins/complex.rs @@ -5,11 +5,7 @@ use crate::{ class::PyClassImpl, common::format::FormatSpec, convert::{IntoPyException, ToPyObject, ToPyResult}, - function::{ - FuncArgs, OptionalArg, OptionalOption, - PyArithmeticValue::{self, *}, - PyComparisonValue, - }, + function::{FuncArgs, OptionalArg, PyComparisonValue}, protocol::PyNumberMethods, stdlib::warnings, types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable}, @@ -268,133 +264,11 @@ impl PyComplex { self.value.im } - #[pymethod] - fn __abs__(&self, vm: &VirtualMachine) -> PyResult { - let Complex64 { im, re } = self.value; - let is_finite = im.is_finite() && re.is_finite(); - let abs_result = re.hypot(im); - if is_finite && abs_result.is_infinite() { - Err(vm.new_overflow_error("absolute value too large")) - } else { - Ok(abs_result) - } - } - - #[inline] - fn op( - &self, - other: PyObjectRef, - op: F, - vm: &VirtualMachine, - ) -> PyResult> - where - F: Fn(Complex64, Complex64) -> PyResult, - { - to_op_complex(&other, vm)?.map_or_else( - || Ok(NotImplemented), - |other| Ok(Implemented(op(self.value, other)?)), - ) - } - - #[pymethod(name = "__radd__")] - #[pymethod] - fn __add__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.op(other, |a, b| Ok(a + b), vm) - } - - #[pymethod] - fn __sub__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.op(other, |a, b| Ok(a - b), vm) - } - - #[pymethod] - fn __rsub__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.op(other, |a, b| Ok(b - a), vm) - } - #[pymethod] fn conjugate(&self) -> Complex64 { self.value.conj() } - #[pymethod(name = "__rmul__")] - #[pymethod] - fn __mul__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.op(other, |a, b| Ok(a * b), vm) - } - - #[pymethod] - fn __truediv__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.op(other, |a, b| inner_div(a, b, vm), vm) - } - - #[pymethod] - fn __rtruediv__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.op(other, |a, b| inner_div(b, a, vm), vm) - } - - #[pymethod] - const fn __pos__(&self) -> Complex64 { - self.value - } - - #[pymethod] - fn __neg__(&self) -> Complex64 { - -self.value - } - - #[pymethod] - fn __pow__( - &self, - other: PyObjectRef, - mod_val: OptionalOption, - vm: &VirtualMachine, - ) -> PyResult> { - if mod_val.flatten().is_some() { - Err(vm.new_value_error("complex modulo not allowed")) - } else { - self.op(other, |a, b| inner_pow(a, b, vm), vm) - } - } - - #[pymethod] - fn __rpow__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.op(other, |a, b| inner_pow(b, a, vm), vm) - } - - #[pymethod] - fn __bool__(&self) -> bool { - !Complex64::is_zero(&self.value) - } - #[pymethod] const fn __getnewargs__(&self) -> (f64, f64) { let Complex64 { re, im } = self.value; @@ -489,7 +363,12 @@ impl AsNumber for PyComplex { }), absolute: Some(|number, vm| { let value = PyComplex::number_downcast(number).value; - value.norm().to_pyresult(vm) + let result = value.norm(); + // Check for overflow: hypot returns inf for finite inputs that overflow + if result.is_infinite() && value.re.is_finite() && value.im.is_finite() { + return Err(vm.new_overflow_error("absolute value too large".to_owned())); + } + result.to_pyresult(vm) }), boolean: Some(|number, _vm| Ok(!PyComplex::number_downcast(number).value.is_zero())), true_divide: Some(|a, b, vm| PyComplex::number_op(a, b, inner_div, vm)), diff --git a/crates/vm/src/builtins/descriptor.rs b/crates/vm/src/builtins/descriptor.rs index 297199eee18..802b81f6d79 100644 --- a/crates/vm/src/builtins/descriptor.rs +++ b/crates/vm/src/builtins/descriptor.rs @@ -4,11 +4,15 @@ use crate::{ builtins::{PyTypeRef, builtin_func::PyNativeMethod, type_}, class::PyClassImpl, common::hash::PyHash, - convert::ToPyResult, + convert::{ToPyObject, ToPyResult}, function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue}, + protocol::{PyNumberBinaryFunc, PyNumberTernaryFunc, PyNumberUnaryFunc}, types::{ - Callable, Comparable, GetDescriptor, HashFunc, Hashable, InitFunc, IterFunc, IterNextFunc, - PyComparisonOp, Representable, StringifyFunc, + Callable, Comparable, DelFunc, DescrGetFunc, DescrSetFunc, GenericMethod, GetDescriptor, + GetattroFunc, HashFunc, Hashable, InitFunc, IterFunc, IterNextFunc, MapAssSubscriptFunc, + MapLenFunc, MapSubscriptFunc, PyComparisonOp, Representable, RichCompareFunc, + SeqAssItemFunc, SeqConcatFunc, SeqContainsFunc, SeqItemFunc, SeqLenFunc, SeqRepeatFunc, + SetattroFunc, StringifyFunc, }, }; use rustpython_common::lock::PyRwLock; @@ -387,22 +391,58 @@ impl GetDescriptor for PyMemberDescriptor { pub fn init(ctx: &Context) { PyMemberDescriptor::extend_class(ctx, ctx.types.member_descriptor_type); PyMethodDescriptor::extend_class(ctx, ctx.types.method_descriptor_type); - PySlotWrapper::extend_class(ctx, ctx.types.wrapper_descriptor_type); + PyWrapper::extend_class(ctx, ctx.types.wrapper_descriptor_type); PyMethodWrapper::extend_class(ctx, ctx.types.method_wrapper_type); } -// PySlotWrapper - wrapper_descriptor +// PyWrapper - wrapper_descriptor -/// Type-erased slot function - mirrors CPython's void* d_wrapped /// Each variant knows how to call the wrapped function with proper types #[derive(Clone, Copy)] pub enum SlotFunc { + // Basic slots Init(InitFunc), Hash(HashFunc), Str(StringifyFunc), Repr(StringifyFunc), Iter(IterFunc), IterNext(IterNextFunc), + Call(GenericMethod), + Del(DelFunc), + + // Attribute access slots + GetAttro(GetattroFunc), + SetAttro(SetattroFunc), // __setattr__ + DelAttro(SetattroFunc), // __delattr__ (same func type, different PySetterValue) + + // Rich comparison slots (with comparison op) + RichCompare(RichCompareFunc, PyComparisonOp), + + // Descriptor slots + DescrGet(DescrGetFunc), + DescrSet(DescrSetFunc), // __set__ + DescrDel(DescrSetFunc), // __delete__ (same func type, different PySetterValue) + + // Sequence sub-slots (sq_*) + SeqLength(SeqLenFunc), + SeqConcat(SeqConcatFunc), + SeqRepeat(SeqRepeatFunc), + SeqItem(SeqItemFunc), + SeqAssItem(SeqAssItemFunc), + SeqContains(SeqContainsFunc), + + // Mapping sub-slots (mp_*) + MapLength(MapLenFunc), + MapSubscript(MapSubscriptFunc), + MapAssSubscript(MapAssSubscriptFunc), + + // Number sub-slots (nb_*) - grouped by signature + NumBoolean(PyNumberUnaryFunc), // __bool__ + NumUnary(PyNumberUnaryFunc), // __int__, __float__, __index__ + NumBinary(PyNumberBinaryFunc), // __add__, __sub__, __mul__, etc. + NumBinaryRight(PyNumberBinaryFunc), // __radd__, __rsub__, etc. (swapped args) + NumTernary(PyNumberTernaryFunc), // __pow__ + NumTernaryRight(PyNumberTernaryFunc), // __rpow__ (swapped first two args) } impl std::fmt::Debug for SlotFunc { @@ -414,6 +454,33 @@ impl std::fmt::Debug for SlotFunc { SlotFunc::Repr(_) => write!(f, "SlotFunc::Repr(...)"), SlotFunc::Iter(_) => write!(f, "SlotFunc::Iter(...)"), SlotFunc::IterNext(_) => write!(f, "SlotFunc::IterNext(...)"), + SlotFunc::Call(_) => write!(f, "SlotFunc::Call(...)"), + SlotFunc::Del(_) => write!(f, "SlotFunc::Del(...)"), + SlotFunc::GetAttro(_) => write!(f, "SlotFunc::GetAttro(...)"), + SlotFunc::SetAttro(_) => write!(f, "SlotFunc::SetAttro(...)"), + SlotFunc::DelAttro(_) => write!(f, "SlotFunc::DelAttro(...)"), + SlotFunc::RichCompare(_, op) => write!(f, "SlotFunc::RichCompare(..., {:?})", op), + SlotFunc::DescrGet(_) => write!(f, "SlotFunc::DescrGet(...)"), + SlotFunc::DescrSet(_) => write!(f, "SlotFunc::DescrSet(...)"), + SlotFunc::DescrDel(_) => write!(f, "SlotFunc::DescrDel(...)"), + // Sequence sub-slots + SlotFunc::SeqLength(_) => write!(f, "SlotFunc::SeqLength(...)"), + SlotFunc::SeqConcat(_) => write!(f, "SlotFunc::SeqConcat(...)"), + SlotFunc::SeqRepeat(_) => write!(f, "SlotFunc::SeqRepeat(...)"), + SlotFunc::SeqItem(_) => write!(f, "SlotFunc::SeqItem(...)"), + SlotFunc::SeqAssItem(_) => write!(f, "SlotFunc::SeqAssItem(...)"), + SlotFunc::SeqContains(_) => write!(f, "SlotFunc::SeqContains(...)"), + // Mapping sub-slots + SlotFunc::MapLength(_) => write!(f, "SlotFunc::MapLength(...)"), + SlotFunc::MapSubscript(_) => write!(f, "SlotFunc::MapSubscript(...)"), + SlotFunc::MapAssSubscript(_) => write!(f, "SlotFunc::MapAssSubscript(...)"), + // Number sub-slots + SlotFunc::NumBoolean(_) => write!(f, "SlotFunc::NumBoolean(...)"), + SlotFunc::NumUnary(_) => write!(f, "SlotFunc::NumUnary(...)"), + SlotFunc::NumBinary(_) => write!(f, "SlotFunc::NumBinary(...)"), + SlotFunc::NumBinaryRight(_) => write!(f, "SlotFunc::NumBinaryRight(...)"), + SlotFunc::NumTernary(_) => write!(f, "SlotFunc::NumTernary(...)"), + SlotFunc::NumTernaryRight(_) => write!(f, "SlotFunc::NumTernaryRight(...)"), } } } @@ -463,6 +530,133 @@ impl SlotFunc { } func(&obj, vm).to_pyresult(vm) } + SlotFunc::Call(func) => func(&obj, args, vm), + SlotFunc::Del(func) => { + if !args.args.is_empty() || !args.kwargs.is_empty() { + return Err( + vm.new_type_error("__del__() takes no arguments (1 given)".to_owned()) + ); + } + func(&obj, vm)?; + Ok(vm.ctx.none()) + } + SlotFunc::GetAttro(func) => { + let (name,): (PyRef,) = args.bind(vm)?; + func(&obj, &name, vm) + } + SlotFunc::SetAttro(func) => { + let (name, value): (PyRef, PyObjectRef) = args.bind(vm)?; + func(&obj, &name, PySetterValue::Assign(value), vm)?; + Ok(vm.ctx.none()) + } + SlotFunc::DelAttro(func) => { + let (name,): (PyRef,) = args.bind(vm)?; + func(&obj, &name, PySetterValue::Delete, vm)?; + Ok(vm.ctx.none()) + } + SlotFunc::RichCompare(func, op) => { + let (other,): (PyObjectRef,) = args.bind(vm)?; + func(&obj, &other, *op, vm).map(|r| match r { + crate::function::Either::A(obj) => obj, + crate::function::Either::B(cmp_val) => cmp_val.to_pyobject(vm), + }) + } + SlotFunc::DescrGet(func) => { + let (instance, owner): (PyObjectRef, crate::function::OptionalArg) = + args.bind(vm)?; + let owner = owner.into_option(); + let instance_opt = if vm.is_none(&instance) { + None + } else { + Some(instance) + }; + func(obj, instance_opt, owner, vm) + } + SlotFunc::DescrSet(func) => { + let (instance, value): (PyObjectRef, PyObjectRef) = args.bind(vm)?; + func(&obj, instance, PySetterValue::Assign(value), vm)?; + Ok(vm.ctx.none()) + } + SlotFunc::DescrDel(func) => { + let (instance,): (PyObjectRef,) = args.bind(vm)?; + func(&obj, instance, PySetterValue::Delete, vm)?; + Ok(vm.ctx.none()) + } + // Sequence sub-slots + SlotFunc::SeqLength(func) => { + args.bind::<()>(vm)?; + let len = func(obj.sequence_unchecked(), vm)?; + Ok(vm.ctx.new_int(len).into()) + } + SlotFunc::SeqConcat(func) => { + let (other,): (PyObjectRef,) = args.bind(vm)?; + func(obj.sequence_unchecked(), &other, vm) + } + SlotFunc::SeqRepeat(func) => { + let (n,): (isize,) = args.bind(vm)?; + func(obj.sequence_unchecked(), n, vm) + } + SlotFunc::SeqItem(func) => { + let (index,): (isize,) = args.bind(vm)?; + func(obj.sequence_unchecked(), index, vm) + } + SlotFunc::SeqAssItem(func) => { + let (index, value): (isize, crate::function::OptionalArg) = + args.bind(vm)?; + func(obj.sequence_unchecked(), index, value.into_option(), vm)?; + Ok(vm.ctx.none()) + } + SlotFunc::SeqContains(func) => { + let (item,): (PyObjectRef,) = args.bind(vm)?; + let result = func(obj.sequence_unchecked(), &item, vm)?; + Ok(vm.ctx.new_bool(result).into()) + } + // Mapping sub-slots + SlotFunc::MapLength(func) => { + args.bind::<()>(vm)?; + let len = func(obj.mapping_unchecked(), vm)?; + Ok(vm.ctx.new_int(len).into()) + } + SlotFunc::MapSubscript(func) => { + let (key,): (PyObjectRef,) = args.bind(vm)?; + func(obj.mapping_unchecked(), &key, vm) + } + SlotFunc::MapAssSubscript(func) => { + let (key, value): (PyObjectRef, crate::function::OptionalArg) = + args.bind(vm)?; + func(obj.mapping_unchecked(), &key, value.into_option(), vm)?; + Ok(vm.ctx.none()) + } + // Number sub-slots + SlotFunc::NumBoolean(func) => { + args.bind::<()>(vm)?; + let result = func(obj.number(), vm)?; + Ok(vm.ctx.new_bool(result).into()) + } + SlotFunc::NumUnary(func) => { + args.bind::<()>(vm)?; + func(obj.number(), vm) + } + SlotFunc::NumBinary(func) => { + let (other,): (PyObjectRef,) = args.bind(vm)?; + func(&obj, &other, vm) + } + SlotFunc::NumBinaryRight(func) => { + let (other,): (PyObjectRef,) = args.bind(vm)?; + func(&other, &obj, vm) // Swapped: other op obj + } + SlotFunc::NumTernary(func) => { + let (y, z): (PyObjectRef, crate::function::OptionalArg) = + args.bind(vm)?; + let z = z.unwrap_or_else(|| vm.ctx.none()); + func(&obj, &y, &z, vm) + } + SlotFunc::NumTernaryRight(func) => { + let (y, z): (PyObjectRef, crate::function::OptionalArg) = + args.bind(vm)?; + let z = z.unwrap_or_else(|| vm.ctx.none()); + func(&y, &obj, &z, vm) // Swapped: y ** obj % z + } } } } @@ -471,20 +665,20 @@ impl SlotFunc { // = PyWrapperDescrObject #[pyclass(name = "wrapper_descriptor", module = false)] #[derive(Debug)] -pub struct PySlotWrapper { +pub struct PyWrapper { pub typ: &'static Py, pub name: &'static PyStrInterned, pub wrapped: SlotFunc, pub doc: Option<&'static str>, } -impl PyPayload for PySlotWrapper { +impl PyPayload for PyWrapper { fn class(ctx: &Context) -> &'static Py { ctx.types.wrapper_descriptor_type } } -impl GetDescriptor for PySlotWrapper { +impl GetDescriptor for PyWrapper { fn descr_get( zelf: PyObjectRef, obj: Option, @@ -501,7 +695,7 @@ impl GetDescriptor for PySlotWrapper { } } -impl Callable for PySlotWrapper { +impl Callable for PyWrapper { type Args = FuncArgs; fn call(zelf: &Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -525,7 +719,7 @@ impl Callable for PySlotWrapper { with(GetDescriptor, Callable, Representable), flags(DISALLOW_INSTANTIATION) )] -impl PySlotWrapper { +impl PyWrapper { #[pygetset] fn __name__(&self) -> &'static PyStrInterned { self.name @@ -547,7 +741,7 @@ impl PySlotWrapper { } } -impl Representable for PySlotWrapper { +impl Representable for PyWrapper { #[inline] fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { Ok(format!( @@ -565,7 +759,7 @@ impl Representable for PySlotWrapper { #[pyclass(name = "method-wrapper", module = false, traverse)] #[derive(Debug)] pub struct PyMethodWrapper { - pub wrapper: PyRef, + pub wrapper: PyRef, #[pytraverse(skip)] pub obj: PyObjectRef, } diff --git a/crates/vm/src/builtins/float.rs b/crates/vm/src/builtins/float.rs index 26182d748a9..f101a3aa8e3 100644 --- a/crates/vm/src/builtins/float.rs +++ b/crates/vm/src/builtins/float.rs @@ -8,8 +8,7 @@ use crate::{ common::{float_ops, format::FormatSpec, hash}, convert::{IntoPyException, ToPyObject, ToPyResult}, function::{ - ArgBytesLike, FuncArgs, OptionalArg, OptionalOption, - PyArithmeticValue::{self, *}, + ArgBytesLike, FuncArgs, OptionalArg, OptionalOption, PyArithmeticValue::*, PyComparisonValue, }, protocol::PyNumberMethods, @@ -239,182 +238,6 @@ impl PyFloat { .to_owned()) } - #[pymethod] - const fn __abs__(&self) -> f64 { - self.value.abs() - } - - #[inline] - fn simple_op( - &self, - other: PyObjectRef, - op: F, - vm: &VirtualMachine, - ) -> PyResult> - where - F: Fn(f64, f64) -> PyResult, - { - to_op_float(&other, vm)?.map_or_else( - || Ok(NotImplemented), - |other| Ok(Implemented(op(self.value, other)?)), - ) - } - - #[inline] - fn complex_op(&self, other: PyObjectRef, op: F, vm: &VirtualMachine) -> PyResult - where - F: Fn(f64, f64) -> PyResult, - { - to_op_float(&other, vm)?.map_or_else( - || Ok(vm.ctx.not_implemented()), - |other| op(self.value, other), - ) - } - - #[inline] - fn tuple_op( - &self, - other: PyObjectRef, - op: F, - vm: &VirtualMachine, - ) -> PyResult> - where - F: Fn(f64, f64) -> PyResult<(f64, f64)>, - { - to_op_float(&other, vm)?.map_or_else( - || Ok(NotImplemented), - |other| Ok(Implemented(op(self.value, other)?)), - ) - } - - #[pymethod(name = "__radd__")] - #[pymethod] - fn __add__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { - self.simple_op(other, |a, b| Ok(a + b), vm) - } - - #[pymethod] - const fn __bool__(&self) -> bool { - self.value != 0.0 - } - - #[pymethod] - fn __divmod__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.tuple_op(other, |a, b| inner_divmod(a, b, vm), vm) - } - - #[pymethod] - fn __rdivmod__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.tuple_op(other, |a, b| inner_divmod(b, a, vm), vm) - } - - #[pymethod] - fn __floordiv__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.simple_op(other, |a, b| inner_floordiv(a, b, vm), vm) - } - - #[pymethod] - fn __rfloordiv__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.simple_op(other, |a, b| inner_floordiv(b, a, vm), vm) - } - - #[pymethod(name = "__mod__")] - fn mod_(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { - self.simple_op(other, |a, b| inner_mod(a, b, vm), vm) - } - - #[pymethod] - fn __rmod__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.simple_op(other, |a, b| inner_mod(b, a, vm), vm) - } - - #[pymethod] - const fn __pos__(&self) -> f64 { - self.value - } - - #[pymethod] - const fn __neg__(&self) -> f64 { - -self.value - } - - #[pymethod] - fn __pow__( - &self, - other: PyObjectRef, - mod_val: OptionalOption, - vm: &VirtualMachine, - ) -> PyResult { - if mod_val.flatten().is_some() { - Err(vm.new_type_error("floating point pow() does not accept a 3rd argument")) - } else { - self.complex_op(other, |a, b| float_pow(a, b, vm), vm) - } - } - - #[pymethod] - fn __rpow__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.complex_op(other, |a, b| float_pow(b, a, vm), vm) - } - - #[pymethod] - fn __sub__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { - self.simple_op(other, |a, b| Ok(a - b), vm) - } - - #[pymethod] - fn __rsub__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.simple_op(other, |a, b| Ok(b - a), vm) - } - - #[pymethod] - fn __truediv__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.simple_op(other, |a, b| inner_div(a, b, vm), vm) - } - - #[pymethod] - fn __rtruediv__( - &self, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult> { - self.simple_op(other, |a, b| inner_div(b, a, vm), vm) - } - - #[pymethod(name = "__rmul__")] - #[pymethod] - fn __mul__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { - self.simple_op(other, |a, b| Ok(a * b), vm) - } - #[pymethod] fn __trunc__(&self, vm: &VirtualMachine) -> PyResult { try_to_bigint(self.value, vm) @@ -460,16 +283,6 @@ impl PyFloat { Ok(value) } - #[pymethod] - fn __int__(&self, vm: &VirtualMachine) -> PyResult { - self.__trunc__(vm) - } - - #[pymethod] - const fn __float__(zelf: PyRef) -> PyRef { - zelf - } - #[pygetset] const fn real(zelf: PyRef) -> PyRef { zelf diff --git a/crates/vm/src/builtins/int.rs b/crates/vm/src/builtins/int.rs index 46a0ff4774d..37b41e085ad 100644 --- a/crates/vm/src/builtins/int.rs +++ b/crates/vm/src/builtins/int.rs @@ -12,8 +12,7 @@ use crate::{ }, convert::{IntoPyException, ToPyObject, ToPyResult}, function::{ - ArgByteOrder, ArgIntoBool, FuncArgs, OptionalArg, OptionalOption, PyArithmeticValue, - PyComparisonValue, + ArgByteOrder, ArgIntoBool, FuncArgs, OptionalArg, PyArithmeticValue, PyComparisonValue, }, protocol::{PyNumberMethods, handle_bytes_to_int_err}, types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable}, @@ -325,83 +324,15 @@ impl PyInt { with(PyRef, Comparable, Hashable, Constructor, AsNumber, Representable) )] impl PyInt { - #[pymethod(name = "__radd__")] - #[pymethod] - fn __add__(&self, other: PyObjectRef) -> PyArithmeticValue { - self.int_op(other, |a, b| a + b) - } - - #[pymethod] - fn __sub__(&self, other: PyObjectRef) -> PyArithmeticValue { - self.int_op(other, |a, b| a - b) - } - - #[pymethod] - fn __rsub__(&self, other: PyObjectRef) -> PyArithmeticValue { - self.int_op(other, |a, b| b - a) - } - - #[pymethod(name = "__rmul__")] - #[pymethod] - fn __mul__(&self, other: PyObjectRef) -> PyArithmeticValue { - self.int_op(other, |a, b| a * b) - } - - #[pymethod] - fn __truediv__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_truediv(a, b, vm), vm) - } - - #[pymethod] - fn __rtruediv__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_truediv(b, a, vm), vm) - } - - #[pymethod] - fn __floordiv__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_floordiv(a, b, vm), vm) - } - - #[pymethod] - fn __rfloordiv__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_floordiv(b, a, vm), vm) - } - - #[pymethod] - fn __lshift__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_lshift(a, b, vm), vm) - } - - #[pymethod] - fn __rlshift__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_lshift(b, a, vm), vm) - } - - #[pymethod] - fn __rshift__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_rshift(a, b, vm), vm) - } - - #[pymethod] - fn __rrshift__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_rshift(b, a, vm), vm) - } - - #[pymethod(name = "__rxor__")] - #[pymethod] - pub fn __xor__(&self, other: PyObjectRef) -> PyArithmeticValue { + pub(crate) fn __xor__(&self, other: PyObjectRef) -> PyArithmeticValue { self.int_op(other, |a, b| a ^ b) } - #[pymethod(name = "__ror__")] - #[pymethod] - pub fn __or__(&self, other: PyObjectRef) -> PyArithmeticValue { + pub(crate) fn __or__(&self, other: PyObjectRef) -> PyArithmeticValue { self.int_op(other, |a, b| a | b) } - #[pymethod(name = "__rand__")] - #[pymethod] - pub fn __and__(&self, other: PyObjectRef) -> PyArithmeticValue { + pub(crate) fn __and__(&self, other: PyObjectRef) -> PyArithmeticValue { self.int_op(other, |a, b| a & b) } @@ -447,54 +378,6 @@ impl PyInt { ) } - #[pymethod] - fn __pow__( - &self, - other: PyObjectRef, - r#mod: OptionalOption, - vm: &VirtualMachine, - ) -> PyResult { - match r#mod.flatten() { - Some(modulus) => self.modpow(other, modulus, vm), - None => self.general_op(other, |a, b| inner_pow(a, b, vm), vm), - } - } - - #[pymethod] - fn __rpow__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_pow(b, a, vm), vm) - } - - #[pymethod(name = "__mod__")] - fn mod_(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_mod(a, b, vm), vm) - } - - #[pymethod] - fn __rmod__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_mod(b, a, vm), vm) - } - - #[pymethod] - fn __divmod__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_divmod(a, b, vm), vm) - } - - #[pymethod] - fn __rdivmod__(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { - self.general_op(other, |a, b| inner_divmod(b, a, vm), vm) - } - - #[pymethod] - fn __neg__(&self) -> BigInt { - -(&self.value) - } - - #[pymethod] - fn __abs__(&self) -> BigInt { - self.value.abs() - } - #[pymethod] fn __round__( zelf: PyRef, @@ -537,16 +420,6 @@ impl PyInt { Ok(zelf) } - #[pymethod] - fn __pos__(&self) -> BigInt { - self.value.clone() - } - - #[pymethod] - fn __float__(&self, vm: &VirtualMachine) -> PyResult { - try_to_float(&self.value, vm) - } - #[pymethod] fn __trunc__(zelf: PyRef, vm: &VirtualMachine) -> PyRefExact { zelf.__int__(vm) @@ -562,16 +435,6 @@ impl PyInt { zelf.__int__(vm) } - #[pymethod] - fn __index__(zelf: PyRef, vm: &VirtualMachine) -> PyRefExact { - zelf.__int__(vm) - } - - #[pymethod] - fn __invert__(&self) -> BigInt { - !(&self.value) - } - #[pymethod] fn __format__(&self, spec: PyStrRef, vm: &VirtualMachine) -> PyResult { FormatSpec::parse(spec.as_str()) @@ -579,11 +442,6 @@ impl PyInt { .map_err(|err| err.into_pyexception(vm)) } - #[pymethod] - fn __bool__(&self) -> bool { - !self.value.is_zero() - } - #[pymethod] fn __sizeof__(&self) -> usize { std::mem::size_of::() + (((self.value.bits() + 7) & !7) / 8) as usize @@ -706,8 +564,7 @@ impl PyInt { #[pyclass] impl PyRef { - #[pymethod] - fn __int__(self, vm: &VirtualMachine) -> PyRefExact { + pub(crate) fn __int__(self, vm: &VirtualMachine) -> PyRefExact { self.into_exact_or(&vm.ctx, |zelf| unsafe { // TODO: this is actually safe. we need better interface PyRefExact::new_unchecked(vm.ctx.new_bigint(&zelf.value)) diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs index ee07c0db0b9..f74095a8c8a 100644 --- a/crates/vm/src/builtins/type.rs +++ b/crates/vm/src/builtins/type.rs @@ -26,7 +26,7 @@ use crate::{ protocol::{PyIterReturn, PyNumberMethods}, types::{ AsNumber, Callable, Constructor, GetAttr, Initializer, PyTypeFlags, PyTypeSlots, - Representable, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot, + Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot, }, }; use indexmap::{IndexMap, map::Entry}; @@ -464,173 +464,11 @@ impl PyType { /// Inherit slots from base type. inherit_slots pub(crate) fn inherit_slots(&self, base: &Self) { - macro_rules! copyslot { - ($slot:ident) => { - if self.slots.$slot.load().is_none() { - if let Some(base_val) = base.slots.$slot.load() { - self.slots.$slot.store(Some(base_val)); - } - } - }; - } - - // Copy init slot only if base actually defines it (not just inherited) - // This is needed for multiple inheritance where a later base might - // have a more specific init slot - macro_rules! copyslot_defined { - ($slot:ident) => { - if self.slots.$slot.load().is_none() { - if let Some(base_val) = base.slots.$slot.load() { - // SLOTDEFINED: base->SLOT && (basebase == NULL || base->SLOT != basebase->SLOT) - let basebase = base.base.as_ref(); - let slot_defined = match basebase { - None => true, - Some(bb) => bb.slots.$slot.load().map(|v| v as usize) - != Some(base_val as usize), - }; - if slot_defined { - self.slots.$slot.store(Some(base_val)); - } - } - } - }; - } - - // Core slots - copyslot!(hash); - copyslot!(call); - copyslot!(str); - copyslot!(repr); - copyslot!(getattro); - copyslot!(setattro); - copyslot!(richcompare); - copyslot!(iter); - copyslot!(iternext); - copyslot!(descr_get); - copyslot!(descr_set); - // init uses SLOTDEFINED check for multiple inheritance support - copyslot_defined!(init); - copyslot!(del); - // new is handled by set_new() - // as_buffer is inherited at type creation time (not AtomicCell) - - // Sub-slots (number, sequence, mapping) - self.inherit_number_slots(base); - self.inherit_sequence_slots(base); - self.inherit_mapping_slots(base); - } - - /// Inherit number sub-slots from base type - fn inherit_number_slots(&self, base: &Self) { - macro_rules! copy_num_slot { - ($slot:ident) => { - if self.slots.as_number.$slot.load().is_none() { - if let Some(base_val) = base.slots.as_number.$slot.load() { - self.slots.as_number.$slot.store(Some(base_val)); - } - } - }; + // Use SLOT_DEFS to iterate all slots + // Note: as_buffer is handled in inherit_readonly_slots (not AtomicCell) + for def in SLOT_DEFS { + def.accessor.copyslot_if_none(self, base); } - - // Binary operations - copy_num_slot!(add); - copy_num_slot!(right_add); - copy_num_slot!(inplace_add); - copy_num_slot!(subtract); - copy_num_slot!(right_subtract); - copy_num_slot!(inplace_subtract); - copy_num_slot!(multiply); - copy_num_slot!(right_multiply); - copy_num_slot!(inplace_multiply); - copy_num_slot!(remainder); - copy_num_slot!(right_remainder); - copy_num_slot!(inplace_remainder); - copy_num_slot!(divmod); - copy_num_slot!(right_divmod); - copy_num_slot!(power); - copy_num_slot!(right_power); - copy_num_slot!(inplace_power); - - // Bitwise operations - copy_num_slot!(lshift); - copy_num_slot!(right_lshift); - copy_num_slot!(inplace_lshift); - copy_num_slot!(rshift); - copy_num_slot!(right_rshift); - copy_num_slot!(inplace_rshift); - copy_num_slot!(and); - copy_num_slot!(right_and); - copy_num_slot!(inplace_and); - copy_num_slot!(xor); - copy_num_slot!(right_xor); - copy_num_slot!(inplace_xor); - copy_num_slot!(or); - copy_num_slot!(right_or); - copy_num_slot!(inplace_or); - - // Division operations - copy_num_slot!(floor_divide); - copy_num_slot!(right_floor_divide); - copy_num_slot!(inplace_floor_divide); - copy_num_slot!(true_divide); - copy_num_slot!(right_true_divide); - copy_num_slot!(inplace_true_divide); - - // Matrix multiplication - copy_num_slot!(matrix_multiply); - copy_num_slot!(right_matrix_multiply); - copy_num_slot!(inplace_matrix_multiply); - - // Unary operations - copy_num_slot!(negative); - copy_num_slot!(positive); - copy_num_slot!(absolute); - copy_num_slot!(boolean); - copy_num_slot!(invert); - - // Conversion - copy_num_slot!(int); - copy_num_slot!(float); - copy_num_slot!(index); - } - - /// Inherit sequence sub-slots from base type - fn inherit_sequence_slots(&self, base: &Self) { - macro_rules! copy_seq_slot { - ($slot:ident) => { - if self.slots.as_sequence.$slot.load().is_none() { - if let Some(base_val) = base.slots.as_sequence.$slot.load() { - self.slots.as_sequence.$slot.store(Some(base_val)); - } - } - }; - } - - copy_seq_slot!(length); - copy_seq_slot!(concat); - copy_seq_slot!(repeat); - copy_seq_slot!(item); - copy_seq_slot!(ass_item); - copy_seq_slot!(contains); - copy_seq_slot!(inplace_concat); - copy_seq_slot!(inplace_repeat); - } - - /// Inherit mapping sub-slots from base type - fn inherit_mapping_slots(&self, base: &Self) { - macro_rules! copy_map_slot { - ($slot:ident) => { - if self.slots.as_mapping.$slot.load().is_none() { - if let Some(base_val) = base.slots.as_mapping.$slot.load() { - self.slots.as_mapping.$slot.store(Some(base_val)); - } - } - }; - } - - copy_map_slot!(length); - copy_map_slot!(subscript); - copy_map_slot!(ass_subscript); } // This is used for class initialization where the vm is not yet available. diff --git a/crates/vm/src/class.rs b/crates/vm/src/class.rs index 577fd7d6844..ce41abcc60b 100644 --- a/crates/vm/src/class.rs +++ b/crates/vm/src/class.rs @@ -2,17 +2,60 @@ use crate::{ PyPayload, - builtins::{ - PyBaseObject, PyType, PyTypeRef, - descriptor::{PySlotWrapper, SlotFunc}, - }, + builtins::{PyBaseObject, PyType, PyTypeRef, descriptor::PyWrapper}, function::PyMethodDef, object::Py, - types::{PyTypeFlags, PyTypeSlots, hash_not_implemented}, + types::{PyTypeFlags, PyTypeSlots, SLOT_DEFS, hash_not_implemented}, vm::Context, }; use rustpython_common::static_cell; +/// Add slot wrapper descriptors to a type's dict +/// +/// Iterates SLOT_DEFS and creates a PyWrapper for each slot that: +/// 1. Has a function set in the type's slots +/// 2. Doesn't already have an attribute in the type's dict +pub fn add_operators(class: &'static Py, ctx: &Context) { + for def in SLOT_DEFS.iter() { + // Skip __new__ - it has special handling + if def.name == "__new__" { + continue; + } + + // Special handling for __hash__ = None + if def.name == "__hash__" + && class + .slots + .hash + .load() + .is_some_and(|h| h as usize == hash_not_implemented as usize) + { + class.set_attr(ctx.names.__hash__, ctx.none.clone().into()); + continue; + } + + // Get the slot function wrapped in SlotFunc + let Some(slot_func) = def.accessor.get_slot_func_with_op(&class.slots, def.op) else { + continue; + }; + + // Check if attribute already exists in dict + let attr_name = ctx.intern_str(def.name); + if class.attributes.read().contains_key(attr_name) { + continue; + } + + // Create and add the wrapper + let wrapper = PyWrapper { + typ: class, + name: attr_name, + wrapped: slot_func, + doc: Some(def.doc), + }; + class.set_attr(attr_name, wrapper.into_ref(ctx).into()); + } +} + pub trait StaticType { // Ideally, saving PyType is better than PyTypeRef fn static_cell() -> &'static static_cell::StaticCell; @@ -140,42 +183,8 @@ pub trait PyClassImpl: PyClassDef { } } - // Add slot wrappers for slots that exist and are not already in dict - // This mirrors CPython's add_operators() in typeobject.c - macro_rules! add_slot_wrapper { - ($slot:ident, $name:ident, $variant:ident, $doc:expr) => { - if let Some(func) = class.slots.$slot.load() { - let attr_name = identifier!(ctx, $name); - if !class.attributes.read().contains_key(attr_name) { - let wrapper = PySlotWrapper { - typ: class, - name: ctx.intern_str(stringify!($name)), - wrapped: SlotFunc::$variant(func), - doc: Some($doc), - }; - class.set_attr(attr_name, wrapper.into_ref(ctx).into()); - } - } - }; - } - - add_slot_wrapper!( - init, - __init__, - Init, - "Initialize self. See help(type(self)) for accurate signature." - ); - add_slot_wrapper!(repr, __repr__, Repr, "Return repr(self)."); - add_slot_wrapper!(str, __str__, Str, "Return str(self)."); - add_slot_wrapper!(iter, __iter__, Iter, "Implement iter(self)."); - add_slot_wrapper!(iternext, __next__, IterNext, "Implement next(self)."); - - // __hash__ needs special handling: hash_not_implemented sets __hash__ = None - if class.slots.hash.load().map_or(0, |h| h as usize) == hash_not_implemented as usize { - class.set_attr(ctx.names.__hash__, ctx.none.clone().into()); - } else { - add_slot_wrapper!(hash, __hash__, Hash, "Return hash(self)."); - } + // Add slot wrappers using SLOT_DEFS array + add_operators(class, ctx); // Inherit slots from base types after slots are fully initialized for base in class.bases.read().iter() { diff --git a/crates/vm/src/protocol/mod.rs b/crates/vm/src/protocol/mod.rs index e7be286c265..6eb2909f167 100644 --- a/crates/vm/src/protocol/mod.rs +++ b/crates/vm/src/protocol/mod.rs @@ -12,6 +12,6 @@ pub use iter::{PyIter, PyIterIter, PyIterReturn}; pub use mapping::{PyMapping, PyMappingMethods, PyMappingSlots}; pub use number::{ PyNumber, PyNumberBinaryFunc, PyNumberBinaryOp, PyNumberMethods, PyNumberSlots, - PyNumberTernaryOp, PyNumberUnaryFunc, handle_bytes_to_int_err, + PyNumberTernaryFunc, PyNumberTernaryOp, PyNumberUnaryFunc, handle_bytes_to_int_err, }; pub use sequence::{PySequence, PySequenceMethods, PySequenceSlots}; diff --git a/crates/vm/src/protocol/number.rs b/crates/vm/src/protocol/number.rs index 4f21a1e64f9..c208bf26de8 100644 --- a/crates/vm/src/protocol/number.rs +++ b/crates/vm/src/protocol/number.rs @@ -256,6 +256,7 @@ pub struct PyNumberSlots { pub int: AtomicCell>, pub float: AtomicCell>, + // Right variants (internal - not exposed in SlotAccessor) pub right_add: AtomicCell>, pub right_subtract: AtomicCell>, pub right_multiply: AtomicCell>, @@ -295,8 +296,7 @@ pub struct PyNumberSlots { impl From<&PyNumberMethods> for PyNumberSlots { fn from(value: &PyNumberMethods) -> Self { - // right_* functions will use the same left function as PyNumberMethods - // allows both f(self, other) and f(other, self) + // right_* slots use the same function as left ops for native types Self { add: AtomicCell::new(value.add), subtract: AtomicCell::new(value.subtract), @@ -352,6 +352,115 @@ impl From<&PyNumberMethods> for PyNumberSlots { } impl PyNumberSlots { + /// Copy from static PyNumberMethods + pub fn copy_from(&self, methods: &PyNumberMethods) { + if let Some(f) = methods.add { + self.add.store(Some(f)); + } + if let Some(f) = methods.subtract { + self.subtract.store(Some(f)); + } + if let Some(f) = methods.multiply { + self.multiply.store(Some(f)); + } + if let Some(f) = methods.remainder { + self.remainder.store(Some(f)); + } + if let Some(f) = methods.divmod { + self.divmod.store(Some(f)); + } + if let Some(f) = methods.power { + self.power.store(Some(f)); + } + if let Some(f) = methods.negative { + self.negative.store(Some(f)); + } + if let Some(f) = methods.positive { + self.positive.store(Some(f)); + } + if let Some(f) = methods.absolute { + self.absolute.store(Some(f)); + } + if let Some(f) = methods.boolean { + self.boolean.store(Some(f)); + } + if let Some(f) = methods.invert { + self.invert.store(Some(f)); + } + if let Some(f) = methods.lshift { + self.lshift.store(Some(f)); + } + if let Some(f) = methods.rshift { + self.rshift.store(Some(f)); + } + if let Some(f) = methods.and { + self.and.store(Some(f)); + } + if let Some(f) = methods.xor { + self.xor.store(Some(f)); + } + if let Some(f) = methods.or { + self.or.store(Some(f)); + } + if let Some(f) = methods.int { + self.int.store(Some(f)); + } + if let Some(f) = methods.float { + self.float.store(Some(f)); + } + if let Some(f) = methods.inplace_add { + self.inplace_add.store(Some(f)); + } + if let Some(f) = methods.inplace_subtract { + self.inplace_subtract.store(Some(f)); + } + if let Some(f) = methods.inplace_multiply { + self.inplace_multiply.store(Some(f)); + } + if let Some(f) = methods.inplace_remainder { + self.inplace_remainder.store(Some(f)); + } + if let Some(f) = methods.inplace_power { + self.inplace_power.store(Some(f)); + } + if let Some(f) = methods.inplace_lshift { + self.inplace_lshift.store(Some(f)); + } + if let Some(f) = methods.inplace_rshift { + self.inplace_rshift.store(Some(f)); + } + if let Some(f) = methods.inplace_and { + self.inplace_and.store(Some(f)); + } + if let Some(f) = methods.inplace_xor { + self.inplace_xor.store(Some(f)); + } + if let Some(f) = methods.inplace_or { + self.inplace_or.store(Some(f)); + } + if let Some(f) = methods.floor_divide { + self.floor_divide.store(Some(f)); + } + if let Some(f) = methods.true_divide { + self.true_divide.store(Some(f)); + } + if let Some(f) = methods.inplace_floor_divide { + self.inplace_floor_divide.store(Some(f)); + } + if let Some(f) = methods.inplace_true_divide { + self.inplace_true_divide.store(Some(f)); + } + if let Some(f) = methods.index { + self.index.store(Some(f)); + } + if let Some(f) = methods.matrix_multiply { + self.matrix_multiply.store(Some(f)); + } + if let Some(f) = methods.inplace_matrix_multiply { + self.inplace_matrix_multiply.store(Some(f)); + } + } + pub fn left_binary_op(&self, op_slot: PyNumberBinaryOp) -> Option { use PyNumberBinaryOp::*; match op_slot { diff --git a/crates/vm/src/types/mod.rs b/crates/vm/src/types/mod.rs index f19328cdd2d..b17a737545f 100644 --- a/crates/vm/src/types/mod.rs +++ b/crates/vm/src/types/mod.rs @@ -1,7 +1,9 @@ mod slot; +pub mod slot_defs; mod structseq; mod zoo; pub use slot::*; +pub use slot_defs::{SLOT_DEFS, SlotAccessor, SlotDef}; pub use structseq::{PyStructSequence, PyStructSequenceData, struct_sequence_new}; pub(crate) use zoo::TypeZoo; diff --git a/crates/vm/src/types/slot.rs b/crates/vm/src/types/slot.rs index 0d6b17ae99d..b7c20041d00 100644 --- a/crates/vm/src/types/slot.rs +++ b/crates/vm/src/types/slot.rs @@ -14,6 +14,7 @@ use crate::{ PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyMappingSlots, PyNumber, PyNumberMethods, PyNumberSlots, PySequence, PySequenceMethods, PySequenceSlots, }, + types::slot_defs::{SlotAccessor, find_slot_defs_by_name}, vm::Context, }; use crossbeam_utils::atomic::AtomicCell; @@ -288,6 +289,21 @@ pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult<()>; pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>; +// Sequence sub-slot function types +pub(crate) type SeqLenFunc = fn(PySequence<'_>, &VirtualMachine) -> PyResult; +pub(crate) type SeqConcatFunc = fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult; +pub(crate) type SeqRepeatFunc = fn(PySequence<'_>, isize, &VirtualMachine) -> PyResult; +pub(crate) type SeqItemFunc = fn(PySequence<'_>, isize, &VirtualMachine) -> PyResult; +pub(crate) type SeqAssItemFunc = + fn(PySequence<'_>, isize, Option, &VirtualMachine) -> PyResult<()>; +pub(crate) type SeqContainsFunc = fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult; + +// Mapping sub-slot function types +pub(crate) type MapLenFunc = fn(PyMapping<'_>, &VirtualMachine) -> PyResult; +pub(crate) type MapSubscriptFunc = fn(PyMapping<'_>, &PyObject, &VirtualMachine) -> PyResult; +pub(crate) type MapAssSubscriptFunc = + fn(PyMapping<'_>, &PyObject, Option, &VirtualMachine) -> PyResult<()>; + // slot_sq_length pub(crate) fn len_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult { let ret = vm.call_special_method(obj, identifier!(vm, __len__), ())?; @@ -331,6 +347,28 @@ macro_rules! number_binary_right_op_wrapper { |a, b, vm| vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(),)) }; } +macro_rules! number_ternary_op_wrapper { + ($name:ident) => { + |a, b, c, vm: &VirtualMachine| { + if vm.is_none(c) { + vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(),)) + } else { + vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(), c.to_owned())) + } + } + }; +} +macro_rules! number_ternary_right_op_wrapper { + ($name:ident) => { + |a, b, c, vm: &VirtualMachine| { + if vm.is_none(c) { + vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(),)) + } else { + vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(), c.to_owned())) + } + } + }; +} fn getitem_wrapper(obj: &PyObject, needle: K, vm: &VirtualMachine) -> PyResult { vm.call_special_method(obj, identifier!(vm, __getitem__), (needle,)) } @@ -507,453 +545,717 @@ fn del_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { } impl PyType { + /// Update slots based on dunder method changes + /// + /// Iterates SLOT_DEFS to find all slots matching the given name and updates them. pub(crate) fn update_slot(&self, name: &'static PyStrInterned, ctx: &Context) { debug_assert!(name.as_str().starts_with("__")); debug_assert!(name.as_str().ends_with("__")); - macro_rules! toggle_slot { - ($name:ident, $func:expr) => {{ + // Find all slot_defs matching this name and update each + for def in find_slot_defs_by_name(name.as_str()) { + self.update_one_slot::(&def.accessor, name, ctx); + } + } + + /// Update a single slot + fn update_one_slot( + &self, + accessor: &SlotAccessor, + name: &'static PyStrInterned, + ctx: &Context, + ) { + use crate::builtins::descriptor::SlotFunc; + + // Helper macro for main slots + macro_rules! update_main_slot { + ($slot:ident, $wrapper:expr, $variant:ident) => {{ if ADD { - self.slots.$name.store(Some($func)); + if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| { + if let SlotFunc::$variant(f) = sf { + Some(*f) + } else { + None + } + }) { + self.slots.$slot.store(Some(func)); + } else { + self.slots.$slot.store(Some($wrapper)); + } } else { - // When deleting, re-inherit from MRO (skip self) - let inherited = self - .mro - .read() - .iter() - .skip(1) - .find_map(|cls| cls.slots.$name.load()); - self.slots.$name.store(inherited); + accessor.inherit_from_mro(self); } }}; } - macro_rules! toggle_sub_slot { - ($group:ident, $name:ident, $func:expr) => {{ + // Helper macro for number/sequence/mapping sub-slots + macro_rules! update_sub_slot { + ($group:ident, $slot:ident, $wrapper:expr, $variant:ident) => {{ if ADD { - self.slots.$group.$name.store(Some($func)); + if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| { + if let SlotFunc::$variant(f) = sf { + Some(*f) + } else { + None + } + }) { + self.slots.$group.$slot.store(Some(func)); + } else { + self.slots.$group.$slot.store(Some($wrapper)); + } } else { - // When deleting, re-inherit from MRO (skip self) - let inherited = self - .mro - .read() - .iter() - .skip(1) - .find_map(|cls| cls.slots.$group.$name.load()); - self.slots.$group.$name.store(inherited); + accessor.inherit_from_mro(self); } }}; } - macro_rules! update_slot { - ($name:ident, $func:expr) => {{ - self.slots.$name.store(Some($func)); - }}; - } - - match name { - _ if name == identifier!(ctx, __len__) => { - toggle_sub_slot!(as_sequence, length, |seq, vm| len_wrapper(seq.obj, vm)); - toggle_sub_slot!(as_mapping, length, |mapping, vm| len_wrapper( - mapping.obj, - vm - )); - } - _ if name == identifier!(ctx, __getitem__) => { - toggle_sub_slot!(as_sequence, item, |seq, i, vm| getitem_wrapper( - seq.obj, i, vm - )); - toggle_sub_slot!(as_mapping, subscript, |mapping, key, vm| { - getitem_wrapper(mapping.obj, key, vm) - }); - } - _ if name == identifier!(ctx, __setitem__) || name == identifier!(ctx, __delitem__) => { - toggle_sub_slot!(as_sequence, ass_item, |seq, i, value, vm| { - setitem_wrapper(seq.obj, i, value, vm) - }); - toggle_sub_slot!(as_mapping, ass_subscript, |mapping, key, value, vm| { - setitem_wrapper(mapping.obj, key, value, vm) - }); - } - _ if name == identifier!(ctx, __contains__) => { - toggle_sub_slot!(as_sequence, contains, |seq, needle, vm| { - contains_wrapper(seq.obj, needle, vm) - }); - } - _ if name == identifier!(ctx, __repr__) => { - update_slot!(repr, repr_wrapper); - } - _ if name == identifier!(ctx, __str__) => { - update_slot!(str, str_wrapper); - } - _ if name == identifier!(ctx, __hash__) => { - let is_unhashable = self - .attributes - .read() - .get(identifier!(ctx, __hash__)) - .is_some_and(|a| a.is(&ctx.none)); - let wrapper = if is_unhashable { - hash_not_implemented + match accessor { + // === Main slots === + SlotAccessor::TpRepr => update_main_slot!(repr, repr_wrapper, Repr), + SlotAccessor::TpStr => update_main_slot!(str, str_wrapper, Str), + SlotAccessor::TpHash => { + // Special handling for __hash__ = None + if ADD { + let method = self.attributes.read().get(name).cloned().or_else(|| { + self.mro + .read() + .iter() + .find_map(|cls| cls.attributes.read().get(name).cloned()) + }); + + if method.as_ref().is_some_and(|m| m.is(&ctx.none)) { + self.slots.hash.store(Some(hash_not_implemented)); + } else if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| { + if let SlotFunc::Hash(f) = sf { + Some(*f) + } else { + None + } + }) { + self.slots.hash.store(Some(func)); + } else { + self.slots.hash.store(Some(hash_wrapper)); + } } else { - hash_wrapper - }; - toggle_slot!(hash, wrapper); - } - _ if name == identifier!(ctx, __call__) => { - toggle_slot!(call, call_wrapper); - } - _ if name == identifier!(ctx, __getattr__) - || name == identifier!(ctx, __getattribute__) => - { - update_slot!(getattro, getattro_wrapper); - } - _ if name == identifier!(ctx, __setattr__) || name == identifier!(ctx, __delattr__) => { - update_slot!(setattro, setattro_wrapper); - } - _ if name == identifier!(ctx, __eq__) - || name == identifier!(ctx, __ne__) - || name == identifier!(ctx, __le__) - || name == identifier!(ctx, __lt__) - || name == identifier!(ctx, __ge__) - || name == identifier!(ctx, __gt__) => - { - update_slot!(richcompare, richcompare_wrapper); - } - _ if name == identifier!(ctx, __iter__) => { - toggle_slot!(iter, iter_wrapper); - } - _ if name == identifier!(ctx, __next__) => { - toggle_slot!(iternext, iternext_wrapper); - } - _ if name == identifier!(ctx, __get__) => { - toggle_slot!(descr_get, descr_get_wrapper); + accessor.inherit_from_mro(self); + } } - _ if name == identifier!(ctx, __set__) || name == identifier!(ctx, __delete__) => { - update_slot!(descr_set, descr_set_wrapper); + SlotAccessor::TpCall => update_main_slot!(call, call_wrapper, Call), + SlotAccessor::TpIter => update_main_slot!(iter, iter_wrapper, Iter), + SlotAccessor::TpIternext => update_main_slot!(iternext, iternext_wrapper, IterNext), + SlotAccessor::TpInit => update_main_slot!(init, init_wrapper, Init), + SlotAccessor::TpNew => { + // __new__ is not wrapped via PyWrapper + if ADD { + self.slots.new.store(Some(new_wrapper)); + } else { + accessor.inherit_from_mro(self); + } } - _ if name == identifier!(ctx, __init__) => { - // Special handling: check if this type or any base has a real __init__ method - // If only slot wrappers exist, use the inherited slot from copyslot! + SlotAccessor::TpDel => update_main_slot!(del, del_wrapper, Del), + SlotAccessor::TpGetattro => update_main_slot!(getattro, getattro_wrapper, GetAttro), + SlotAccessor::TpSetattro => { + // __setattr__ and __delattr__ share the same slot if ADD { - // First check if this type has __init__ in its own dict - let has_own_init = self.attributes.read().contains_key(name); - if has_own_init { - // This type defines __init__ - use wrapper - self.slots.init.store(Some(init_wrapper)); - } else if self.has_real_method_in_mro(name, ctx) { - // A base class defines a real __init__ method - use wrapper - self.slots.init.store(Some(init_wrapper)); + if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| match sf { + SlotFunc::SetAttro(f) | SlotFunc::DelAttro(f) => Some(*f), + _ => None, + }) { + self.slots.setattro.store(Some(func)); + } else { + self.slots.setattro.store(Some(setattro_wrapper)); } - // else: keep inherited slot from copyslot! } else { - let inherited = self - .mro - .read() - .iter() - .skip(1) - .find_map(|cls| cls.slots.init.load()); - self.slots.init.store(inherited); + accessor.inherit_from_mro(self); } } - _ if name == identifier!(ctx, __new__) => { - toggle_slot!(new, new_wrapper); - } - _ if name == identifier!(ctx, __del__) => { - // Same special handling as __init__ + SlotAccessor::TpDescrGet => update_main_slot!(descr_get, descr_get_wrapper, DescrGet), + SlotAccessor::TpDescrSet => { + // __set__ and __delete__ share the same slot if ADD { - let has_own_del = self.attributes.read().contains_key(name); - if has_own_del || self.has_real_method_in_mro(name, ctx) { - self.slots.del.store(Some(del_wrapper)); + if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| match sf { + SlotFunc::DescrSet(f) | SlotFunc::DescrDel(f) => Some(*f), + _ => None, + }) { + self.slots.descr_set.store(Some(func)); + } else { + self.slots.descr_set.store(Some(descr_set_wrapper)); } } else { - let inherited = self - .mro - .read() - .iter() - .skip(1) - .find_map(|cls| cls.slots.del.load()); - self.slots.del.store(inherited); + accessor.inherit_from_mro(self); } } - _ if name == identifier!(ctx, __bool__) => { - toggle_sub_slot!(as_number, boolean, bool_wrapper); - } - _ if name == identifier!(ctx, __int__) => { - toggle_sub_slot!(as_number, int, number_unary_op_wrapper!(__int__)); - } - _ if name == identifier!(ctx, __index__) => { - toggle_sub_slot!(as_number, index, number_unary_op_wrapper!(__index__)); - } - _ if name == identifier!(ctx, __float__) => { - toggle_sub_slot!(as_number, float, number_unary_op_wrapper!(__float__)); - } - _ if name == identifier!(ctx, __add__) => { - toggle_sub_slot!(as_number, add, number_binary_op_wrapper!(__add__)); - } - _ if name == identifier!(ctx, __radd__) => { - toggle_sub_slot!( - as_number, - right_add, - number_binary_right_op_wrapper!(__radd__) - ); - } - _ if name == identifier!(ctx, __iadd__) => { - toggle_sub_slot!(as_number, inplace_add, number_binary_op_wrapper!(__iadd__)); + + // === Rich compare (__lt__, __le__, __eq__, __ne__, __gt__, __ge__) === + SlotAccessor::TpRichcompare => { + if ADD { + // Check if self or any class in MRO has a Python-defined comparison method + // All comparison ops share the same slot, so if any is overridden anywhere + // in the hierarchy with a Python function, we need to use the wrapper + let cmp_names = [ + identifier!(ctx, __eq__), + identifier!(ctx, __ne__), + identifier!(ctx, __lt__), + identifier!(ctx, __le__), + identifier!(ctx, __gt__), + identifier!(ctx, __ge__), + ]; + + let has_python_cmp = { + // Check self first + let attrs = self.attributes.read(); + let in_self = cmp_names.iter().any(|n| attrs.contains_key(*n)); + drop(attrs); + + in_self + || self.mro.read().iter().any(|cls| { + let attrs = cls.attributes.read(); + cmp_names.iter().any(|n| { + if let Some(attr) = attrs.get(*n) { + // Check if it's a Python function (not wrapper_descriptor) + !attr.class().is(ctx.types.wrapper_descriptor_type) + } else { + false + } + }) + }) + }; + + if has_python_cmp { + // Use wrapper to call the Python method + self.slots.richcompare.store(Some(richcompare_wrapper)); + } else if let Some(func) = self.lookup_slot_in_mro(name, ctx, |sf| { + if let SlotFunc::RichCompare(f, _) = sf { + Some(*f) + } else { + None + } + }) { + self.slots.richcompare.store(Some(func)); + } else { + self.slots.richcompare.store(Some(richcompare_wrapper)); + } + } else { + accessor.inherit_from_mro(self); + } } - _ if name == identifier!(ctx, __sub__) => { - toggle_sub_slot!(as_number, subtract, number_binary_op_wrapper!(__sub__)); + + // === Number binary operations === + SlotAccessor::NbAdd => { + if name.as_str() == "__radd__" { + update_sub_slot!( + as_number, + right_add, + number_binary_right_op_wrapper!(__radd__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + add, + number_binary_op_wrapper!(__add__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __rsub__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceAdd => { + update_sub_slot!( as_number, - right_subtract, - number_binary_right_op_wrapper!(__rsub__) - ); + inplace_add, + number_binary_op_wrapper!(__iadd__), + NumBinary + ) + } + SlotAccessor::NbSubtract => { + if name.as_str() == "__rsub__" { + update_sub_slot!( + as_number, + right_subtract, + number_binary_right_op_wrapper!(__rsub__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + subtract, + number_binary_op_wrapper!(__sub__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __isub__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceSubtract => { + update_sub_slot!( as_number, inplace_subtract, - number_binary_op_wrapper!(__isub__) - ); - } - _ if name == identifier!(ctx, __mul__) => { - toggle_sub_slot!(as_number, multiply, number_binary_op_wrapper!(__mul__)); - } - _ if name == identifier!(ctx, __rmul__) => { - toggle_sub_slot!( - as_number, - right_multiply, - number_binary_right_op_wrapper!(__rmul__) - ); + number_binary_op_wrapper!(__isub__), + NumBinary + ) + } + SlotAccessor::NbMultiply => { + if name.as_str() == "__rmul__" { + update_sub_slot!( + as_number, + right_multiply, + number_binary_right_op_wrapper!(__rmul__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + multiply, + number_binary_op_wrapper!(__mul__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __imul__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceMultiply => { + update_sub_slot!( as_number, inplace_multiply, - number_binary_op_wrapper!(__imul__) - ); - } - _ if name == identifier!(ctx, __mod__) => { - toggle_sub_slot!(as_number, remainder, number_binary_op_wrapper!(__mod__)); - } - _ if name == identifier!(ctx, __rmod__) => { - toggle_sub_slot!( - as_number, - right_remainder, - number_binary_right_op_wrapper!(__rmod__) - ); + number_binary_op_wrapper!(__imul__), + NumBinary + ) + } + SlotAccessor::NbRemainder => { + if name.as_str() == "__rmod__" { + update_sub_slot!( + as_number, + right_remainder, + number_binary_right_op_wrapper!(__rmod__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + remainder, + number_binary_op_wrapper!(__mod__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __imod__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceRemainder => { + update_sub_slot!( as_number, inplace_remainder, - number_binary_op_wrapper!(__imod__) - ); + number_binary_op_wrapper!(__imod__), + NumBinary + ) + } + SlotAccessor::NbDivmod => { + if name.as_str() == "__rdivmod__" { + update_sub_slot!( + as_number, + right_divmod, + number_binary_right_op_wrapper!(__rdivmod__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + divmod, + number_binary_op_wrapper!(__divmod__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __divmod__) => { - toggle_sub_slot!(as_number, divmod, number_binary_op_wrapper!(__divmod__)); + SlotAccessor::NbPower => { + if name.as_str() == "__rpow__" { + update_sub_slot!( + as_number, + right_power, + number_ternary_right_op_wrapper!(__rpow__), + NumTernary + ) + } else { + update_sub_slot!( + as_number, + power, + number_ternary_op_wrapper!(__pow__), + NumTernary + ) + } } - _ if name == identifier!(ctx, __rdivmod__) => { - toggle_sub_slot!( + SlotAccessor::NbInplacePower => { + update_sub_slot!( as_number, - right_divmod, - number_binary_right_op_wrapper!(__rdivmod__) - ); - } - _ if name == identifier!(ctx, __pow__) => { - toggle_sub_slot!(as_number, power, |a, b, c, vm| { - let args = if vm.is_none(c) { - vec![b.to_owned()] - } else { - vec![b.to_owned(), c.to_owned()] - }; - vm.call_special_method(a, identifier!(vm, __pow__), args) - }); - } - _ if name == identifier!(ctx, __rpow__) => { - toggle_sub_slot!(as_number, right_power, |a, b, c, vm| { - let args = if vm.is_none(c) { - vec![a.to_owned()] - } else { - vec![a.to_owned(), c.to_owned()] - }; - vm.call_special_method(b, identifier!(vm, __rpow__), args) - }); - } - _ if name == identifier!(ctx, __ipow__) => { - toggle_sub_slot!(as_number, inplace_power, |a, b, _, vm| { - vm.call_special_method(a, identifier!(vm, __ipow__), (b.to_owned(),)) - }); - } - _ if name == identifier!(ctx, __lshift__) => { - toggle_sub_slot!(as_number, lshift, number_binary_op_wrapper!(__lshift__)); + inplace_power, + number_ternary_op_wrapper!(__ipow__), + NumTernary + ) + } + SlotAccessor::NbFloorDivide => { + if name.as_str() == "__rfloordiv__" { + update_sub_slot!( + as_number, + right_floor_divide, + number_binary_right_op_wrapper!(__rfloordiv__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + floor_divide, + number_binary_op_wrapper!(__floordiv__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __rlshift__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceFloorDivide => { + update_sub_slot!( as_number, - right_lshift, - number_binary_right_op_wrapper!(__rlshift__) - ); + inplace_floor_divide, + number_binary_op_wrapper!(__ifloordiv__), + NumBinary + ) + } + SlotAccessor::NbTrueDivide => { + if name.as_str() == "__rtruediv__" { + update_sub_slot!( + as_number, + right_true_divide, + number_binary_right_op_wrapper!(__rtruediv__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + true_divide, + number_binary_op_wrapper!(__truediv__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __ilshift__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceTrueDivide => { + update_sub_slot!( as_number, - inplace_lshift, - number_binary_op_wrapper!(__ilshift__) - ); + inplace_true_divide, + number_binary_op_wrapper!(__itruediv__), + NumBinary + ) + } + SlotAccessor::NbMatrixMultiply => { + if name.as_str() == "__rmatmul__" { + update_sub_slot!( + as_number, + right_matrix_multiply, + number_binary_right_op_wrapper!(__rmatmul__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + matrix_multiply, + number_binary_op_wrapper!(__matmul__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __rshift__) => { - toggle_sub_slot!(as_number, rshift, number_binary_op_wrapper!(__rshift__)); + SlotAccessor::NbInplaceMatrixMultiply => { + update_sub_slot!( + as_number, + inplace_matrix_multiply, + number_binary_op_wrapper!(__imatmul__), + NumBinary + ) + } + + // === Number bitwise operations === + SlotAccessor::NbLshift => { + if name.as_str() == "__rlshift__" { + update_sub_slot!( + as_number, + right_lshift, + number_binary_right_op_wrapper!(__rlshift__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + lshift, + number_binary_op_wrapper!(__lshift__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __rrshift__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceLshift => { + update_sub_slot!( as_number, - right_rshift, - number_binary_right_op_wrapper!(__rrshift__) - ); + inplace_lshift, + number_binary_op_wrapper!(__ilshift__), + NumBinary + ) + } + SlotAccessor::NbRshift => { + if name.as_str() == "__rrshift__" { + update_sub_slot!( + as_number, + right_rshift, + number_binary_right_op_wrapper!(__rrshift__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + rshift, + number_binary_op_wrapper!(__rshift__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __irshift__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceRshift => { + update_sub_slot!( as_number, inplace_rshift, - number_binary_op_wrapper!(__irshift__) - ); - } - _ if name == identifier!(ctx, __and__) => { - toggle_sub_slot!(as_number, and, number_binary_op_wrapper!(__and__)); + number_binary_op_wrapper!(__irshift__), + NumBinary + ) + } + SlotAccessor::NbAnd => { + if name.as_str() == "__rand__" { + update_sub_slot!( + as_number, + right_and, + number_binary_right_op_wrapper!(__rand__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + and, + number_binary_op_wrapper!(__and__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __rand__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceAnd => { + update_sub_slot!( as_number, - right_and, - number_binary_right_op_wrapper!(__rand__) - ); - } - _ if name == identifier!(ctx, __iand__) => { - toggle_sub_slot!(as_number, inplace_and, number_binary_op_wrapper!(__iand__)); - } - _ if name == identifier!(ctx, __xor__) => { - toggle_sub_slot!(as_number, xor, number_binary_op_wrapper!(__xor__)); + inplace_and, + number_binary_op_wrapper!(__iand__), + NumBinary + ) + } + SlotAccessor::NbXor => { + if name.as_str() == "__rxor__" { + update_sub_slot!( + as_number, + right_xor, + number_binary_right_op_wrapper!(__rxor__), + NumBinary + ) + } else { + update_sub_slot!( + as_number, + xor, + number_binary_op_wrapper!(__xor__), + NumBinary + ) + } } - _ if name == identifier!(ctx, __rxor__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceXor => { + update_sub_slot!( as_number, - right_xor, - number_binary_right_op_wrapper!(__rxor__) - ); - } - _ if name == identifier!(ctx, __ixor__) => { - toggle_sub_slot!(as_number, inplace_xor, number_binary_op_wrapper!(__ixor__)); - } - _ if name == identifier!(ctx, __or__) => { - toggle_sub_slot!(as_number, or, number_binary_op_wrapper!(__or__)); + inplace_xor, + number_binary_op_wrapper!(__ixor__), + NumBinary + ) + } + SlotAccessor::NbOr => { + if name.as_str() == "__ror__" { + update_sub_slot!( + as_number, + right_or, + number_binary_right_op_wrapper!(__ror__), + NumBinary + ) + } else { + update_sub_slot!(as_number, or, number_binary_op_wrapper!(__or__), NumBinary) + } } - _ if name == identifier!(ctx, __ror__) => { - toggle_sub_slot!( + SlotAccessor::NbInplaceOr => { + update_sub_slot!( as_number, - right_or, - number_binary_right_op_wrapper!(__ror__) - ); - } - _ if name == identifier!(ctx, __ior__) => { - toggle_sub_slot!(as_number, inplace_or, number_binary_op_wrapper!(__ior__)); + inplace_or, + number_binary_op_wrapper!(__ior__), + NumBinary + ) } - _ if name == identifier!(ctx, __floordiv__) => { - toggle_sub_slot!( + + // === Number unary operations === + SlotAccessor::NbNegative => { + update_sub_slot!( as_number, - floor_divide, - number_binary_op_wrapper!(__floordiv__) - ); + negative, + number_unary_op_wrapper!(__neg__), + NumUnary + ) } - _ if name == identifier!(ctx, __rfloordiv__) => { - toggle_sub_slot!( + SlotAccessor::NbPositive => { + update_sub_slot!( as_number, - right_floor_divide, - number_binary_right_op_wrapper!(__rfloordiv__) - ); + positive, + number_unary_op_wrapper!(__pos__), + NumUnary + ) } - _ if name == identifier!(ctx, __ifloordiv__) => { - toggle_sub_slot!( + SlotAccessor::NbAbsolute => { + update_sub_slot!( as_number, - inplace_floor_divide, - number_binary_op_wrapper!(__ifloordiv__) - ); + absolute, + number_unary_op_wrapper!(__abs__), + NumUnary + ) } - _ if name == identifier!(ctx, __truediv__) => { - toggle_sub_slot!( + SlotAccessor::NbInvert => { + update_sub_slot!( as_number, - true_divide, - number_binary_op_wrapper!(__truediv__) - ); + invert, + number_unary_op_wrapper!(__invert__), + NumUnary + ) } - _ if name == identifier!(ctx, __rtruediv__) => { - toggle_sub_slot!( - as_number, - right_true_divide, - number_binary_right_op_wrapper!(__rtruediv__) - ); + SlotAccessor::NbBool => { + update_sub_slot!(as_number, boolean, bool_wrapper, NumBoolean) } - _ if name == identifier!(ctx, __itruediv__) => { - toggle_sub_slot!( - as_number, - inplace_true_divide, - number_binary_op_wrapper!(__itruediv__) - ); + SlotAccessor::NbInt => { + update_sub_slot!(as_number, int, number_unary_op_wrapper!(__int__), NumUnary) } - _ if name == identifier!(ctx, __matmul__) => { - toggle_sub_slot!( + SlotAccessor::NbFloat => { + update_sub_slot!( as_number, - matrix_multiply, - number_binary_op_wrapper!(__matmul__) - ); + float, + number_unary_op_wrapper!(__float__), + NumUnary + ) } - _ if name == identifier!(ctx, __rmatmul__) => { - toggle_sub_slot!( + SlotAccessor::NbIndex => { + update_sub_slot!( as_number, - right_matrix_multiply, - number_binary_right_op_wrapper!(__rmatmul__) - ); + index, + number_unary_op_wrapper!(__index__), + NumUnary + ) + } + + // === Sequence slots === + SlotAccessor::SqLength => { + update_sub_slot!( + as_sequence, + length, + |seq, vm| len_wrapper(seq.obj, vm), + SeqLength + ) + } + SlotAccessor::SqConcat | SlotAccessor::SqInplaceConcat => { + // Sequence concat uses sq_concat slot - no generic wrapper needed + // (handled by number protocol fallback) + if !ADD { + accessor.inherit_from_mro(self); + } } - _ if name == identifier!(ctx, __imatmul__) => { - toggle_sub_slot!( - as_number, - inplace_matrix_multiply, - number_binary_op_wrapper!(__imatmul__) - ); + SlotAccessor::SqRepeat | SlotAccessor::SqInplaceRepeat => { + // Sequence repeat uses sq_repeat slot - no generic wrapper needed + // (handled by number protocol fallback) + if !ADD { + accessor.inherit_from_mro(self); + } } + SlotAccessor::SqItem => { + update_sub_slot!( + as_sequence, + item, + |seq, i, vm| getitem_wrapper(seq.obj, i, vm), + SeqItem + ) + } + SlotAccessor::SqAssItem => { + update_sub_slot!( + as_sequence, + ass_item, + |seq, i, value, vm| setitem_wrapper(seq.obj, i, value, vm), + SeqAssItem + ) + } + SlotAccessor::SqContains => { + update_sub_slot!( + as_sequence, + contains, + |seq, needle, vm| contains_wrapper(seq.obj, needle, vm), + SeqContains + ) + } + + // === Mapping slots === + SlotAccessor::MpLength => { + update_sub_slot!( + as_mapping, + length, + |mapping, vm| len_wrapper(mapping.obj, vm), + MapLength + ) + } + SlotAccessor::MpSubscript => { + update_sub_slot!( + as_mapping, + subscript, + |mapping, key, vm| getitem_wrapper(mapping.obj, key, vm), + MapSubscript + ) + } + SlotAccessor::MpAssSubscript => { + update_sub_slot!( + as_mapping, + ass_subscript, + |mapping, key, value, vm| setitem_wrapper(mapping.obj, key, value, vm), + MapAssSubscript + ) + } + + // Reserved slots - no-op _ => {} } } - /// Check if there's a real method (not a slot wrapper) for `name` anywhere in the MRO. - /// If a real method exists, we should use a wrapper function. Otherwise, use the inherited slot. - fn has_real_method_in_mro(&self, name: &'static PyStrInterned, ctx: &Context) -> bool { - use crate::builtins::descriptor::PySlotWrapper; + /// Look up a method in MRO and extract the slot function if it's a slot wrapper. + /// Returns Some(slot_func) if a matching slot wrapper is found, None if a real method + /// is found or no method exists. + fn lookup_slot_in_mro( + &self, + name: &'static PyStrInterned, + ctx: &Context, + extract: impl Fn(&crate::builtins::descriptor::SlotFunc) -> Option, + ) -> Option { + use crate::builtins::descriptor::PyWrapper; + + // Helper to extract slot from an attribute if it's a wrapper descriptor + let try_extract = |attr: &PyObjectRef| -> Option { + if attr.class().is(ctx.types.wrapper_descriptor_type) { + attr.downcast_ref::() + .and_then(|wrapper| extract(&wrapper.wrapped)) + } else { + None + } + }; - // Check the entire MRO (including self) for the method + // Look up in self's dict first + if let Some(attr) = self.attributes.read().get(name).cloned() { + if let Some(func) = try_extract(&attr) { + return Some(func); + } + return None; + } + + // Look up in MRO for cls in self.mro.read().iter() { if let Some(attr) = cls.attributes.read().get(name).cloned() { - // Found the method - check if it's a slot wrapper - if attr.class().is(ctx.types.wrapper_descriptor_type) { - if let Some(wrapper) = attr.downcast_ref::() { - // It's a slot wrapper - check if it belongs to this class - let wrapper_typ: *const _ = wrapper.typ; - let cls_ptr: *const _ = cls.as_ref(); - if wrapper_typ == cls_ptr { - // Slot wrapper defined on this exact class - use inherited slot - return false; - } - } - // Inherited slot wrapper - continue checking MRO - } else { - // Real method found - use wrapper function - return true; + if let Some(func) = try_extract(&attr) { + return Some(func); } + return None; } } - // No real method found in MRO - use inherited slot - false + // No method found in MRO + None } } @@ -1233,60 +1535,8 @@ pub trait Comparable: PyPayload { vm: &VirtualMachine, ) -> PyResult; - #[inline] - #[pymethod] - fn __eq__( - zelf: &Py, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult { - Self::cmp(zelf, &other, PyComparisonOp::Eq, vm) - } - #[inline] - #[pymethod] - fn __ne__( - zelf: &Py, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult { - Self::cmp(zelf, &other, PyComparisonOp::Ne, vm) - } - #[inline] - #[pymethod] - fn __lt__( - zelf: &Py, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult { - Self::cmp(zelf, &other, PyComparisonOp::Lt, vm) - } - #[inline] - #[pymethod] - fn __le__( - zelf: &Py, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult { - Self::cmp(zelf, &other, PyComparisonOp::Le, vm) - } - #[inline] - #[pymethod] - fn __ge__( - zelf: &Py, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult { - Self::cmp(zelf, &other, PyComparisonOp::Ge, vm) - } - #[inline] - #[pymethod] - fn __gt__( - zelf: &Py, - other: PyObjectRef, - vm: &VirtualMachine, - ) -> PyResult { - Self::cmp(zelf, &other, PyComparisonOp::Gt, vm) - } + // Comparison methods are exposed as wrapper_descriptor via slot_richcompare + // No #[pymethod] - add_operators creates wrapper from SLOT_DEFS } #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -1495,6 +1745,10 @@ pub trait AsNumber: PyPayload { #[pyslot] fn as_number() -> &'static PyNumberMethods; + fn extend_slots(slots: &mut PyTypeSlots) { + slots.as_number.copy_from(Self::as_number()); + } + fn clone_exact(_zelf: &Py, _vm: &VirtualMachine) -> PyRef { // not all AsNumber requires this implementation. unimplemented!() diff --git a/crates/vm/src/types/slot_defs.rs b/crates/vm/src/types/slot_defs.rs new file mode 100644 index 00000000000..0d8dbf4b0cc --- /dev/null +++ b/crates/vm/src/types/slot_defs.rs @@ -0,0 +1,1465 @@ +//! Slot definitions array +//! +//! This module provides a centralized array of all slot definitions, + +use super::{PyComparisonOp, PyTypeSlots}; +use crate::builtins::descriptor::SlotFunc; + +/// Slot operation type +/// +/// Used to distinguish between different operations that share the same slot: +/// - RichCompare: Lt, Le, Eq, Ne, Gt, Ge +/// - Binary ops: Left (__add__) vs Right (__radd__) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SlotOp { + // RichCompare operations + Lt, + Le, + Eq, + Ne, + Gt, + Ge, + // Binary operation direction + Left, + Right, + // Setter vs Deleter + Delete, +} + +impl SlotOp { + /// Convert to PyComparisonOp if this is a comparison operation + pub fn as_compare_op(&self) -> Option { + match self { + Self::Lt => Some(PyComparisonOp::Lt), + Self::Le => Some(PyComparisonOp::Le), + Self::Eq => Some(PyComparisonOp::Eq), + Self::Ne => Some(PyComparisonOp::Ne), + Self::Gt => Some(PyComparisonOp::Gt), + Self::Ge => Some(PyComparisonOp::Ge), + _ => None, + } + } + + /// Check if this is a right operation (__radd__, __rsub__, etc.) + pub fn is_right(&self) -> bool { + matches!(self, Self::Right) + } +} + +/// Slot definition entry +#[derive(Clone, Copy)] +pub struct SlotDef { + /// Method name ("__init__", "__add__", etc.) + pub name: &'static str, + + /// Slot accessor (which slot field to access) + pub accessor: SlotAccessor, + + /// Operation type (for shared slots like RichCompare, binary ops) + pub op: Option, + + /// Documentation string + pub doc: &'static str, +} + +/// Slot accessor +/// +/// Values match CPython's Py_* slot IDs from typeslots.h. +/// Unused slots are included for value reservation. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum SlotAccessor { + // Buffer protocol (1-2) - Reserved, not used in RustPython + BfGetBuffer = 1, + BfReleaseBuffer = 2, + + // Mapping protocol (3-5) + MpAssSubscript = 3, + MpLength = 4, + MpSubscript = 5, + + // Number protocol (6-38) + NbAbsolute = 6, + NbAdd = 7, + NbAnd = 8, + NbBool = 9, + NbDivmod = 10, + NbFloat = 11, + NbFloorDivide = 12, + NbIndex = 13, + NbInplaceAdd = 14, + NbInplaceAnd = 15, + NbInplaceFloorDivide = 16, + NbInplaceLshift = 17, + NbInplaceMultiply = 18, + NbInplaceOr = 19, + NbInplacePower = 20, + NbInplaceRemainder = 21, + NbInplaceRshift = 22, + NbInplaceSubtract = 23, + NbInplaceTrueDivide = 24, + NbInplaceXor = 25, + NbInt = 26, + NbInvert = 27, + NbLshift = 28, + NbMultiply = 29, + NbNegative = 30, + NbOr = 31, + NbPositive = 32, + NbPower = 33, + NbRemainder = 34, + NbRshift = 35, + NbSubtract = 36, + NbTrueDivide = 37, + NbXor = 38, + + // Sequence protocol (39-46) + SqAssItem = 39, + SqConcat = 40, + SqContains = 41, + SqInplaceConcat = 42, + SqInplaceRepeat = 43, + SqItem = 44, + SqLength = 45, + SqRepeat = 46, + + // Type slots (47-74) + TpAlloc = 47, // Reserved + TpBase = 48, // Reserved + TpBases = 49, // Reserved + TpCall = 50, + TpClear = 51, // Reserved + TpDealloc = 52, // Reserved + TpDel = 53, + TpDescrGet = 54, + TpDescrSet = 55, + TpDoc = 56, // Reserved + TpGetattr = 57, // Reserved (use TpGetattro) + TpGetattro = 58, + TpHash = 59, + TpInit = 60, + TpIsGc = 61, // Reserved + TpIter = 62, + TpIternext = 63, + TpMethods = 64, // Reserved + TpNew = 65, + TpRepr = 66, + TpRichcompare = 67, + TpSetattr = 68, // Reserved (use TpSetattro) + TpSetattro = 69, + TpStr = 70, + TpTraverse = 71, // Reserved + TpMembers = 72, // Reserved + TpGetset = 73, // Reserved + TpFree = 74, // Reserved + + // Number protocol additions (75-76) + NbMatrixMultiply = 75, + NbInplaceMatrixMultiply = 76, + + // Async protocol (77-81) - Reserved for future + AmAwait = 77, + AmAiter = 78, + AmAnext = 79, + TpFinalize = 80, + AmSend = 81, +} + +impl SlotAccessor { + /// Check if this accessor is for a reserved/unused slot + pub fn is_reserved(&self) -> bool { + matches!( + self, + Self::BfGetBuffer + | Self::BfReleaseBuffer + | Self::TpAlloc + | Self::TpBase + | Self::TpBases + | Self::TpClear + | Self::TpDealloc + | Self::TpDoc + | Self::TpGetattr + | Self::TpIsGc + | Self::TpMethods + | Self::TpSetattr + | Self::TpTraverse + | Self::TpMembers + | Self::TpGetset + | Self::TpFree + | Self::TpFinalize + | Self::AmAwait + | Self::AmAiter + | Self::AmAnext + | Self::AmSend + ) + } + + /// Check if this is a number binary operation slot + pub fn is_number_binary(&self) -> bool { + matches!( + self, + Self::NbAdd + | Self::NbSubtract + | Self::NbMultiply + | Self::NbRemainder + | Self::NbDivmod + | Self::NbPower + | Self::NbLshift + | Self::NbRshift + | Self::NbAnd + | Self::NbXor + | Self::NbOr + | Self::NbFloorDivide + | Self::NbTrueDivide + | Self::NbMatrixMultiply + ) + } + + /// Check if this accessor refers to a shared slot + /// + /// Shared slots are used by multiple dunder methods: + /// - TpSetattro: __setattr__ and __delattr__ + /// - TpRichcompare: __lt__, __le__, __eq__, __ne__, __gt__, __ge__ + /// - TpDescrSet: __set__ and __delete__ + /// - SqAssItem/MpAssSubscript: __setitem__ and __delitem__ + /// - Number binaries: __add__ and __radd__, etc. + pub fn is_shared_slot(&self) -> bool { + matches!( + self, + Self::TpSetattro + | Self::TpRichcompare + | Self::TpDescrSet + | Self::SqAssItem + | Self::MpAssSubscript + ) || self.is_number_binary() + } + + /// Get underlying slot field name for debugging + pub fn slot_name(&self) -> &'static str { + match self { + Self::BfGetBuffer => "bf_getbuffer", + Self::BfReleaseBuffer => "bf_releasebuffer", + Self::MpAssSubscript => "mp_ass_subscript", + Self::MpLength => "mp_length", + Self::MpSubscript => "mp_subscript", + Self::NbAbsolute => "nb_absolute", + Self::NbAdd => "nb_add", + Self::NbAnd => "nb_and", + Self::NbBool => "nb_bool", + Self::NbDivmod => "nb_divmod", + Self::NbFloat => "nb_float", + Self::NbFloorDivide => "nb_floor_divide", + Self::NbIndex => "nb_index", + Self::NbInplaceAdd => "nb_inplace_add", + Self::NbInplaceAnd => "nb_inplace_and", + Self::NbInplaceFloorDivide => "nb_inplace_floor_divide", + Self::NbInplaceLshift => "nb_inplace_lshift", + Self::NbInplaceMultiply => "nb_inplace_multiply", + Self::NbInplaceOr => "nb_inplace_or", + Self::NbInplacePower => "nb_inplace_power", + Self::NbInplaceRemainder => "nb_inplace_remainder", + Self::NbInplaceRshift => "nb_inplace_rshift", + Self::NbInplaceSubtract => "nb_inplace_subtract", + Self::NbInplaceTrueDivide => "nb_inplace_true_divide", + Self::NbInplaceXor => "nb_inplace_xor", + Self::NbInt => "nb_int", + Self::NbInvert => "nb_invert", + Self::NbLshift => "nb_lshift", + Self::NbMultiply => "nb_multiply", + Self::NbNegative => "nb_negative", + Self::NbOr => "nb_or", + Self::NbPositive => "nb_positive", + Self::NbPower => "nb_power", + Self::NbRemainder => "nb_remainder", + Self::NbRshift => "nb_rshift", + Self::NbSubtract => "nb_subtract", + Self::NbTrueDivide => "nb_true_divide", + Self::NbXor => "nb_xor", + Self::SqAssItem => "sq_ass_item", + Self::SqConcat => "sq_concat", + Self::SqContains => "sq_contains", + Self::SqInplaceConcat => "sq_inplace_concat", + Self::SqInplaceRepeat => "sq_inplace_repeat", + Self::SqItem => "sq_item", + Self::SqLength => "sq_length", + Self::SqRepeat => "sq_repeat", + Self::TpAlloc => "tp_alloc", + Self::TpBase => "tp_base", + Self::TpBases => "tp_bases", + Self::TpCall => "tp_call", + Self::TpClear => "tp_clear", + Self::TpDealloc => "tp_dealloc", + Self::TpDel => "tp_del", + Self::TpDescrGet => "tp_descr_get", + Self::TpDescrSet => "tp_descr_set", + Self::TpDoc => "tp_doc", + Self::TpGetattr => "tp_getattr", + Self::TpGetattro => "tp_getattro", + Self::TpHash => "tp_hash", + Self::TpInit => "tp_init", + Self::TpIsGc => "tp_is_gc", + Self::TpIter => "tp_iter", + Self::TpIternext => "tp_iternext", + Self::TpMethods => "tp_methods", + Self::TpNew => "tp_new", + Self::TpRepr => "tp_repr", + Self::TpRichcompare => "tp_richcompare", + Self::TpSetattr => "tp_setattr", + Self::TpSetattro => "tp_setattro", + Self::TpStr => "tp_str", + Self::TpTraverse => "tp_traverse", + Self::TpMembers => "tp_members", + Self::TpGetset => "tp_getset", + Self::TpFree => "tp_free", + Self::NbMatrixMultiply => "nb_matrix_multiply", + Self::NbInplaceMatrixMultiply => "nb_inplace_matrix_multiply", + Self::AmAwait => "am_await", + Self::AmAiter => "am_aiter", + Self::AmAnext => "am_anext", + Self::TpFinalize => "tp_finalize", + Self::AmSend => "am_send", + } + } + + /// Extract the raw function pointer from a SlotFunc if it matches this accessor's type + pub fn extract_from_slot_func(&self, slot_func: &SlotFunc) -> bool { + match self { + // Type slots + Self::TpHash => matches!(slot_func, SlotFunc::Hash(_)), + Self::TpRepr => matches!(slot_func, SlotFunc::Repr(_)), + Self::TpStr => matches!(slot_func, SlotFunc::Str(_)), + Self::TpCall => matches!(slot_func, SlotFunc::Call(_)), + Self::TpIter => matches!(slot_func, SlotFunc::Iter(_)), + Self::TpIternext => matches!(slot_func, SlotFunc::IterNext(_)), + Self::TpInit => matches!(slot_func, SlotFunc::Init(_)), + Self::TpDel => matches!(slot_func, SlotFunc::Del(_)), + Self::TpGetattro => matches!(slot_func, SlotFunc::GetAttro(_)), + Self::TpSetattro => { + matches!(slot_func, SlotFunc::SetAttro(_) | SlotFunc::DelAttro(_)) + } + Self::TpDescrGet => matches!(slot_func, SlotFunc::DescrGet(_)), + Self::TpDescrSet => { + matches!(slot_func, SlotFunc::DescrSet(_) | SlotFunc::DescrDel(_)) + } + Self::TpRichcompare => matches!(slot_func, SlotFunc::RichCompare(_, _)), + + // Number - Power (ternary) + Self::NbPower | Self::NbInplacePower => { + matches!(slot_func, SlotFunc::NumTernary(_)) + } + // Number - Boolean + Self::NbBool => matches!(slot_func, SlotFunc::NumBoolean(_)), + // Number - Unary + Self::NbNegative + | Self::NbPositive + | Self::NbAbsolute + | Self::NbInvert + | Self::NbInt + | Self::NbFloat + | Self::NbIndex => matches!(slot_func, SlotFunc::NumUnary(_)), + // Number - Binary + Self::NbAdd + | Self::NbSubtract + | Self::NbMultiply + | Self::NbRemainder + | Self::NbDivmod + | Self::NbLshift + | Self::NbRshift + | Self::NbAnd + | Self::NbXor + | Self::NbOr + | Self::NbFloorDivide + | Self::NbTrueDivide + | Self::NbMatrixMultiply + | Self::NbInplaceAdd + | Self::NbInplaceSubtract + | Self::NbInplaceMultiply + | Self::NbInplaceRemainder + | Self::NbInplaceLshift + | Self::NbInplaceRshift + | Self::NbInplaceAnd + | Self::NbInplaceXor + | Self::NbInplaceOr + | Self::NbInplaceFloorDivide + | Self::NbInplaceTrueDivide + | Self::NbInplaceMatrixMultiply => matches!(slot_func, SlotFunc::NumBinary(_)), + + // Sequence + Self::SqLength => matches!(slot_func, SlotFunc::SeqLength(_)), + Self::SqConcat | Self::SqInplaceConcat => matches!(slot_func, SlotFunc::SeqConcat(_)), + Self::SqRepeat | Self::SqInplaceRepeat => matches!(slot_func, SlotFunc::SeqRepeat(_)), + Self::SqItem => matches!(slot_func, SlotFunc::SeqItem(_)), + Self::SqAssItem => matches!(slot_func, SlotFunc::SeqAssItem(_)), + Self::SqContains => matches!(slot_func, SlotFunc::SeqContains(_)), + + // Mapping + Self::MpLength => matches!(slot_func, SlotFunc::MapLength(_)), + Self::MpSubscript => matches!(slot_func, SlotFunc::MapSubscript(_)), + Self::MpAssSubscript => matches!(slot_func, SlotFunc::MapAssSubscript(_)), + + // New and reserved slots + Self::TpNew => false, + _ => false, // Reserved slots + } + } + + /// Inherit slot value from MRO + pub fn inherit_from_mro(&self, typ: &crate::builtins::PyType) { + // Note: typ.mro does NOT include typ itself + let mro = typ.mro.read(); + + macro_rules! inherit_main { + ($slot:ident) => {{ + let inherited = mro.iter().find_map(|cls| cls.slots.$slot.load()); + typ.slots.$slot.store(inherited); + }}; + } + + macro_rules! inherit_number { + ($slot:ident) => {{ + let inherited = mro.iter().find_map(|cls| cls.slots.as_number.$slot.load()); + typ.slots.as_number.$slot.store(inherited); + }}; + } + + macro_rules! inherit_sequence { + ($slot:ident) => {{ + let inherited = mro + .iter() + .find_map(|cls| cls.slots.as_sequence.$slot.load()); + typ.slots.as_sequence.$slot.store(inherited); + }}; + } + + macro_rules! inherit_mapping { + ($slot:ident) => {{ + let inherited = mro.iter().find_map(|cls| cls.slots.as_mapping.$slot.load()); + typ.slots.as_mapping.$slot.store(inherited); + }}; + } + + match self { + // Type slots + Self::TpHash => inherit_main!(hash), + Self::TpRepr => inherit_main!(repr), + Self::TpStr => inherit_main!(str), + Self::TpCall => inherit_main!(call), + Self::TpIter => inherit_main!(iter), + Self::TpIternext => inherit_main!(iternext), + Self::TpInit => inherit_main!(init), + Self::TpNew => inherit_main!(new), + Self::TpDel => inherit_main!(del), + Self::TpGetattro => inherit_main!(getattro), + Self::TpSetattro => inherit_main!(setattro), + Self::TpDescrGet => inherit_main!(descr_get), + Self::TpDescrSet => inherit_main!(descr_set), + Self::TpRichcompare => inherit_main!(richcompare), + + // Number slots + Self::NbAdd => inherit_number!(add), + Self::NbSubtract => inherit_number!(subtract), + Self::NbMultiply => inherit_number!(multiply), + Self::NbRemainder => inherit_number!(remainder), + Self::NbDivmod => inherit_number!(divmod), + Self::NbPower => inherit_number!(power), + Self::NbLshift => inherit_number!(lshift), + Self::NbRshift => inherit_number!(rshift), + Self::NbAnd => inherit_number!(and), + Self::NbXor => inherit_number!(xor), + Self::NbOr => inherit_number!(or), + Self::NbFloorDivide => inherit_number!(floor_divide), + Self::NbTrueDivide => inherit_number!(true_divide), + Self::NbMatrixMultiply => inherit_number!(matrix_multiply), + Self::NbInplaceAdd => inherit_number!(inplace_add), + Self::NbInplaceSubtract => inherit_number!(inplace_subtract), + Self::NbInplaceMultiply => inherit_number!(inplace_multiply), + Self::NbInplaceRemainder => inherit_number!(inplace_remainder), + Self::NbInplacePower => inherit_number!(inplace_power), + Self::NbInplaceLshift => inherit_number!(inplace_lshift), + Self::NbInplaceRshift => inherit_number!(inplace_rshift), + Self::NbInplaceAnd => inherit_number!(inplace_and), + Self::NbInplaceXor => inherit_number!(inplace_xor), + Self::NbInplaceOr => inherit_number!(inplace_or), + Self::NbInplaceFloorDivide => inherit_number!(inplace_floor_divide), + Self::NbInplaceTrueDivide => inherit_number!(inplace_true_divide), + Self::NbInplaceMatrixMultiply => inherit_number!(inplace_matrix_multiply), + // Number unary + Self::NbNegative => inherit_number!(negative), + Self::NbPositive => inherit_number!(positive), + Self::NbAbsolute => inherit_number!(absolute), + Self::NbInvert => inherit_number!(invert), + Self::NbBool => inherit_number!(boolean), + Self::NbInt => inherit_number!(int), + Self::NbFloat => inherit_number!(float), + Self::NbIndex => inherit_number!(index), + + // Sequence slots + Self::SqLength => inherit_sequence!(length), + Self::SqConcat => inherit_sequence!(concat), + Self::SqRepeat => inherit_sequence!(repeat), + Self::SqItem => inherit_sequence!(item), + Self::SqAssItem => inherit_sequence!(ass_item), + Self::SqContains => inherit_sequence!(contains), + Self::SqInplaceConcat => inherit_sequence!(inplace_concat), + Self::SqInplaceRepeat => inherit_sequence!(inplace_repeat), + + // Mapping slots + Self::MpLength => inherit_mapping!(length), + Self::MpSubscript => inherit_mapping!(subscript), + Self::MpAssSubscript => inherit_mapping!(ass_subscript), + + // Reserved slots - no-op + _ => {} + } + } + + /// Copy slot from base type if self's slot is None + pub fn copyslot_if_none(&self, typ: &crate::builtins::PyType, base: &crate::builtins::PyType) { + macro_rules! copy_main { + ($slot:ident) => {{ + if typ.slots.$slot.load().is_none() { + if let Some(base_val) = base.slots.$slot.load() { + typ.slots.$slot.store(Some(base_val)); + } + } + }}; + } + + macro_rules! copy_number { + ($slot:ident) => {{ + if typ.slots.as_number.$slot.load().is_none() { + if let Some(base_val) = base.slots.as_number.$slot.load() { + typ.slots.as_number.$slot.store(Some(base_val)); + } + } + }}; + } + + macro_rules! copy_sequence { + ($slot:ident) => {{ + if typ.slots.as_sequence.$slot.load().is_none() { + if let Some(base_val) = base.slots.as_sequence.$slot.load() { + typ.slots.as_sequence.$slot.store(Some(base_val)); + } + } + }}; + } + + macro_rules! copy_mapping { + ($slot:ident) => {{ + if typ.slots.as_mapping.$slot.load().is_none() { + if let Some(base_val) = base.slots.as_mapping.$slot.load() { + typ.slots.as_mapping.$slot.store(Some(base_val)); + } + } + }}; + } + + match self { + // Type slots + Self::TpHash => copy_main!(hash), + Self::TpRepr => copy_main!(repr), + Self::TpStr => copy_main!(str), + Self::TpCall => copy_main!(call), + Self::TpIter => copy_main!(iter), + Self::TpIternext => copy_main!(iternext), + Self::TpInit => { + // SLOTDEFINED check for multiple inheritance support + if typ.slots.init.load().is_none() + && let Some(base_val) = base.slots.init.load() + { + let slot_defined = base.base.as_ref().is_none_or(|bb| { + bb.slots.init.load().map(|v| v as usize) != Some(base_val as usize) + }); + if slot_defined { + typ.slots.init.store(Some(base_val)); + } + } + } + Self::TpNew => {} // handled by set_new() + Self::TpDel => copy_main!(del), + Self::TpGetattro => copy_main!(getattro), + Self::TpSetattro => copy_main!(setattro), + Self::TpDescrGet => copy_main!(descr_get), + Self::TpDescrSet => copy_main!(descr_set), + Self::TpRichcompare => copy_main!(richcompare), + + // Number slots + Self::NbAdd => copy_number!(add), + Self::NbSubtract => copy_number!(subtract), + Self::NbMultiply => copy_number!(multiply), + Self::NbRemainder => copy_number!(remainder), + Self::NbDivmod => copy_number!(divmod), + Self::NbPower => copy_number!(power), + Self::NbLshift => copy_number!(lshift), + Self::NbRshift => copy_number!(rshift), + Self::NbAnd => copy_number!(and), + Self::NbXor => copy_number!(xor), + Self::NbOr => copy_number!(or), + Self::NbFloorDivide => copy_number!(floor_divide), + Self::NbTrueDivide => copy_number!(true_divide), + Self::NbMatrixMultiply => copy_number!(matrix_multiply), + Self::NbInplaceAdd => copy_number!(inplace_add), + Self::NbInplaceSubtract => copy_number!(inplace_subtract), + Self::NbInplaceMultiply => copy_number!(inplace_multiply), + Self::NbInplaceRemainder => copy_number!(inplace_remainder), + Self::NbInplacePower => copy_number!(inplace_power), + Self::NbInplaceLshift => copy_number!(inplace_lshift), + Self::NbInplaceRshift => copy_number!(inplace_rshift), + Self::NbInplaceAnd => copy_number!(inplace_and), + Self::NbInplaceXor => copy_number!(inplace_xor), + Self::NbInplaceOr => copy_number!(inplace_or), + Self::NbInplaceFloorDivide => copy_number!(inplace_floor_divide), + Self::NbInplaceTrueDivide => copy_number!(inplace_true_divide), + Self::NbInplaceMatrixMultiply => copy_number!(inplace_matrix_multiply), + // Number unary + Self::NbNegative => copy_number!(negative), + Self::NbPositive => copy_number!(positive), + Self::NbAbsolute => copy_number!(absolute), + Self::NbInvert => copy_number!(invert), + Self::NbBool => copy_number!(boolean), + Self::NbInt => copy_number!(int), + Self::NbFloat => copy_number!(float), + Self::NbIndex => copy_number!(index), + + // Sequence slots + Self::SqLength => copy_sequence!(length), + Self::SqConcat => copy_sequence!(concat), + Self::SqRepeat => copy_sequence!(repeat), + Self::SqItem => copy_sequence!(item), + Self::SqAssItem => copy_sequence!(ass_item), + Self::SqContains => copy_sequence!(contains), + Self::SqInplaceConcat => copy_sequence!(inplace_concat), + Self::SqInplaceRepeat => copy_sequence!(inplace_repeat), + + // Mapping slots + Self::MpLength => copy_mapping!(length), + Self::MpSubscript => copy_mapping!(subscript), + Self::MpAssSubscript => copy_mapping!(ass_subscript), + + // Reserved slots - no-op + _ => {} + } + } + + /// Get the SlotFunc from type slots for this accessor + pub fn get_slot_func(&self, slots: &PyTypeSlots) -> Option { + match self { + // Type slots + Self::TpHash => slots.hash.load().map(SlotFunc::Hash), + Self::TpRepr => slots.repr.load().map(SlotFunc::Repr), + Self::TpStr => slots.str.load().map(SlotFunc::Str), + Self::TpCall => slots.call.load().map(SlotFunc::Call), + Self::TpIter => slots.iter.load().map(SlotFunc::Iter), + Self::TpIternext => slots.iternext.load().map(SlotFunc::IterNext), + Self::TpInit => slots.init.load().map(SlotFunc::Init), + Self::TpNew => None, // __new__ handled separately + Self::TpDel => slots.del.load().map(SlotFunc::Del), + Self::TpGetattro => slots.getattro.load().map(SlotFunc::GetAttro), + Self::TpSetattro => slots.setattro.load().map(SlotFunc::SetAttro), + Self::TpDescrGet => slots.descr_get.load().map(SlotFunc::DescrGet), + Self::TpDescrSet => slots.descr_set.load().map(SlotFunc::DescrSet), + Self::TpRichcompare => slots + .richcompare + .load() + .map(|f| SlotFunc::RichCompare(f, PyComparisonOp::Eq)), + + // Number binary slots + Self::NbAdd => slots.as_number.add.load().map(SlotFunc::NumBinary), + Self::NbSubtract => slots.as_number.subtract.load().map(SlotFunc::NumBinary), + Self::NbMultiply => slots.as_number.multiply.load().map(SlotFunc::NumBinary), + Self::NbRemainder => slots.as_number.remainder.load().map(SlotFunc::NumBinary), + Self::NbDivmod => slots.as_number.divmod.load().map(SlotFunc::NumBinary), + Self::NbPower => slots.as_number.power.load().map(SlotFunc::NumTernary), + Self::NbLshift => slots.as_number.lshift.load().map(SlotFunc::NumBinary), + Self::NbRshift => slots.as_number.rshift.load().map(SlotFunc::NumBinary), + Self::NbAnd => slots.as_number.and.load().map(SlotFunc::NumBinary), + Self::NbXor => slots.as_number.xor.load().map(SlotFunc::NumBinary), + Self::NbOr => slots.as_number.or.load().map(SlotFunc::NumBinary), + Self::NbFloorDivide => slots.as_number.floor_divide.load().map(SlotFunc::NumBinary), + Self::NbTrueDivide => slots.as_number.true_divide.load().map(SlotFunc::NumBinary), + Self::NbMatrixMultiply => slots + .as_number + .matrix_multiply + .load() + .map(SlotFunc::NumBinary), + + // Number inplace slots + Self::NbInplaceAdd => slots.as_number.inplace_add.load().map(SlotFunc::NumBinary), + Self::NbInplaceSubtract => slots + .as_number + .inplace_subtract + .load() + .map(SlotFunc::NumBinary), + Self::NbInplaceMultiply => slots + .as_number + .inplace_multiply + .load() + .map(SlotFunc::NumBinary), + Self::NbInplaceRemainder => slots + .as_number + .inplace_remainder + .load() + .map(SlotFunc::NumBinary), + Self::NbInplacePower => slots + .as_number + .inplace_power + .load() + .map(SlotFunc::NumTernary), + Self::NbInplaceLshift => slots + .as_number + .inplace_lshift + .load() + .map(SlotFunc::NumBinary), + Self::NbInplaceRshift => slots + .as_number + .inplace_rshift + .load() + .map(SlotFunc::NumBinary), + Self::NbInplaceAnd => slots.as_number.inplace_and.load().map(SlotFunc::NumBinary), + Self::NbInplaceXor => slots.as_number.inplace_xor.load().map(SlotFunc::NumBinary), + Self::NbInplaceOr => slots.as_number.inplace_or.load().map(SlotFunc::NumBinary), + Self::NbInplaceFloorDivide => slots + .as_number + .inplace_floor_divide + .load() + .map(SlotFunc::NumBinary), + Self::NbInplaceTrueDivide => slots + .as_number + .inplace_true_divide + .load() + .map(SlotFunc::NumBinary), + Self::NbInplaceMatrixMultiply => slots + .as_number + .inplace_matrix_multiply + .load() + .map(SlotFunc::NumBinary), + + // Number unary slots + Self::NbNegative => slots.as_number.negative.load().map(SlotFunc::NumUnary), + Self::NbPositive => slots.as_number.positive.load().map(SlotFunc::NumUnary), + Self::NbAbsolute => slots.as_number.absolute.load().map(SlotFunc::NumUnary), + Self::NbInvert => slots.as_number.invert.load().map(SlotFunc::NumUnary), + Self::NbBool => slots.as_number.boolean.load().map(SlotFunc::NumBoolean), + Self::NbInt => slots.as_number.int.load().map(SlotFunc::NumUnary), + Self::NbFloat => slots.as_number.float.load().map(SlotFunc::NumUnary), + Self::NbIndex => slots.as_number.index.load().map(SlotFunc::NumUnary), + + // Sequence slots + Self::SqLength => slots.as_sequence.length.load().map(SlotFunc::SeqLength), + Self::SqConcat => slots.as_sequence.concat.load().map(SlotFunc::SeqConcat), + Self::SqRepeat => slots.as_sequence.repeat.load().map(SlotFunc::SeqRepeat), + Self::SqItem => slots.as_sequence.item.load().map(SlotFunc::SeqItem), + Self::SqAssItem => slots.as_sequence.ass_item.load().map(SlotFunc::SeqAssItem), + Self::SqContains => slots.as_sequence.contains.load().map(SlotFunc::SeqContains), + Self::SqInplaceConcat => slots + .as_sequence + .inplace_concat + .load() + .map(SlotFunc::SeqConcat), + Self::SqInplaceRepeat => slots + .as_sequence + .inplace_repeat + .load() + .map(SlotFunc::SeqRepeat), + + // Mapping slots + Self::MpLength => slots.as_mapping.length.load().map(SlotFunc::MapLength), + Self::MpSubscript => slots + .as_mapping + .subscript + .load() + .map(SlotFunc::MapSubscript), + Self::MpAssSubscript => slots + .as_mapping + .ass_subscript + .load() + .map(SlotFunc::MapAssSubscript), + + // Reserved slots + _ => None, + } + } + + /// Get slot function considering SlotOp for right-hand and delete operations + pub fn get_slot_func_with_op( + &self, + slots: &PyTypeSlots, + op: Option, + ) -> Option { + // For Delete operations, return the delete variant + if op == Some(SlotOp::Delete) { + match self { + Self::TpSetattro => return slots.setattro.load().map(SlotFunc::DelAttro), + Self::TpDescrSet => return slots.descr_set.load().map(SlotFunc::DescrDel), + _ => {} + } + } + // For Right operations on binary number slots, use right_* fields with swapped args + if op == Some(SlotOp::Right) { + match self { + Self::NbAdd => { + return slots + .as_number + .right_add + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbSubtract => { + return slots + .as_number + .right_subtract + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbMultiply => { + return slots + .as_number + .right_multiply + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbRemainder => { + return slots + .as_number + .right_remainder + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbDivmod => { + return slots + .as_number + .right_divmod + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbPower => { + return slots + .as_number + .right_power + .load() + .map(SlotFunc::NumTernaryRight); + } + Self::NbLshift => { + return slots + .as_number + .right_lshift + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbRshift => { + return slots + .as_number + .right_rshift + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbAnd => { + return slots + .as_number + .right_and + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbXor => { + return slots + .as_number + .right_xor + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbOr => { + return slots + .as_number + .right_or + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbFloorDivide => { + return slots + .as_number + .right_floor_divide + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbTrueDivide => { + return slots + .as_number + .right_true_divide + .load() + .map(SlotFunc::NumBinaryRight); + } + Self::NbMatrixMultiply => { + return slots + .as_number + .right_matrix_multiply + .load() + .map(SlotFunc::NumBinaryRight); + } + _ => {} + } + } + // For comparison operations, use the appropriate PyComparisonOp + if let Self::TpRichcompare = self + && let Some(cmp_op) = op.and_then(|o| o.as_compare_op()) + { + return slots + .richcompare + .load() + .map(|f| SlotFunc::RichCompare(f, cmp_op)); + } + // Fall back to existing get_slot_func for left/other operations + self.get_slot_func(slots) + } +} + +/// Find all slot definitions with a given name +pub fn find_slot_defs_by_name(name: &str) -> impl Iterator { + SLOT_DEFS.iter().filter(move |def| def.name == name) +} + +/// Total number of slot definitions +pub const SLOT_DEFS_COUNT: usize = SLOT_DEFS.len(); + +/// All slot definitions +pub static SLOT_DEFS: &[SlotDef] = &[ + // Type slots (tp_*) + SlotDef { + name: "__init__", + accessor: SlotAccessor::TpInit, + op: None, + doc: "Initialize self. See help(type(self)) for accurate signature.", + }, + SlotDef { + name: "__new__", + accessor: SlotAccessor::TpNew, + op: None, + doc: "Create and return a new object. See help(type) for accurate signature.", + }, + SlotDef { + name: "__del__", + accessor: SlotAccessor::TpDel, + op: None, + doc: "Called when the instance is about to be destroyed.", + }, + SlotDef { + name: "__repr__", + accessor: SlotAccessor::TpRepr, + op: None, + doc: "Return repr(self).", + }, + SlotDef { + name: "__str__", + accessor: SlotAccessor::TpStr, + op: None, + doc: "Return str(self).", + }, + SlotDef { + name: "__hash__", + accessor: SlotAccessor::TpHash, + op: None, + doc: "Return hash(self).", + }, + SlotDef { + name: "__call__", + accessor: SlotAccessor::TpCall, + op: None, + doc: "Call self as a function.", + }, + SlotDef { + name: "__iter__", + accessor: SlotAccessor::TpIter, + op: None, + doc: "Implement iter(self).", + }, + SlotDef { + name: "__next__", + accessor: SlotAccessor::TpIternext, + op: None, + doc: "Implement next(self).", + }, + // Attribute access + SlotDef { + name: "__getattribute__", + accessor: SlotAccessor::TpGetattro, + op: None, + doc: "Return getattr(self, name).", + }, + SlotDef { + name: "__setattr__", + accessor: SlotAccessor::TpSetattro, + op: None, + doc: "Implement setattr(self, name, value).", + }, + SlotDef { + name: "__delattr__", + accessor: SlotAccessor::TpSetattro, + op: Some(SlotOp::Delete), + doc: "Implement delattr(self, name).", + }, + // Rich comparison - all map to TpRichcompare with different op + SlotDef { + name: "__eq__", + accessor: SlotAccessor::TpRichcompare, + op: Some(SlotOp::Eq), + doc: "Return self==value.", + }, + SlotDef { + name: "__ne__", + accessor: SlotAccessor::TpRichcompare, + op: Some(SlotOp::Ne), + doc: "Return self!=value.", + }, + SlotDef { + name: "__lt__", + accessor: SlotAccessor::TpRichcompare, + op: Some(SlotOp::Lt), + doc: "Return selfvalue.", + }, + SlotDef { + name: "__ge__", + accessor: SlotAccessor::TpRichcompare, + op: Some(SlotOp::Ge), + doc: "Return self>=value.", + }, + // Descriptor protocol + SlotDef { + name: "__get__", + accessor: SlotAccessor::TpDescrGet, + op: None, + doc: "Return an attribute of instance, which is of type owner.", + }, + SlotDef { + name: "__set__", + accessor: SlotAccessor::TpDescrSet, + op: None, + doc: "Set an attribute of instance to value.", + }, + SlotDef { + name: "__delete__", + accessor: SlotAccessor::TpDescrSet, + op: Some(SlotOp::Delete), + doc: "Delete an attribute of instance.", + }, + // Sequence protocol (sq_*) + SlotDef { + name: "__len__", + accessor: SlotAccessor::SqLength, + op: None, + doc: "Return len(self).", + }, + SlotDef { + name: "__getitem__", + accessor: SlotAccessor::SqItem, + op: None, + doc: "Return self[key].", + }, + SlotDef { + name: "__setitem__", + accessor: SlotAccessor::SqAssItem, + op: None, + doc: "Set self[key] to value.", + }, + SlotDef { + name: "__delitem__", + accessor: SlotAccessor::SqAssItem, + op: None, + doc: "Delete self[key].", + }, + SlotDef { + name: "__contains__", + accessor: SlotAccessor::SqContains, + op: None, + doc: "Return key in self.", + }, + // Mapping protocol (mp_*) + SlotDef { + name: "__len__", + accessor: SlotAccessor::MpLength, + op: None, + doc: "Return len(self).", + }, + SlotDef { + name: "__getitem__", + accessor: SlotAccessor::MpSubscript, + op: None, + doc: "Return self[key].", + }, + SlotDef { + name: "__setitem__", + accessor: SlotAccessor::MpAssSubscript, + op: None, + doc: "Set self[key] to value.", + }, + SlotDef { + name: "__delitem__", + accessor: SlotAccessor::MpAssSubscript, + op: None, + doc: "Delete self[key].", + }, + // Number protocol - binary ops with left/right variants + SlotDef { + name: "__add__", + accessor: SlotAccessor::NbAdd, + op: Some(SlotOp::Left), + doc: "Return self+value.", + }, + SlotDef { + name: "__radd__", + accessor: SlotAccessor::NbAdd, + op: Some(SlotOp::Right), + doc: "Return value+self.", + }, + SlotDef { + name: "__iadd__", + accessor: SlotAccessor::NbInplaceAdd, + op: None, + doc: "Implement self+=value.", + }, + SlotDef { + name: "__sub__", + accessor: SlotAccessor::NbSubtract, + op: Some(SlotOp::Left), + doc: "Return self-value.", + }, + SlotDef { + name: "__rsub__", + accessor: SlotAccessor::NbSubtract, + op: Some(SlotOp::Right), + doc: "Return value-self.", + }, + SlotDef { + name: "__isub__", + accessor: SlotAccessor::NbInplaceSubtract, + op: None, + doc: "Implement self-=value.", + }, + SlotDef { + name: "__mul__", + accessor: SlotAccessor::NbMultiply, + op: Some(SlotOp::Left), + doc: "Return self*value.", + }, + SlotDef { + name: "__rmul__", + accessor: SlotAccessor::NbMultiply, + op: Some(SlotOp::Right), + doc: "Return value*self.", + }, + SlotDef { + name: "__imul__", + accessor: SlotAccessor::NbInplaceMultiply, + op: None, + doc: "Implement self*=value.", + }, + SlotDef { + name: "__mod__", + accessor: SlotAccessor::NbRemainder, + op: Some(SlotOp::Left), + doc: "Return self%value.", + }, + SlotDef { + name: "__rmod__", + accessor: SlotAccessor::NbRemainder, + op: Some(SlotOp::Right), + doc: "Return value%self.", + }, + SlotDef { + name: "__imod__", + accessor: SlotAccessor::NbInplaceRemainder, + op: None, + doc: "Implement self%=value.", + }, + SlotDef { + name: "__divmod__", + accessor: SlotAccessor::NbDivmod, + op: Some(SlotOp::Left), + doc: "Return divmod(self, value).", + }, + SlotDef { + name: "__rdivmod__", + accessor: SlotAccessor::NbDivmod, + op: Some(SlotOp::Right), + doc: "Return divmod(value, self).", + }, + SlotDef { + name: "__pow__", + accessor: SlotAccessor::NbPower, + op: Some(SlotOp::Left), + doc: "Return pow(self, value, mod).", + }, + SlotDef { + name: "__rpow__", + accessor: SlotAccessor::NbPower, + op: Some(SlotOp::Right), + doc: "Return pow(value, self, mod).", + }, + SlotDef { + name: "__ipow__", + accessor: SlotAccessor::NbInplacePower, + op: None, + doc: "Implement self**=value.", + }, + SlotDef { + name: "__lshift__", + accessor: SlotAccessor::NbLshift, + op: Some(SlotOp::Left), + doc: "Return self<>value.", + }, + SlotDef { + name: "__rrshift__", + accessor: SlotAccessor::NbRshift, + op: Some(SlotOp::Right), + doc: "Return value>>self.", + }, + SlotDef { + name: "__irshift__", + accessor: SlotAccessor::NbInplaceRshift, + op: None, + doc: "Implement self>>=value.", + }, + SlotDef { + name: "__and__", + accessor: SlotAccessor::NbAnd, + op: Some(SlotOp::Left), + doc: "Return self&value.", + }, + SlotDef { + name: "__rand__", + accessor: SlotAccessor::NbAnd, + op: Some(SlotOp::Right), + doc: "Return value&self.", + }, + SlotDef { + name: "__iand__", + accessor: SlotAccessor::NbInplaceAnd, + op: None, + doc: "Implement self&=value.", + }, + SlotDef { + name: "__xor__", + accessor: SlotAccessor::NbXor, + op: Some(SlotOp::Left), + doc: "Return self^value.", + }, + SlotDef { + name: "__rxor__", + accessor: SlotAccessor::NbXor, + op: Some(SlotOp::Right), + doc: "Return value^self.", + }, + SlotDef { + name: "__ixor__", + accessor: SlotAccessor::NbInplaceXor, + op: None, + doc: "Implement self^=value.", + }, + SlotDef { + name: "__or__", + accessor: SlotAccessor::NbOr, + op: Some(SlotOp::Left), + doc: "Return self|value.", + }, + SlotDef { + name: "__ror__", + accessor: SlotAccessor::NbOr, + op: Some(SlotOp::Right), + doc: "Return value|self.", + }, + SlotDef { + name: "__ior__", + accessor: SlotAccessor::NbInplaceOr, + op: None, + doc: "Implement self|=value.", + }, + SlotDef { + name: "__floordiv__", + accessor: SlotAccessor::NbFloorDivide, + op: Some(SlotOp::Left), + doc: "Return self//value.", + }, + SlotDef { + name: "__rfloordiv__", + accessor: SlotAccessor::NbFloorDivide, + op: Some(SlotOp::Right), + doc: "Return value//self.", + }, + SlotDef { + name: "__ifloordiv__", + accessor: SlotAccessor::NbInplaceFloorDivide, + op: None, + doc: "Implement self//=value.", + }, + SlotDef { + name: "__truediv__", + accessor: SlotAccessor::NbTrueDivide, + op: Some(SlotOp::Left), + doc: "Return self/value.", + }, + SlotDef { + name: "__rtruediv__", + accessor: SlotAccessor::NbTrueDivide, + op: Some(SlotOp::Right), + doc: "Return value/self.", + }, + SlotDef { + name: "__itruediv__", + accessor: SlotAccessor::NbInplaceTrueDivide, + op: None, + doc: "Implement self/=value.", + }, + SlotDef { + name: "__matmul__", + accessor: SlotAccessor::NbMatrixMultiply, + op: Some(SlotOp::Left), + doc: "Return self@value.", + }, + SlotDef { + name: "__rmatmul__", + accessor: SlotAccessor::NbMatrixMultiply, + op: Some(SlotOp::Right), + doc: "Return value@self.", + }, + SlotDef { + name: "__imatmul__", + accessor: SlotAccessor::NbInplaceMatrixMultiply, + op: None, + doc: "Implement self@=value.", + }, + // Number unary operations + SlotDef { + name: "__neg__", + accessor: SlotAccessor::NbNegative, + op: None, + doc: "Return -self.", + }, + SlotDef { + name: "__pos__", + accessor: SlotAccessor::NbPositive, + op: None, + doc: "Return +self.", + }, + SlotDef { + name: "__abs__", + accessor: SlotAccessor::NbAbsolute, + op: None, + doc: "Return abs(self).", + }, + SlotDef { + name: "__invert__", + accessor: SlotAccessor::NbInvert, + op: None, + doc: "Return ~self.", + }, + SlotDef { + name: "__bool__", + accessor: SlotAccessor::NbBool, + op: None, + doc: "Return self != 0.", + }, + SlotDef { + name: "__int__", + accessor: SlotAccessor::NbInt, + op: None, + doc: "Return int(self).", + }, + SlotDef { + name: "__float__", + accessor: SlotAccessor::NbFloat, + op: None, + doc: "Return float(self).", + }, + SlotDef { + name: "__index__", + accessor: SlotAccessor::NbIndex, + op: None, + doc: "Return self converted to an integer, if self is suitable for use as an index into a list.", + }, + // Sequence inplace operations (also map to number slots for some types) + SlotDef { + name: "__add__", + accessor: SlotAccessor::SqConcat, + op: None, + doc: "Return self+value.", + }, + SlotDef { + name: "__mul__", + accessor: SlotAccessor::SqRepeat, + op: None, + doc: "Return self*value.", + }, + SlotDef { + name: "__iadd__", + accessor: SlotAccessor::SqInplaceConcat, + op: None, + doc: "Implement self+=value.", + }, + SlotDef { + name: "__imul__", + accessor: SlotAccessor::SqInplaceRepeat, + op: None, + doc: "Implement self*=value.", + }, +]; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_find_by_name() { + // __len__ appears in both sequence and mapping + let len_defs: Vec<_> = find_slot_defs_by_name("__len__").collect(); + assert_eq!(len_defs.len(), 2); + + // __init__ appears once + let init_defs: Vec<_> = find_slot_defs_by_name("__init__").collect(); + assert_eq!(init_defs.len(), 1); + + // __add__ appears in number (left/right) and sequence + let add_defs: Vec<_> = find_slot_defs_by_name("__add__").collect(); + assert_eq!(add_defs.len(), 2); // NbAdd(Left) and SqConcat + } + + #[test] + fn test_slot_op() { + // Test comparison ops + assert_eq!(SlotOp::Lt.as_compare_op(), Some(PyComparisonOp::Lt)); + assert_eq!(SlotOp::Eq.as_compare_op(), Some(PyComparisonOp::Eq)); + assert_eq!(SlotOp::Left.as_compare_op(), None); + + // Test right check + assert!(SlotOp::Right.is_right()); + assert!(!SlotOp::Left.is_right()); + } +} diff --git a/crates/vm/src/types/zoo.rs b/crates/vm/src/types/zoo.rs index 9d164241676..c4aa7258c07 100644 --- a/crates/vm/src/types/zoo.rs +++ b/crates/vm/src/types/zoo.rs @@ -189,7 +189,7 @@ impl TypeZoo { generic_alias_type: genericalias::PyGenericAlias::init_builtin_type(), union_type: union_::PyUnion::init_builtin_type(), member_descriptor_type: descriptor::PyMemberDescriptor::init_builtin_type(), - wrapper_descriptor_type: descriptor::PySlotWrapper::init_builtin_type(), + wrapper_descriptor_type: descriptor::PyWrapper::init_builtin_type(), method_wrapper_type: descriptor::PyMethodWrapper::init_builtin_type(), method_def: crate::function::HeapMethodDef::init_builtin_type(),