Skip to main content

rustpython_vm/types/
slot.rs

1use crate::common::lock::{
2    PyMappedRwLockReadGuard, PyMappedRwLockWriteGuard, PyRwLockReadGuard, PyRwLockWriteGuard,
3};
4use crate::{
5    AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
6    builtins::{PyInt, PyStr, PyStrInterned, PyType, PyTypeRef},
7    bytecode::ComparisonOperator,
8    common::hash::{PyHash, fix_sentinel, hash_bigint},
9    convert::ToPyObject,
10    function::{Either, FromArgs, FuncArgs, PyComparisonValue, PyMethodDef, PySetterValue},
11    protocol::{
12        PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyMappingSlots, PyNumber,
13        PyNumberMethods, PyNumberSlots, PySequence, PySequenceMethods, PySequenceSlots,
14    },
15    types::slot_defs::{SlotAccessor, find_slot_defs_by_name},
16    vm::Context,
17};
18use core::{any::Any, any::TypeId, borrow::Borrow, cmp::Ordering, ops::Deref};
19use crossbeam_utils::atomic::AtomicCell;
20use num_traits::{Signed, ToPrimitive};
21use rustpython_common::wtf8::Wtf8Buf;
22
23/// Type-erased storage for extension module data attached to heap types.
24pub struct TypeDataSlot {
25    // PyObject_GetTypeData
26    type_id: TypeId,
27    data: Box<dyn Any + Send + Sync>,
28}
29
30impl TypeDataSlot {
31    /// Create a new type data slot with the given data.
32    pub fn new<T: Any + Send + Sync + 'static>(data: T) -> Self {
33        Self {
34            type_id: TypeId::of::<T>(),
35            data: Box::new(data),
36        }
37    }
38
39    /// Get a reference to the data if the type matches.
40    pub fn get<T: Any + 'static>(&self) -> Option<&T> {
41        if self.type_id == TypeId::of::<T>() {
42            self.data.downcast_ref()
43        } else {
44            None
45        }
46    }
47
48    /// Get a mutable reference to the data if the type matches.
49    pub fn get_mut<T: Any + 'static>(&mut self) -> Option<&mut T> {
50        if self.type_id == TypeId::of::<T>() {
51            self.data.downcast_mut()
52        } else {
53            None
54        }
55    }
56}
57
58/// Read guard for type data access, using mapped guard for zero-cost deref.
59pub struct TypeDataRef<'a, T: 'static> {
60    guard: PyMappedRwLockReadGuard<'a, T>,
61}
62
63impl<'a, T: Any + 'static> TypeDataRef<'a, T> {
64    /// Try to create a TypeDataRef from a read guard.
65    /// Returns None if the slot is empty or contains a different type.
66    pub fn try_new(guard: PyRwLockReadGuard<'a, Option<TypeDataSlot>>) -> Option<Self> {
67        PyRwLockReadGuard::try_map(guard, |opt| opt.as_ref().and_then(|slot| slot.get::<T>()))
68            .ok()
69            .map(|guard| Self { guard })
70    }
71}
72
73impl<T: Any + 'static> core::ops::Deref for TypeDataRef<'_, T> {
74    type Target = T;
75
76    fn deref(&self) -> &Self::Target {
77        &self.guard
78    }
79}
80
81/// Write guard for type data access, using mapped guard for zero-cost deref.
82pub struct TypeDataRefMut<'a, T: 'static> {
83    guard: PyMappedRwLockWriteGuard<'a, T>,
84}
85
86impl<'a, T: Any + 'static> TypeDataRefMut<'a, T> {
87    /// Try to create a TypeDataRefMut from a write guard.
88    /// Returns None if the slot is empty or contains a different type.
89    pub fn try_new(guard: PyRwLockWriteGuard<'a, Option<TypeDataSlot>>) -> Option<Self> {
90        PyRwLockWriteGuard::try_map(guard, |opt| {
91            opt.as_mut().and_then(|slot| slot.get_mut::<T>())
92        })
93        .ok()
94        .map(|guard| Self { guard })
95    }
96}
97
98impl<T: Any + 'static> core::ops::Deref for TypeDataRefMut<'_, T> {
99    type Target = T;
100
101    fn deref(&self) -> &Self::Target {
102        &self.guard
103    }
104}
105
106impl<T: Any + 'static> core::ops::DerefMut for TypeDataRefMut<'_, T> {
107    fn deref_mut(&mut self) -> &mut Self::Target {
108        &mut self.guard
109    }
110}
111
112#[macro_export]
113macro_rules! atomic_func {
114    ($x:expr) => {
115        Some($x)
116    };
117}
118
119// The corresponding field in CPython is `tp_` prefixed.
120// e.g. name -> tp_name
121#[derive(Default)]
122#[non_exhaustive]
123pub struct PyTypeSlots {
124    /// # Safety
125    /// For static types, always safe.
126    /// For heap types, `__name__` must alive
127    pub(crate) name: &'static str, // tp_name with <module>.<class> for print, not class name
128
129    pub basicsize: usize,
130    pub itemsize: usize, // tp_itemsize
131
132    // Methods to implement standard operations
133
134    // Method suites for standard classes
135    pub as_number: PyNumberSlots,
136    pub as_sequence: PySequenceSlots,
137    pub as_mapping: PyMappingSlots,
138
139    // More standard operations (here for binary compatibility)
140    pub hash: AtomicCell<Option<HashFunc>>,
141    pub call: AtomicCell<Option<GenericMethod>>,
142    pub vectorcall: AtomicCell<Option<VectorCallFunc>>,
143    pub str: AtomicCell<Option<StringifyFunc>>,
144    pub repr: AtomicCell<Option<StringifyFunc>>,
145    pub getattro: AtomicCell<Option<GetattroFunc>>,
146    pub setattro: AtomicCell<Option<SetattroFunc>>,
147
148    // Functions to access object as input/output buffer
149    pub as_buffer: Option<AsBufferFunc>,
150
151    // Assigned meaning in release 2.1
152    // rich comparisons
153    pub richcompare: AtomicCell<Option<RichCompareFunc>>,
154
155    // Iterators
156    pub iter: AtomicCell<Option<IterFunc>>,
157    pub iternext: AtomicCell<Option<IterNextFunc>>,
158
159    pub methods: &'static [PyMethodDef],
160
161    // Flags to define presence of optional/expanded features
162    pub flags: PyTypeFlags,
163
164    // tp_doc
165    pub doc: Option<&'static str>,
166
167    // Strong reference on a heap type, borrowed reference on a static type
168    // tp_base
169    // tp_dict
170    pub descr_get: AtomicCell<Option<DescrGetFunc>>,
171    pub descr_set: AtomicCell<Option<DescrSetFunc>>,
172    // tp_dictoffset
173    pub init: AtomicCell<Option<InitFunc>>,
174    // tp_alloc
175    pub alloc: AtomicCell<Option<AllocFunc>>,
176    pub new: AtomicCell<Option<NewFunc>>,
177    // tp_free
178    // tp_is_gc
179    // tp_bases
180    // tp_mro
181    // tp_cache
182    // tp_subclasses
183    // tp_weaklist
184    pub del: AtomicCell<Option<DelFunc>>,
185
186    // The count of tp_members.
187    pub member_count: usize,
188}
189
190impl PyTypeSlots {
191    pub fn new(name: &'static str, flags: PyTypeFlags) -> Self {
192        Self {
193            name,
194            flags,
195            ..Default::default()
196        }
197    }
198
199    pub fn heap_default() -> Self {
200        Self {
201            // init: AtomicCell::new(Some(init_wrapper)),
202            ..Default::default()
203        }
204    }
205}
206
207impl core::fmt::Debug for PyTypeSlots {
208    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
209        f.write_str("PyTypeSlots")
210    }
211}
212
213bitflags! {
214    #[derive(Copy, Clone, Debug, PartialEq)]
215    #[non_exhaustive]
216    pub struct PyTypeFlags: u64 {
217        const MANAGED_WEAKREF = 1 << 3;
218        const MANAGED_DICT = 1 << 4;
219        const SEQUENCE = 1 << 5;
220        const MAPPING = 1 << 6;
221        const DISALLOW_INSTANTIATION = 1 << 7;
222        const IMMUTABLETYPE = 1 << 8;
223        const HEAPTYPE = 1 << 9;
224        const BASETYPE = 1 << 10;
225        const METHOD_DESCRIPTOR = 1 << 17;
226        // For built-in types that match the subject itself in pattern matching
227        // (bool, int, float, str, bytes, bytearray, list, tuple, dict, set, frozenset)
228        // This is not a stable API
229        const _MATCH_SELF = 1 << 22;
230        const HAS_DICT = 1 << 40;
231        const HAS_WEAKREF = 1 << 41;
232
233        #[cfg(debug_assertions)]
234        const _CREATED_WITH_FLAGS = 1 << 63;
235    }
236}
237
238impl PyTypeFlags {
239    // Default used for both built-in and normal classes: empty, for now.
240    // CPython default: Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | Py_TPFLAGS_HAVE_VERSION_TAG
241    pub const DEFAULT: Self = Self::empty();
242
243    // CPython: See initialization of flags in type_new.
244    /// Used for types created in Python. Subclassable and are a
245    /// heaptype.
246    pub const fn heap_type_flags() -> Self {
247        match Self::from_bits(Self::DEFAULT.bits() | Self::HEAPTYPE.bits() | Self::BASETYPE.bits())
248        {
249            Some(flags) => flags,
250            None => unreachable!(),
251        }
252    }
253
254    pub const fn has_feature(self, flag: Self) -> bool {
255        self.contains(flag)
256    }
257
258    #[cfg(debug_assertions)]
259    pub const fn is_created_with_flags(self) -> bool {
260        self.contains(Self::_CREATED_WITH_FLAGS)
261    }
262}
263
264impl Default for PyTypeFlags {
265    fn default() -> Self {
266        Self::DEFAULT
267    }
268}
269
270pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult;
271/// Vectorcall function pointer (PEP 590).
272/// args: owned positional args followed by kwarg values.
273/// nargs: number of positional args (self prepended by caller if needed).
274/// kwnames: keyword argument names (last kwnames.len() entries in args are kwarg values).
275pub(crate) type VectorCallFunc = fn(
276    &PyObject,              // callable
277    Vec<PyObjectRef>,       // owned args (positional + kwarg values)
278    usize,                  // nargs (positional count)
279    Option<&[PyObjectRef]>, // kwnames (keyword argument names)
280    &VirtualMachine,
281) -> PyResult;
282pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyHash>;
283// CallFunc = GenericMethod
284pub(crate) type StringifyFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyRef<PyStr>>;
285pub(crate) type GetattroFunc = fn(&PyObject, &Py<PyStr>, &VirtualMachine) -> PyResult;
286pub(crate) type SetattroFunc =
287    fn(&PyObject, &Py<PyStr>, PySetterValue, &VirtualMachine) -> PyResult<()>;
288pub(crate) type AsBufferFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyBuffer>;
289pub(crate) type RichCompareFunc = fn(
290    &PyObject,
291    &PyObject,
292    PyComparisonOp,
293    &VirtualMachine,
294) -> PyResult<Either<PyObjectRef, PyComparisonValue>>;
295pub(crate) type IterFunc = fn(PyObjectRef, &VirtualMachine) -> PyResult;
296pub(crate) type IterNextFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyIterReturn>;
297pub(crate) type DescrGetFunc =
298    fn(PyObjectRef, Option<PyObjectRef>, Option<PyObjectRef>, &VirtualMachine) -> PyResult;
299pub(crate) type DescrSetFunc =
300    fn(&PyObject, PyObjectRef, PySetterValue, &VirtualMachine) -> PyResult<()>;
301pub(crate) type AllocFunc = fn(PyTypeRef, usize, &VirtualMachine) -> PyResult;
302pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult;
303pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult<()>;
304pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>;
305
306// Sequence sub-slot function types
307pub(crate) type SeqLenFunc = fn(PySequence<'_>, &VirtualMachine) -> PyResult<usize>;
308pub(crate) type SeqConcatFunc = fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult;
309pub(crate) type SeqRepeatFunc = fn(PySequence<'_>, isize, &VirtualMachine) -> PyResult;
310pub(crate) type SeqItemFunc = fn(PySequence<'_>, isize, &VirtualMachine) -> PyResult;
311pub(crate) type SeqAssItemFunc =
312    fn(PySequence<'_>, isize, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>;
313pub(crate) type SeqContainsFunc = fn(PySequence<'_>, &PyObject, &VirtualMachine) -> PyResult<bool>;
314
315// Mapping sub-slot function types
316pub(crate) type MapLenFunc = fn(PyMapping<'_>, &VirtualMachine) -> PyResult<usize>;
317pub(crate) type MapSubscriptFunc = fn(PyMapping<'_>, &PyObject, &VirtualMachine) -> PyResult;
318pub(crate) type MapAssSubscriptFunc =
319    fn(PyMapping<'_>, &PyObject, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>;
320
321// slot_sq_length
322pub(crate) fn len_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> {
323    let ret = vm.call_special_method(obj, identifier!(vm, __len__), ())?;
324    let len = ret.downcast_ref::<PyInt>().ok_or_else(|| {
325        vm.new_type_error(format!(
326            "'{}' object cannot be interpreted as an integer",
327            ret.class()
328        ))
329    })?;
330    let len = len.as_bigint();
331    if len.is_negative() {
332        return Err(vm.new_value_error("__len__() should return >= 0"));
333    }
334    let len = len
335        .to_isize()
336        .ok_or_else(|| vm.new_overflow_error("cannot fit 'int' into an index-sized integer"))?;
337    Ok(len as usize)
338}
339
340pub(crate) fn contains_wrapper(
341    obj: &PyObject,
342    needle: &PyObject,
343    vm: &VirtualMachine,
344) -> PyResult<bool> {
345    let ret = vm.call_special_method(obj, identifier!(vm, __contains__), (needle,))?;
346    ret.try_to_bool(vm)
347}
348
349macro_rules! number_unary_op_wrapper {
350    ($name:ident) => {
351        |a, vm| vm.call_special_method(a.deref(), identifier!(vm, $name), ())
352    };
353}
354macro_rules! number_binary_op_wrapper {
355    ($name:ident) => {
356        |a, b, vm| vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(),))
357    };
358}
359macro_rules! number_binary_right_op_wrapper {
360    ($name:ident) => {
361        |a, b, vm| vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(),))
362    };
363}
364macro_rules! number_ternary_op_wrapper {
365    ($name:ident) => {
366        |a, b, c, vm: &VirtualMachine| {
367            if vm.is_none(c) {
368                vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(),))
369            } else {
370                vm.call_special_method(a, identifier!(vm, $name), (b.to_owned(), c.to_owned()))
371            }
372        }
373    };
374}
375macro_rules! number_ternary_right_op_wrapper {
376    ($name:ident) => {
377        |a, b, c, vm: &VirtualMachine| {
378            if vm.is_none(c) {
379                vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(),))
380            } else {
381                vm.call_special_method(b, identifier!(vm, $name), (a.to_owned(), c.to_owned()))
382            }
383        }
384    };
385}
386fn getitem_wrapper<K: ToPyObject>(obj: &PyObject, needle: K, vm: &VirtualMachine) -> PyResult {
387    vm.call_special_method(obj, identifier!(vm, __getitem__), (needle,))
388}
389
390fn setitem_wrapper<K: ToPyObject>(
391    obj: &PyObject,
392    needle: K,
393    value: Option<PyObjectRef>,
394    vm: &VirtualMachine,
395) -> PyResult<()> {
396    match value {
397        Some(value) => vm.call_special_method(obj, identifier!(vm, __setitem__), (needle, value)),
398        None => vm.call_special_method(obj, identifier!(vm, __delitem__), (needle,)),
399    }
400    .map(drop)
401}
402
403#[inline(never)]
404fn mapping_setitem_wrapper(
405    mapping: PyMapping<'_>,
406    key: &PyObject,
407    value: Option<PyObjectRef>,
408    vm: &VirtualMachine,
409) -> PyResult<()> {
410    setitem_wrapper(mapping.obj, key, value, vm)
411}
412
413#[inline(never)]
414fn mapping_getitem_wrapper(
415    mapping: PyMapping<'_>,
416    key: &PyObject,
417    vm: &VirtualMachine,
418) -> PyResult {
419    getitem_wrapper(mapping.obj, key, vm)
420}
421
422#[inline(never)]
423fn mapping_len_wrapper(mapping: PyMapping<'_>, vm: &VirtualMachine) -> PyResult<usize> {
424    len_wrapper(mapping.obj, vm)
425}
426
427#[inline(never)]
428fn sequence_len_wrapper(seq: PySequence<'_>, vm: &VirtualMachine) -> PyResult<usize> {
429    len_wrapper(seq.obj, vm)
430}
431
432#[inline(never)]
433fn sequence_getitem_wrapper(seq: PySequence<'_>, i: isize, vm: &VirtualMachine) -> PyResult {
434    getitem_wrapper(seq.obj, i, vm)
435}
436
437#[inline(never)]
438fn sequence_setitem_wrapper(
439    seq: PySequence<'_>,
440    i: isize,
441    value: Option<PyObjectRef>,
442    vm: &VirtualMachine,
443) -> PyResult<()> {
444    setitem_wrapper(seq.obj, i, value, vm)
445}
446
447#[inline(never)]
448fn sequence_contains_wrapper(
449    seq: PySequence<'_>,
450    needle: &PyObject,
451    vm: &VirtualMachine,
452) -> PyResult<bool> {
453    contains_wrapper(seq.obj, needle, vm)
454}
455
456#[inline(never)]
457fn sequence_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult {
458    vm.call_special_method(seq.obj, identifier!(vm, __mul__), (n,))
459}
460
461#[inline(never)]
462fn sequence_inplace_repeat_wrapper(seq: PySequence<'_>, n: isize, vm: &VirtualMachine) -> PyResult {
463    vm.call_special_method(seq.obj, identifier!(vm, __imul__), (n,))
464}
465
466fn repr_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
467    let ret = vm.call_special_method(zelf, identifier!(vm, __repr__), ())?;
468    ret.downcast::<PyStr>().map_err(|obj| {
469        vm.new_type_error(format!(
470            "__repr__ returned non-string (type {})",
471            obj.class()
472        ))
473    })
474}
475
476fn str_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
477    let ret = vm.call_special_method(zelf, identifier!(vm, __str__), ())?;
478    ret.downcast::<PyStr>().map_err(|obj| {
479        vm.new_type_error(format!(
480            "__str__ returned non-string (type {})",
481            obj.class()
482        ))
483    })
484}
485
486fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
487    let hash_obj = vm.call_special_method(zelf, identifier!(vm, __hash__), ())?;
488    let py_int = hash_obj
489        .downcast_ref::<PyInt>()
490        .ok_or_else(|| vm.new_type_error("__hash__ method should return an integer"))?;
491    let big_int = py_int.as_bigint();
492    let hash = big_int
493        .to_i64()
494        .map(fix_sentinel)
495        .unwrap_or_else(|| hash_bigint(big_int));
496    Ok(hash)
497}
498
499/// Marks a type as unhashable. Similar to PyObject_HashNotImplemented in CPython
500pub fn hash_not_implemented(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
501    Err(vm.new_type_error(format!("unhashable type: '{}'", zelf.class().name())))
502}
503
504fn call_wrapper(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
505    vm.call_special_method(zelf, identifier!(vm, __call__), args)
506}
507
508fn getattro_wrapper(zelf: &PyObject, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
509    let __getattribute__ = identifier!(vm, __getattribute__);
510    let __getattr__ = identifier!(vm, __getattr__);
511    match vm.call_special_method(zelf, __getattribute__, (name.to_owned(),)) {
512        Ok(r) => Ok(r),
513        Err(e)
514            if e.fast_isinstance(vm.ctx.exceptions.attribute_error)
515                && zelf.class().has_attr(__getattr__) =>
516        {
517            vm.call_special_method(zelf, __getattr__, (name.to_owned(),))
518        }
519        Err(e) => Err(e),
520    }
521}
522
523fn setattro_wrapper(
524    zelf: &PyObject,
525    name: &Py<PyStr>,
526    value: PySetterValue,
527    vm: &VirtualMachine,
528) -> PyResult<()> {
529    let name = name.to_owned();
530    match value {
531        PySetterValue::Assign(value) => {
532            vm.call_special_method(zelf, identifier!(vm, __setattr__), (name, value))?;
533        }
534        PySetterValue::Delete => {
535            vm.call_special_method(zelf, identifier!(vm, __delattr__), (name,))?;
536        }
537    };
538    Ok(())
539}
540
541pub(crate) fn richcompare_wrapper(
542    zelf: &PyObject,
543    other: &PyObject,
544    op: PyComparisonOp,
545    vm: &VirtualMachine,
546) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
547    vm.call_special_method(zelf, op.method_name(&vm.ctx), (other.to_owned(),))
548        .map(Either::A)
549}
550
551fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
552    // slot_tp_iter: if __iter__ is None, the type is explicitly not iterable
553    let cls = zelf.class();
554    let iter_attr = cls.get_attr(identifier!(vm, __iter__));
555    match iter_attr {
556        Some(attr) if vm.is_none(&attr) => {
557            Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name())))
558        }
559        _ => vm.call_special_method(&zelf, identifier!(vm, __iter__), ()),
560    }
561}
562
563fn bool_wrapper(num: PyNumber<'_>, vm: &VirtualMachine) -> PyResult<bool> {
564    let result = vm.call_special_method(num.obj, identifier!(vm, __bool__), ())?;
565    // __bool__ must return exactly bool, not int subclass
566    if !result.class().is(vm.ctx.types.bool_type) {
567        return Err(vm.new_type_error(format!(
568            "__bool__ should return bool, returned {}",
569            result.class().name()
570        )));
571    }
572    Ok(crate::builtins::bool_::get_value(&result))
573}
574
575// PyObject_SelfIter in CPython
576const fn self_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult {
577    Ok(zelf)
578}
579
580fn iternext_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
581    PyIterReturn::from_pyresult(
582        vm.call_special_method(zelf, identifier!(vm, __next__), ()),
583        vm,
584    )
585}
586
587fn descr_get_wrapper(
588    zelf: PyObjectRef,
589    obj: Option<PyObjectRef>,
590    cls: Option<PyObjectRef>,
591    vm: &VirtualMachine,
592) -> PyResult {
593    vm.call_special_method(&zelf, identifier!(vm, __get__), (obj, cls))
594}
595
596fn descr_set_wrapper(
597    zelf: &PyObject,
598    obj: PyObjectRef,
599    value: PySetterValue,
600    vm: &VirtualMachine,
601) -> PyResult<()> {
602    match value {
603        PySetterValue::Assign(val) => {
604            vm.call_special_method(zelf, identifier!(vm, __set__), (obj, val))
605        }
606        PySetterValue::Delete => vm.call_special_method(zelf, identifier!(vm, __delete__), (obj,)),
607    }
608    .map(drop)
609}
610
611fn init_wrapper(obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
612    let res = vm.call_special_method(&obj, identifier!(vm, __init__), args)?;
613    if !vm.is_none(&res) {
614        return Err(vm.new_type_error(format!(
615            "__init__() should return None, not '{:.200}'",
616            res.class().name()
617        )));
618    }
619    Ok(())
620}
621
622pub(crate) fn new_wrapper(cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
623    let new = cls.get_attr(identifier!(vm, __new__)).unwrap();
624    args.prepend_arg(cls.into());
625    new.call(args, vm)
626}
627
628fn del_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
629    vm.call_special_method(zelf, identifier!(vm, __del__), ())?;
630    Ok(())
631}
632
633/// Result of looking up a slot function in MRO.
634enum SlotLookupResult<T> {
635    /// Found a native slot function from a wrapper_descriptor.
636    NativeSlot(T),
637    /// Found a Python-level method (not a native slot).
638    /// The caller should use the wrapper function.
639    PythonMethod,
640    /// No method with this name found in MRO at all.
641    /// The caller should inherit the slot from MRO.
642    NotFound,
643}
644
645impl PyType {
646    /// Update slots based on dunder method changes
647    ///
648    /// Iterates SLOT_DEFS to find all slots matching the given name and updates them.
649    /// Also recursively updates subclasses that don't have their own definition.
650    pub(crate) fn update_slot<const ADD: bool>(&self, name: &'static PyStrInterned, ctx: &Context) {
651        debug_assert!(name.as_str().starts_with("__"));
652        debug_assert!(name.as_str().ends_with("__"));
653
654        // Find all slot_defs matching this name and update each
655        // NOTE: Collect into Vec first to avoid issues during iteration
656        let defs: Vec<_> = find_slot_defs_by_name(name.as_str()).collect();
657        for def in defs {
658            self.update_one_slot::<ADD>(&def.accessor, name, ctx);
659        }
660
661        // Recursively update subclasses that don't have their own definition
662        self.update_subclasses::<ADD>(name, ctx);
663    }
664
665    /// Recursively update subclasses' slots
666    /// recurse_down_subclasses
667    fn update_subclasses<const ADD: bool>(&self, name: &'static PyStrInterned, ctx: &Context) {
668        let subclasses = self.subclasses.read();
669        for weak_ref in subclasses.iter() {
670            let Some(subclass) = weak_ref.upgrade() else {
671                continue;
672            };
673            let Some(subclass) = subclass.downcast_ref::<PyType>() else {
674                continue;
675            };
676
677            // Skip if subclass has its own definition for this attribute
678            if subclass.attributes.read().contains_key(name) {
679                continue;
680            }
681
682            // Update subclass's slots
683            for def in find_slot_defs_by_name(name.as_str()) {
684                subclass.update_one_slot::<ADD>(&def.accessor, name, ctx);
685            }
686
687            // Recurse into subclass's subclasses
688            subclass.update_subclasses::<ADD>(name, ctx);
689        }
690    }
691
692    /// Update a single slot
693    fn update_one_slot<const ADD: bool>(
694        &self,
695        accessor: &SlotAccessor,
696        name: &'static PyStrInterned,
697        ctx: &Context,
698    ) {
699        use crate::builtins::descriptor::SlotFunc;
700
701        // Helper macro for main slots
702        macro_rules! update_main_slot {
703            ($slot:ident, $wrapper:expr, $variant:ident) => {{
704                if ADD {
705                    match self.lookup_slot_in_mro(name, ctx, |sf| {
706                        if let SlotFunc::$variant(f) = sf {
707                            Some(*f)
708                        } else {
709                            None
710                        }
711                    }) {
712                        SlotLookupResult::NativeSlot(func) => {
713                            self.slots.$slot.store(Some(func));
714                        }
715                        SlotLookupResult::PythonMethod => {
716                            self.slots.$slot.store(Some($wrapper));
717                        }
718                        SlotLookupResult::NotFound => {
719                            accessor.inherit_from_mro(self);
720                        }
721                    }
722                } else {
723                    accessor.inherit_from_mro(self);
724                }
725            }};
726        }
727
728        // Helper macro for number/sequence/mapping sub-slots
729        macro_rules! update_sub_slot {
730            ($group:ident, $slot:ident, $wrapper:expr, $variant:ident) => {{
731                if ADD {
732                    // Check if this type defines any method that maps to this slot.
733                    // Some slots like SqAssItem/MpAssSubscript are shared by multiple
734                    // methods (__setitem__ and __delitem__). If any of those methods
735                    // is defined, we must use the wrapper to ensure Python method calls.
736                    let has_own = {
737                        let guard = self.attributes.read();
738                        // Check the current method name
739                        let mut result = guard.contains_key(name);
740                        // For ass_item/ass_subscript slots, also check the paired method
741                        // (__setitem__ and __delitem__ share the same slot)
742                        if !result
743                            && (stringify!($slot) == "ass_item"
744                                || stringify!($slot) == "ass_subscript")
745                        {
746                            let setitem = ctx.intern_str("__setitem__");
747                            let delitem = ctx.intern_str("__delitem__");
748                            result = guard.contains_key(setitem) || guard.contains_key(delitem);
749                        }
750                        result
751                    };
752                    if has_own {
753                        self.slots.$group.$slot.store(Some($wrapper));
754                    } else {
755                        match self.lookup_slot_in_mro(name, ctx, |sf| {
756                            if let SlotFunc::$variant(f) = sf {
757                                Some(*f)
758                            } else {
759                                None
760                            }
761                        }) {
762                            SlotLookupResult::NativeSlot(func) => {
763                                self.slots.$group.$slot.store(Some(func));
764                            }
765                            SlotLookupResult::PythonMethod => {
766                                self.slots.$group.$slot.store(Some($wrapper));
767                            }
768                            SlotLookupResult::NotFound => {
769                                accessor.inherit_from_mro(self);
770                            }
771                        }
772                    }
773                } else {
774                    accessor.inherit_from_mro(self);
775                }
776            }};
777        }
778
779        match accessor {
780            // === Main slots ===
781            SlotAccessor::TpRepr => update_main_slot!(repr, repr_wrapper, Repr),
782            SlotAccessor::TpStr => update_main_slot!(str, str_wrapper, Str),
783            SlotAccessor::TpHash => {
784                // Special handling for __hash__ = None
785                if ADD {
786                    let method = self.attributes.read().get(name).cloned().or_else(|| {
787                        self.mro
788                            .read()
789                            .iter()
790                            .find_map(|cls| cls.attributes.read().get(name).cloned())
791                    });
792
793                    if method.as_ref().is_some_and(|m| m.is(&ctx.none)) {
794                        self.slots.hash.store(Some(hash_not_implemented));
795                    } else {
796                        match self.lookup_slot_in_mro(name, ctx, |sf| {
797                            if let SlotFunc::Hash(f) = sf {
798                                Some(*f)
799                            } else {
800                                None
801                            }
802                        }) {
803                            SlotLookupResult::NativeSlot(func) => {
804                                self.slots.hash.store(Some(func));
805                            }
806                            SlotLookupResult::PythonMethod => {
807                                self.slots.hash.store(Some(hash_wrapper));
808                            }
809                            SlotLookupResult::NotFound => {
810                                accessor.inherit_from_mro(self);
811                            }
812                        }
813                    }
814                } else {
815                    accessor.inherit_from_mro(self);
816                }
817            }
818            SlotAccessor::TpCall => {
819                update_main_slot!(call, call_wrapper, Call);
820                // When __call__ is overridden in Python, clear vectorcall
821                // so the slow path through call_wrapper is used.
822                if ADD {
823                    self.slots.vectorcall.store(None);
824                }
825            }
826            SlotAccessor::TpIter => update_main_slot!(iter, iter_wrapper, Iter),
827            SlotAccessor::TpIternext => update_main_slot!(iternext, iternext_wrapper, IterNext),
828            SlotAccessor::TpInit => {
829                update_main_slot!(init, init_wrapper, Init);
830                if ADD {
831                    self.slots.vectorcall.store(None);
832                }
833            }
834            SlotAccessor::TpNew => {
835                // __new__ is not wrapped via PyWrapper
836                if ADD {
837                    self.slots.new.store(Some(new_wrapper));
838                    self.slots.vectorcall.store(None);
839                } else {
840                    accessor.inherit_from_mro(self);
841                }
842            }
843            SlotAccessor::TpDel => update_main_slot!(del, del_wrapper, Del),
844            SlotAccessor::TpGetattro => {
845                // __getattribute__ and __getattr__ both map to TpGetattro.
846                // If __getattr__ is defined anywhere in MRO, we must use the wrapper
847                // because the native slot won't call __getattr__.
848                let __getattr__ = identifier!(ctx, __getattr__);
849                let has_getattr = {
850                    let attrs = self.attributes.read();
851                    let in_self = attrs.contains_key(__getattr__);
852                    drop(attrs);
853                    // mro[0] is self, so skip it
854                    in_self
855                        || self
856                            .mro
857                            .read()
858                            .iter()
859                            .skip(1)
860                            .any(|cls| cls.attributes.read().contains_key(__getattr__))
861                };
862
863                if has_getattr {
864                    // Must use wrapper to handle __getattr__
865                    self.slots.getattro.store(Some(getattro_wrapper));
866                } else if ADD {
867                    match self.lookup_slot_in_mro(name, ctx, |sf| {
868                        if let SlotFunc::GetAttro(f) = sf {
869                            Some(*f)
870                        } else {
871                            None
872                        }
873                    }) {
874                        SlotLookupResult::NativeSlot(func) => {
875                            self.slots.getattro.store(Some(func));
876                        }
877                        SlotLookupResult::PythonMethod => {
878                            self.slots.getattro.store(Some(getattro_wrapper));
879                        }
880                        SlotLookupResult::NotFound => {
881                            accessor.inherit_from_mro(self);
882                        }
883                    }
884                } else {
885                    accessor.inherit_from_mro(self);
886                }
887            }
888            SlotAccessor::TpSetattro => {
889                // __setattr__ and __delattr__ share the same slot
890                if ADD {
891                    match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
892                        SlotFunc::SetAttro(f) | SlotFunc::DelAttro(f) => Some(*f),
893                        _ => None,
894                    }) {
895                        SlotLookupResult::NativeSlot(func) => {
896                            self.slots.setattro.store(Some(func));
897                        }
898                        SlotLookupResult::PythonMethod => {
899                            self.slots.setattro.store(Some(setattro_wrapper));
900                        }
901                        SlotLookupResult::NotFound => {
902                            accessor.inherit_from_mro(self);
903                        }
904                    }
905                } else {
906                    accessor.inherit_from_mro(self);
907                }
908            }
909            SlotAccessor::TpDescrGet => update_main_slot!(descr_get, descr_get_wrapper, DescrGet),
910            SlotAccessor::TpDescrSet => {
911                // __set__ and __delete__ share the same slot
912                if ADD {
913                    match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
914                        SlotFunc::DescrSet(f) | SlotFunc::DescrDel(f) => Some(*f),
915                        _ => None,
916                    }) {
917                        SlotLookupResult::NativeSlot(func) => {
918                            self.slots.descr_set.store(Some(func));
919                        }
920                        SlotLookupResult::PythonMethod => {
921                            self.slots.descr_set.store(Some(descr_set_wrapper));
922                        }
923                        SlotLookupResult::NotFound => {
924                            accessor.inherit_from_mro(self);
925                        }
926                    }
927                } else {
928                    accessor.inherit_from_mro(self);
929                }
930            }
931
932            // === Rich compare (__lt__, __le__, __eq__, __ne__, __gt__, __ge__) ===
933            SlotAccessor::TpRichcompare => {
934                if ADD {
935                    // Check if self or any class in MRO has a Python-defined comparison method
936                    // All comparison ops share the same slot, so if any is overridden anywhere
937                    // in the hierarchy with a Python function, we need to use the wrapper
938                    let cmp_names = [
939                        identifier!(ctx, __eq__),
940                        identifier!(ctx, __ne__),
941                        identifier!(ctx, __lt__),
942                        identifier!(ctx, __le__),
943                        identifier!(ctx, __gt__),
944                        identifier!(ctx, __ge__),
945                    ];
946
947                    let has_python_cmp = {
948                        // Check self first
949                        let attrs = self.attributes.read();
950                        let in_self = cmp_names.iter().any(|n| attrs.contains_key(*n));
951                        drop(attrs);
952
953                        // mro[0] is self, so skip it since we already checked self above
954                        in_self
955                            || self.mro.read()[1..].iter().any(|cls| {
956                                let attrs = cls.attributes.read();
957                                cmp_names.iter().any(|n| {
958                                    if let Some(attr) = attrs.get(*n) {
959                                        // Check if it's a Python function (not a native descriptor)
960                                        !attr.class().is(ctx.types.wrapper_descriptor_type)
961                                            && !attr.class().is(ctx.types.method_descriptor_type)
962                                    } else {
963                                        false
964                                    }
965                                })
966                            })
967                    };
968
969                    if has_python_cmp {
970                        // Use wrapper to call the Python method
971                        self.slots.richcompare.store(Some(richcompare_wrapper));
972                    } else {
973                        match self.lookup_slot_in_mro(name, ctx, |sf| {
974                            if let SlotFunc::RichCompare(f, _) = sf {
975                                Some(*f)
976                            } else {
977                                None
978                            }
979                        }) {
980                            SlotLookupResult::NativeSlot(func) => {
981                                self.slots.richcompare.store(Some(func));
982                            }
983                            SlotLookupResult::PythonMethod => {
984                                self.slots.richcompare.store(Some(richcompare_wrapper));
985                            }
986                            SlotLookupResult::NotFound => {
987                                accessor.inherit_from_mro(self);
988                            }
989                        }
990                    }
991                } else {
992                    accessor.inherit_from_mro(self);
993                }
994            }
995
996            // === Number binary operations ===
997            SlotAccessor::NbAdd => {
998                if name.as_str() == "__radd__" {
999                    update_sub_slot!(
1000                        as_number,
1001                        right_add,
1002                        number_binary_right_op_wrapper!(__radd__),
1003                        NumBinary
1004                    )
1005                } else {
1006                    update_sub_slot!(
1007                        as_number,
1008                        add,
1009                        number_binary_op_wrapper!(__add__),
1010                        NumBinary
1011                    )
1012                }
1013            }
1014            SlotAccessor::NbInplaceAdd => {
1015                update_sub_slot!(
1016                    as_number,
1017                    inplace_add,
1018                    number_binary_op_wrapper!(__iadd__),
1019                    NumBinary
1020                )
1021            }
1022            SlotAccessor::NbSubtract => {
1023                if name.as_str() == "__rsub__" {
1024                    update_sub_slot!(
1025                        as_number,
1026                        right_subtract,
1027                        number_binary_right_op_wrapper!(__rsub__),
1028                        NumBinary
1029                    )
1030                } else {
1031                    update_sub_slot!(
1032                        as_number,
1033                        subtract,
1034                        number_binary_op_wrapper!(__sub__),
1035                        NumBinary
1036                    )
1037                }
1038            }
1039            SlotAccessor::NbInplaceSubtract => {
1040                update_sub_slot!(
1041                    as_number,
1042                    inplace_subtract,
1043                    number_binary_op_wrapper!(__isub__),
1044                    NumBinary
1045                )
1046            }
1047            SlotAccessor::NbMultiply => {
1048                if name.as_str() == "__rmul__" {
1049                    update_sub_slot!(
1050                        as_number,
1051                        right_multiply,
1052                        number_binary_right_op_wrapper!(__rmul__),
1053                        NumBinary
1054                    )
1055                } else {
1056                    update_sub_slot!(
1057                        as_number,
1058                        multiply,
1059                        number_binary_op_wrapper!(__mul__),
1060                        NumBinary
1061                    )
1062                }
1063            }
1064            SlotAccessor::NbInplaceMultiply => {
1065                update_sub_slot!(
1066                    as_number,
1067                    inplace_multiply,
1068                    number_binary_op_wrapper!(__imul__),
1069                    NumBinary
1070                )
1071            }
1072            SlotAccessor::NbRemainder => {
1073                if name.as_str() == "__rmod__" {
1074                    update_sub_slot!(
1075                        as_number,
1076                        right_remainder,
1077                        number_binary_right_op_wrapper!(__rmod__),
1078                        NumBinary
1079                    )
1080                } else {
1081                    update_sub_slot!(
1082                        as_number,
1083                        remainder,
1084                        number_binary_op_wrapper!(__mod__),
1085                        NumBinary
1086                    )
1087                }
1088            }
1089            SlotAccessor::NbInplaceRemainder => {
1090                update_sub_slot!(
1091                    as_number,
1092                    inplace_remainder,
1093                    number_binary_op_wrapper!(__imod__),
1094                    NumBinary
1095                )
1096            }
1097            SlotAccessor::NbDivmod => {
1098                if name.as_str() == "__rdivmod__" {
1099                    update_sub_slot!(
1100                        as_number,
1101                        right_divmod,
1102                        number_binary_right_op_wrapper!(__rdivmod__),
1103                        NumBinary
1104                    )
1105                } else {
1106                    update_sub_slot!(
1107                        as_number,
1108                        divmod,
1109                        number_binary_op_wrapper!(__divmod__),
1110                        NumBinary
1111                    )
1112                }
1113            }
1114            SlotAccessor::NbPower => {
1115                if name.as_str() == "__rpow__" {
1116                    update_sub_slot!(
1117                        as_number,
1118                        right_power,
1119                        number_ternary_right_op_wrapper!(__rpow__),
1120                        NumTernary
1121                    )
1122                } else {
1123                    update_sub_slot!(
1124                        as_number,
1125                        power,
1126                        number_ternary_op_wrapper!(__pow__),
1127                        NumTernary
1128                    )
1129                }
1130            }
1131            SlotAccessor::NbInplacePower => {
1132                update_sub_slot!(
1133                    as_number,
1134                    inplace_power,
1135                    number_ternary_op_wrapper!(__ipow__),
1136                    NumTernary
1137                )
1138            }
1139            SlotAccessor::NbFloorDivide => {
1140                if name.as_str() == "__rfloordiv__" {
1141                    update_sub_slot!(
1142                        as_number,
1143                        right_floor_divide,
1144                        number_binary_right_op_wrapper!(__rfloordiv__),
1145                        NumBinary
1146                    )
1147                } else {
1148                    update_sub_slot!(
1149                        as_number,
1150                        floor_divide,
1151                        number_binary_op_wrapper!(__floordiv__),
1152                        NumBinary
1153                    )
1154                }
1155            }
1156            SlotAccessor::NbInplaceFloorDivide => {
1157                update_sub_slot!(
1158                    as_number,
1159                    inplace_floor_divide,
1160                    number_binary_op_wrapper!(__ifloordiv__),
1161                    NumBinary
1162                )
1163            }
1164            SlotAccessor::NbTrueDivide => {
1165                if name.as_str() == "__rtruediv__" {
1166                    update_sub_slot!(
1167                        as_number,
1168                        right_true_divide,
1169                        number_binary_right_op_wrapper!(__rtruediv__),
1170                        NumBinary
1171                    )
1172                } else {
1173                    update_sub_slot!(
1174                        as_number,
1175                        true_divide,
1176                        number_binary_op_wrapper!(__truediv__),
1177                        NumBinary
1178                    )
1179                }
1180            }
1181            SlotAccessor::NbInplaceTrueDivide => {
1182                update_sub_slot!(
1183                    as_number,
1184                    inplace_true_divide,
1185                    number_binary_op_wrapper!(__itruediv__),
1186                    NumBinary
1187                )
1188            }
1189            SlotAccessor::NbMatrixMultiply => {
1190                if name.as_str() == "__rmatmul__" {
1191                    update_sub_slot!(
1192                        as_number,
1193                        right_matrix_multiply,
1194                        number_binary_right_op_wrapper!(__rmatmul__),
1195                        NumBinary
1196                    )
1197                } else {
1198                    update_sub_slot!(
1199                        as_number,
1200                        matrix_multiply,
1201                        number_binary_op_wrapper!(__matmul__),
1202                        NumBinary
1203                    )
1204                }
1205            }
1206            SlotAccessor::NbInplaceMatrixMultiply => {
1207                update_sub_slot!(
1208                    as_number,
1209                    inplace_matrix_multiply,
1210                    number_binary_op_wrapper!(__imatmul__),
1211                    NumBinary
1212                )
1213            }
1214
1215            // === Number bitwise operations ===
1216            SlotAccessor::NbLshift => {
1217                if name.as_str() == "__rlshift__" {
1218                    update_sub_slot!(
1219                        as_number,
1220                        right_lshift,
1221                        number_binary_right_op_wrapper!(__rlshift__),
1222                        NumBinary
1223                    )
1224                } else {
1225                    update_sub_slot!(
1226                        as_number,
1227                        lshift,
1228                        number_binary_op_wrapper!(__lshift__),
1229                        NumBinary
1230                    )
1231                }
1232            }
1233            SlotAccessor::NbInplaceLshift => {
1234                update_sub_slot!(
1235                    as_number,
1236                    inplace_lshift,
1237                    number_binary_op_wrapper!(__ilshift__),
1238                    NumBinary
1239                )
1240            }
1241            SlotAccessor::NbRshift => {
1242                if name.as_str() == "__rrshift__" {
1243                    update_sub_slot!(
1244                        as_number,
1245                        right_rshift,
1246                        number_binary_right_op_wrapper!(__rrshift__),
1247                        NumBinary
1248                    )
1249                } else {
1250                    update_sub_slot!(
1251                        as_number,
1252                        rshift,
1253                        number_binary_op_wrapper!(__rshift__),
1254                        NumBinary
1255                    )
1256                }
1257            }
1258            SlotAccessor::NbInplaceRshift => {
1259                update_sub_slot!(
1260                    as_number,
1261                    inplace_rshift,
1262                    number_binary_op_wrapper!(__irshift__),
1263                    NumBinary
1264                )
1265            }
1266            SlotAccessor::NbAnd => {
1267                if name.as_str() == "__rand__" {
1268                    update_sub_slot!(
1269                        as_number,
1270                        right_and,
1271                        number_binary_right_op_wrapper!(__rand__),
1272                        NumBinary
1273                    )
1274                } else {
1275                    update_sub_slot!(
1276                        as_number,
1277                        and,
1278                        number_binary_op_wrapper!(__and__),
1279                        NumBinary
1280                    )
1281                }
1282            }
1283            SlotAccessor::NbInplaceAnd => {
1284                update_sub_slot!(
1285                    as_number,
1286                    inplace_and,
1287                    number_binary_op_wrapper!(__iand__),
1288                    NumBinary
1289                )
1290            }
1291            SlotAccessor::NbXor => {
1292                if name.as_str() == "__rxor__" {
1293                    update_sub_slot!(
1294                        as_number,
1295                        right_xor,
1296                        number_binary_right_op_wrapper!(__rxor__),
1297                        NumBinary
1298                    )
1299                } else {
1300                    update_sub_slot!(
1301                        as_number,
1302                        xor,
1303                        number_binary_op_wrapper!(__xor__),
1304                        NumBinary
1305                    )
1306                }
1307            }
1308            SlotAccessor::NbInplaceXor => {
1309                update_sub_slot!(
1310                    as_number,
1311                    inplace_xor,
1312                    number_binary_op_wrapper!(__ixor__),
1313                    NumBinary
1314                )
1315            }
1316            SlotAccessor::NbOr => {
1317                if name.as_str() == "__ror__" {
1318                    update_sub_slot!(
1319                        as_number,
1320                        right_or,
1321                        number_binary_right_op_wrapper!(__ror__),
1322                        NumBinary
1323                    )
1324                } else {
1325                    update_sub_slot!(as_number, or, number_binary_op_wrapper!(__or__), NumBinary)
1326                }
1327            }
1328            SlotAccessor::NbInplaceOr => {
1329                update_sub_slot!(
1330                    as_number,
1331                    inplace_or,
1332                    number_binary_op_wrapper!(__ior__),
1333                    NumBinary
1334                )
1335            }
1336
1337            // === Number unary operations ===
1338            SlotAccessor::NbNegative => {
1339                update_sub_slot!(
1340                    as_number,
1341                    negative,
1342                    number_unary_op_wrapper!(__neg__),
1343                    NumUnary
1344                )
1345            }
1346            SlotAccessor::NbPositive => {
1347                update_sub_slot!(
1348                    as_number,
1349                    positive,
1350                    number_unary_op_wrapper!(__pos__),
1351                    NumUnary
1352                )
1353            }
1354            SlotAccessor::NbAbsolute => {
1355                update_sub_slot!(
1356                    as_number,
1357                    absolute,
1358                    number_unary_op_wrapper!(__abs__),
1359                    NumUnary
1360                )
1361            }
1362            SlotAccessor::NbInvert => {
1363                update_sub_slot!(
1364                    as_number,
1365                    invert,
1366                    number_unary_op_wrapper!(__invert__),
1367                    NumUnary
1368                )
1369            }
1370            SlotAccessor::NbBool => {
1371                update_sub_slot!(as_number, boolean, bool_wrapper, NumBoolean)
1372            }
1373            SlotAccessor::NbInt => {
1374                update_sub_slot!(as_number, int, number_unary_op_wrapper!(__int__), NumUnary)
1375            }
1376            SlotAccessor::NbFloat => {
1377                update_sub_slot!(
1378                    as_number,
1379                    float,
1380                    number_unary_op_wrapper!(__float__),
1381                    NumUnary
1382                )
1383            }
1384            SlotAccessor::NbIndex => {
1385                update_sub_slot!(
1386                    as_number,
1387                    index,
1388                    number_unary_op_wrapper!(__index__),
1389                    NumUnary
1390                )
1391            }
1392
1393            // === Sequence slots ===
1394            SlotAccessor::SqLength => {
1395                update_sub_slot!(as_sequence, length, sequence_len_wrapper, SeqLength)
1396            }
1397            SlotAccessor::SqConcat | SlotAccessor::SqInplaceConcat => {
1398                // Sequence concat uses sq_concat slot - no generic wrapper needed
1399                // (handled by number protocol fallback)
1400                if !ADD {
1401                    accessor.inherit_from_mro(self);
1402                }
1403            }
1404            SlotAccessor::SqRepeat => {
1405                update_sub_slot!(as_sequence, repeat, sequence_repeat_wrapper, SeqRepeat)
1406            }
1407            SlotAccessor::SqInplaceRepeat => {
1408                update_sub_slot!(
1409                    as_sequence,
1410                    inplace_repeat,
1411                    sequence_inplace_repeat_wrapper,
1412                    SeqRepeat
1413                )
1414            }
1415            SlotAccessor::SqItem => {
1416                update_sub_slot!(as_sequence, item, sequence_getitem_wrapper, SeqItem)
1417            }
1418            SlotAccessor::SqAssItem => {
1419                // SqAssItem is shared by __setitem__ (SeqSetItem) and __delitem__ (SeqDelItem)
1420                if ADD {
1421                    let has_own = {
1422                        let guard = self.attributes.read();
1423                        let setitem = ctx.intern_str("__setitem__");
1424                        let delitem = ctx.intern_str("__delitem__");
1425                        guard.contains_key(setitem) || guard.contains_key(delitem)
1426                    };
1427                    if has_own {
1428                        self.slots
1429                            .as_sequence
1430                            .ass_item
1431                            .store(Some(sequence_setitem_wrapper));
1432                    } else {
1433                        match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
1434                            SlotFunc::SeqSetItem(f) | SlotFunc::SeqDelItem(f) => Some(*f),
1435                            _ => None,
1436                        }) {
1437                            SlotLookupResult::NativeSlot(func) => {
1438                                self.slots.as_sequence.ass_item.store(Some(func));
1439                            }
1440                            SlotLookupResult::PythonMethod => {
1441                                self.slots
1442                                    .as_sequence
1443                                    .ass_item
1444                                    .store(Some(sequence_setitem_wrapper));
1445                            }
1446                            SlotLookupResult::NotFound => {
1447                                accessor.inherit_from_mro(self);
1448                            }
1449                        }
1450                    }
1451                } else {
1452                    accessor.inherit_from_mro(self);
1453                }
1454            }
1455            SlotAccessor::SqContains => {
1456                update_sub_slot!(
1457                    as_sequence,
1458                    contains,
1459                    sequence_contains_wrapper,
1460                    SeqContains
1461                )
1462            }
1463
1464            // === Mapping slots ===
1465            SlotAccessor::MpLength => {
1466                update_sub_slot!(as_mapping, length, mapping_len_wrapper, MapLength)
1467            }
1468            SlotAccessor::MpSubscript => {
1469                update_sub_slot!(as_mapping, subscript, mapping_getitem_wrapper, MapSubscript)
1470            }
1471            SlotAccessor::MpAssSubscript => {
1472                // MpAssSubscript is shared by __setitem__ (MapSetSubscript) and __delitem__ (MapDelSubscript)
1473                if ADD {
1474                    let has_own = {
1475                        let guard = self.attributes.read();
1476                        let setitem = ctx.intern_str("__setitem__");
1477                        let delitem = ctx.intern_str("__delitem__");
1478                        guard.contains_key(setitem) || guard.contains_key(delitem)
1479                    };
1480                    if has_own {
1481                        self.slots
1482                            .as_mapping
1483                            .ass_subscript
1484                            .store(Some(mapping_setitem_wrapper));
1485                    } else {
1486                        match self.lookup_slot_in_mro(name, ctx, |sf| match sf {
1487                            SlotFunc::MapSetSubscript(f) | SlotFunc::MapDelSubscript(f) => Some(*f),
1488                            _ => None,
1489                        }) {
1490                            SlotLookupResult::NativeSlot(func) => {
1491                                self.slots.as_mapping.ass_subscript.store(Some(func));
1492                            }
1493                            SlotLookupResult::PythonMethod => {
1494                                self.slots
1495                                    .as_mapping
1496                                    .ass_subscript
1497                                    .store(Some(mapping_setitem_wrapper));
1498                            }
1499                            SlotLookupResult::NotFound => {
1500                                accessor.inherit_from_mro(self);
1501                            }
1502                        }
1503                    }
1504                } else {
1505                    accessor.inherit_from_mro(self);
1506                }
1507            }
1508
1509            // Reserved slots - no-op
1510            _ => {}
1511        }
1512    }
1513
1514    /// Look up a method in MRO and extract the slot function if it's a slot wrapper.
1515    fn lookup_slot_in_mro<T: Copy>(
1516        &self,
1517        name: &'static PyStrInterned,
1518        ctx: &Context,
1519        extract: impl Fn(&crate::builtins::descriptor::SlotFunc) -> Option<T>,
1520    ) -> SlotLookupResult<T> {
1521        use crate::builtins::descriptor::PyWrapper;
1522
1523        // Helper to check if a class is a subclass of another by checking MRO
1524        let is_subclass_of = |subclass_mro: &[PyRef<PyType>], superclass: &Py<PyType>| -> bool {
1525            subclass_mro.iter().any(|c| c.is(superclass))
1526        };
1527
1528        // Helper to extract slot from an attribute if it's a wrapper descriptor
1529        // and the wrapper's type is compatible with the given class.
1530        // bpo-37619: wrapper descriptor from wrong class should not be used directly.
1531        let try_extract = |attr: &PyObjectRef, for_class_mro: &[PyRef<PyType>]| -> Option<T> {
1532            if attr.class().is(ctx.types.wrapper_descriptor_type) {
1533                attr.downcast_ref::<PyWrapper>().and_then(|wrapper| {
1534                    // Only extract slot if for_class is a subclass of wrapper.typ
1535                    if is_subclass_of(for_class_mro, wrapper.typ) {
1536                        extract(&wrapper.wrapped)
1537                    } else {
1538                        None
1539                    }
1540                })
1541            } else {
1542                None
1543            }
1544        };
1545
1546        let mro = self.mro.read();
1547
1548        // Look up in self's dict first
1549        if let Some(attr) = self.attributes.read().get(name).cloned() {
1550            if let Some(func) = try_extract(&attr, &mro) {
1551                return SlotLookupResult::NativeSlot(func);
1552            }
1553            return SlotLookupResult::PythonMethod;
1554        }
1555
1556        // Look up in MRO (mro[0] is self, so skip it)
1557        for (i, cls) in mro[1..].iter().enumerate() {
1558            if let Some(attr) = cls.attributes.read().get(name).cloned() {
1559                // Use the slice starting from this class in MRO
1560                if let Some(func) = try_extract(&attr, &mro[i + 1..]) {
1561                    return SlotLookupResult::NativeSlot(func);
1562                }
1563                return SlotLookupResult::PythonMethod;
1564            }
1565        }
1566        // No method found in MRO
1567        SlotLookupResult::NotFound
1568    }
1569}
1570
1571/// Trait for types that can be constructed via Python's `__new__` method.
1572///
1573/// `slot_new` corresponds to the `__new__` type slot.
1574///
1575/// In most cases, `__new__` simply initializes the payload and assigns a type,
1576/// so you only need to override `py_new`. The default `slot_new` implementation
1577/// will call `py_new` and then wrap the result with `into_ref_with_type`.
1578///
1579/// However, if a subtype requires more than just payload initialization
1580/// (e.g., returning an existing object for optimization, setting attributes
1581/// after creation, or special handling of the class type), you should override
1582/// `slot_new` directly instead of `py_new`.
1583///
1584/// # When to use `py_new` only (most common case):
1585/// - Simple payload initialization that just creates `Self`
1586/// - The type doesn't need special handling for subtypes
1587///
1588/// # When to override `slot_new`:
1589/// - Returning existing objects (e.g., `PyInt`, `PyStr`, `PyBool` for optimization)
1590/// - Setting attributes or dict entries after object creation
1591/// - Special class type handling (e.g., `PyType` and its metaclasses)
1592/// - Post-creation mutations that require `PyRef`
1593#[pyclass]
1594pub trait Constructor: PyPayload + core::fmt::Debug {
1595    type Args: FromArgs;
1596
1597    /// The type slot for `__new__`. Override this only when you need special
1598    /// behavior beyond simple payload creation.
1599    #[inline]
1600    #[pyslot]
1601    fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1602        let args: Self::Args = args.bind(vm)?;
1603        let payload = Self::py_new(&cls, args, vm)?;
1604        payload.into_ref_with_type(vm, cls).map(Into::into)
1605    }
1606
1607    /// Creates the payload for this type. In most cases, just implement this method
1608    /// and let the default `slot_new` handle wrapping with the correct type.
1609    fn py_new(cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self>;
1610}
1611
1612pub trait DefaultConstructor: PyPayload + Default + core::fmt::Debug {
1613    fn construct_and_init(args: Self::Args, vm: &VirtualMachine) -> PyResult<PyRef<Self>>
1614    where
1615        Self: Initializer,
1616    {
1617        let this = Self::default().into_ref(&vm.ctx);
1618        Self::init(this.clone(), args, vm)?;
1619        Ok(this)
1620    }
1621}
1622
1623impl<T> Constructor for T
1624where
1625    T: DefaultConstructor,
1626{
1627    type Args = FuncArgs;
1628
1629    fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1630        Self::default().into_ref_with_type(vm, cls).map(Into::into)
1631    }
1632
1633    fn py_new(cls: &Py<PyType>, _args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
1634        Err(vm.new_type_error(format!("cannot create {} instances", cls.slot_name())))
1635    }
1636}
1637
1638#[pyclass]
1639pub trait Initializer: PyPayload {
1640    type Args: FromArgs;
1641
1642    #[inline]
1643    #[pyslot]
1644    fn slot_init(zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
1645        #[cfg(debug_assertions)]
1646        let class_name_for_debug = zelf.class().name().to_string();
1647
1648        let zelf = match zelf.try_into_value(vm) {
1649            Ok(zelf) => zelf,
1650            Err(err) => {
1651                #[cfg(debug_assertions)]
1652                {
1653                    if let Ok(msg) = err.as_object().repr(vm) {
1654                        let double_appearance = msg
1655                            .to_string_lossy()
1656                            .matches(&class_name_for_debug as &str)
1657                            .count()
1658                            == 2;
1659                        if double_appearance {
1660                            panic!(
1661                                "This type `{}` doesn't seem to support `init`. Override `slot_init` instead: {}",
1662                                class_name_for_debug, msg
1663                            );
1664                        }
1665                    }
1666                }
1667                return Err(err);
1668            }
1669        };
1670        let args: Self::Args = args.bind(vm)?;
1671        Self::init(zelf, args, vm)
1672    }
1673
1674    fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()>;
1675}
1676
1677#[pyclass]
1678pub trait Destructor: PyPayload {
1679    #[inline] // for __del__
1680    #[pyslot]
1681    fn slot_del(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
1682        let zelf = zelf
1683            .downcast_ref()
1684            .ok_or_else(|| vm.new_type_error("unexpected payload for __del__"))?;
1685        Self::del(zelf, vm)
1686    }
1687
1688    fn del(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<()>;
1689}
1690
1691#[pyclass]
1692pub trait Callable: PyPayload {
1693    type Args: FromArgs;
1694
1695    #[inline]
1696    #[pyslot]
1697    fn slot_call(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1698        let zelf = zelf.downcast_ref().ok_or_else(|| {
1699            let repr = zelf.repr(vm);
1700            let help: Wtf8Buf = if let Ok(repr) = repr.as_ref() {
1701                repr.as_wtf8().to_owned()
1702            } else {
1703                zelf.class().name().to_owned().into()
1704            };
1705            let mut msg = Wtf8Buf::from("unexpected payload for __call__ of ");
1706            msg.push_wtf8(&help);
1707            vm.new_type_error(msg)
1708        })?;
1709        let args = args.bind(vm)?;
1710        Self::call(zelf, args, vm)
1711    }
1712
1713    fn call(zelf: &Py<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult;
1714}
1715
1716#[pyclass]
1717pub trait GetDescriptor: PyPayload {
1718    #[pyslot]
1719    fn descr_get(
1720        zelf: PyObjectRef,
1721        obj: Option<PyObjectRef>,
1722        cls: Option<PyObjectRef>,
1723        vm: &VirtualMachine,
1724    ) -> PyResult;
1725
1726    #[inline]
1727    fn _as_pyref<'a>(zelf: &'a PyObject, vm: &VirtualMachine) -> PyResult<&'a Py<Self>> {
1728        zelf.try_to_value(vm)
1729    }
1730
1731    #[inline]
1732    fn _unwrap<'a>(
1733        zelf: &'a PyObject,
1734        obj: Option<PyObjectRef>,
1735        vm: &VirtualMachine,
1736    ) -> PyResult<(&'a Py<Self>, PyObjectRef)> {
1737        let zelf = Self::_as_pyref(zelf, vm)?;
1738        let obj = vm.unwrap_or_none(obj);
1739        Ok((zelf, obj))
1740    }
1741
1742    #[inline]
1743    fn _check<'a>(
1744        zelf: &'a PyObject,
1745        obj: Option<PyObjectRef>,
1746        vm: &VirtualMachine,
1747    ) -> Option<(&'a Py<Self>, PyObjectRef)> {
1748        // CPython descr_check
1749        let obj = obj?;
1750        // if (!PyObject_TypeCheck(obj, descr->d_type)) {
1751        //     PyErr_Format(PyExc_TypeError,
1752        //                  "descriptor '%V' for '%.100s' objects "
1753        //                  "doesn't apply to a '%.100s' object",
1754        //                  descr_name((PyDescrObject *)descr), "?",
1755        //                  descr->d_type->slot_name,
1756        //                  obj->ob_type->slot_name);
1757        //     *pres = NULL;
1758        //     return 1;
1759        // } else {
1760        Some((Self::_as_pyref(zelf, vm).unwrap(), obj))
1761    }
1762
1763    #[inline]
1764    fn _cls_is(cls: &Option<PyObjectRef>, other: &impl Borrow<PyObject>) -> bool {
1765        cls.as_ref().is_some_and(|cls| other.borrow().is(cls))
1766    }
1767}
1768
1769#[pyclass]
1770pub trait Hashable: PyPayload {
1771    #[inline]
1772    #[pyslot]
1773    fn slot_hash(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
1774        let zelf = zelf
1775            .downcast_ref()
1776            .ok_or_else(|| vm.new_type_error("unexpected payload for __hash__"))?;
1777        Self::hash(zelf, vm)
1778    }
1779
1780    fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash>;
1781}
1782
1783#[pyclass]
1784pub trait Representable: PyPayload {
1785    #[inline]
1786    #[pyslot]
1787    fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
1788        let zelf = zelf
1789            .downcast_ref()
1790            .ok_or_else(|| vm.new_type_error("unexpected payload for __repr__"))?;
1791        Self::repr(zelf, vm)
1792    }
1793
1794    #[inline]
1795    fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
1796        let repr = Self::repr_wtf8(zelf, vm)?;
1797        Ok(vm.ctx.new_str(repr))
1798    }
1799
1800    fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
1801        Self::repr_str(zelf, vm).map(|utf8| utf8.into())
1802    }
1803    fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1804        unreachable!("Representable requires overriding either repr_str or repr_wtf8")
1805    }
1806}
1807
1808#[pyclass]
1809pub trait Comparable: PyPayload {
1810    #[inline]
1811    #[pyslot]
1812    fn slot_richcompare(
1813        zelf: &PyObject,
1814        other: &PyObject,
1815        op: PyComparisonOp,
1816        vm: &VirtualMachine,
1817    ) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
1818        let zelf = zelf.downcast_ref().ok_or_else(|| {
1819            vm.new_type_error(format!(
1820                "unexpected payload for {}",
1821                op.method_name(&vm.ctx).as_str()
1822            ))
1823        })?;
1824        Self::cmp(zelf, other, op, vm).map(Either::B)
1825    }
1826
1827    fn cmp(
1828        zelf: &Py<Self>,
1829        other: &PyObject,
1830        op: PyComparisonOp,
1831        vm: &VirtualMachine,
1832    ) -> PyResult<PyComparisonValue>;
1833}
1834
1835#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1836#[repr(transparent)]
1837pub struct PyComparisonOp(ComparisonOperator);
1838
1839impl From<ComparisonOperator> for PyComparisonOp {
1840    fn from(op: ComparisonOperator) -> Self {
1841        Self(op)
1842    }
1843}
1844
1845#[allow(non_upper_case_globals)]
1846impl PyComparisonOp {
1847    pub const Lt: Self = Self(ComparisonOperator::Less);
1848    pub const Gt: Self = Self(ComparisonOperator::Greater);
1849    pub const Ne: Self = Self(ComparisonOperator::NotEqual);
1850    pub const Eq: Self = Self(ComparisonOperator::Equal);
1851    pub const Le: Self = Self(ComparisonOperator::LessOrEqual);
1852    pub const Ge: Self = Self(ComparisonOperator::GreaterOrEqual);
1853}
1854
1855impl PyComparisonOp {
1856    pub fn eq_only(
1857        self,
1858        f: impl FnOnce() -> PyResult<PyComparisonValue>,
1859    ) -> PyResult<PyComparisonValue> {
1860        match self {
1861            Self::Eq => f(),
1862            Self::Ne => f().map(|x| x.map(|eq| !eq)),
1863            _ => Ok(PyComparisonValue::NotImplemented),
1864        }
1865    }
1866
1867    pub fn eval_ord(self, ord: Ordering) -> bool {
1868        match self {
1869            Self::Lt => ord == Ordering::Less,
1870            Self::Le => ord != Ordering::Greater,
1871            Self::Eq => ord == Ordering::Equal,
1872            Self::Ne => ord != Ordering::Equal,
1873            Self::Gt => ord == Ordering::Greater,
1874            Self::Ge => ord != Ordering::Less,
1875        }
1876    }
1877
1878    pub const fn swapped(self) -> Self {
1879        match self {
1880            Self::Lt => Self::Gt,
1881            Self::Le => Self::Ge,
1882            Self::Eq => Self::Eq,
1883            Self::Ne => Self::Ne,
1884            Self::Ge => Self::Le,
1885            Self::Gt => Self::Lt,
1886        }
1887    }
1888
1889    pub fn method_name(self, ctx: &Context) -> &'static PyStrInterned {
1890        match self {
1891            Self::Lt => identifier!(ctx, __lt__),
1892            Self::Le => identifier!(ctx, __le__),
1893            Self::Eq => identifier!(ctx, __eq__),
1894            Self::Ne => identifier!(ctx, __ne__),
1895            Self::Ge => identifier!(ctx, __ge__),
1896            Self::Gt => identifier!(ctx, __gt__),
1897        }
1898    }
1899
1900    pub const fn operator_token(self) -> &'static str {
1901        match self {
1902            Self::Lt => "<",
1903            Self::Le => "<=",
1904            Self::Eq => "==",
1905            Self::Ne => "!=",
1906            Self::Ge => ">=",
1907            Self::Gt => ">",
1908        }
1909    }
1910
1911    /// Returns an appropriate return value for the comparison when a and b are the same object, if an
1912    /// appropriate return value exists.
1913    #[inline]
1914    pub fn identical_optimization(
1915        self,
1916        a: &impl Borrow<PyObject>,
1917        b: &impl Borrow<PyObject>,
1918    ) -> Option<bool> {
1919        self.map_eq(|| a.borrow().is(b.borrow()))
1920    }
1921
1922    /// Returns `Some(true)` when self is `Eq` and `f()` returns true. Returns `Some(false)` when self
1923    /// is `Ne` and `f()` returns true. Otherwise returns `None`.
1924    #[inline]
1925    pub fn map_eq(self, f: impl FnOnce() -> bool) -> Option<bool> {
1926        let eq = match self {
1927            Self::Eq => true,
1928            Self::Ne => false,
1929            _ => return None,
1930        };
1931        f().then_some(eq)
1932    }
1933}
1934
1935#[pyclass]
1936pub trait GetAttr: PyPayload {
1937    #[pyslot]
1938    fn slot_getattro(obj: &PyObject, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
1939        let zelf = obj
1940            .downcast_ref()
1941            .ok_or_else(|| vm.new_type_error("unexpected payload for __getattribute__"))?;
1942        Self::getattro(zelf, name, vm)
1943    }
1944
1945    fn getattro(zelf: &Py<Self>, name: &Py<PyStr>, vm: &VirtualMachine) -> PyResult;
1946}
1947
1948#[pyclass]
1949pub trait SetAttr: PyPayload {
1950    #[pyslot]
1951    #[inline]
1952    fn slot_setattro(
1953        obj: &PyObject,
1954        name: &Py<PyStr>,
1955        value: PySetterValue,
1956        vm: &VirtualMachine,
1957    ) -> PyResult<()> {
1958        let zelf = obj
1959            .downcast_ref::<Self>()
1960            .ok_or_else(|| vm.new_type_error("unexpected payload for __setattr__"))?;
1961        Self::setattro(zelf, name, value, vm)
1962    }
1963
1964    fn setattro(
1965        zelf: &Py<Self>,
1966        name: &Py<PyStr>,
1967        value: PySetterValue,
1968        vm: &VirtualMachine,
1969    ) -> PyResult<()>;
1970}
1971
1972#[pyclass]
1973pub trait AsBuffer: PyPayload {
1974    // TODO: `flags` parameter
1975    #[inline]
1976    #[pyslot]
1977    fn slot_as_buffer(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyBuffer> {
1978        let zelf = zelf
1979            .downcast_ref()
1980            .ok_or_else(|| vm.new_type_error("unexpected payload for as_buffer"))?;
1981        Self::as_buffer(zelf, vm)
1982    }
1983
1984    fn as_buffer(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyBuffer>;
1985}
1986
1987#[pyclass]
1988pub trait AsMapping: PyPayload {
1989    fn as_mapping() -> &'static PyMappingMethods;
1990
1991    #[inline]
1992    fn mapping_downcast(mapping: PyMapping<'_>) -> &Py<Self> {
1993        unsafe { mapping.obj.downcast_unchecked_ref() }
1994    }
1995
1996    fn extend_slots(slots: &mut PyTypeSlots) {
1997        slots.as_mapping.copy_from(Self::as_mapping());
1998    }
1999}
2000
2001#[pyclass]
2002pub trait AsSequence: PyPayload {
2003    fn as_sequence() -> &'static PySequenceMethods;
2004
2005    #[inline]
2006    fn sequence_downcast(seq: PySequence<'_>) -> &Py<Self> {
2007        unsafe { seq.obj.downcast_unchecked_ref() }
2008    }
2009
2010    fn extend_slots(slots: &mut PyTypeSlots) {
2011        slots.as_sequence.copy_from(Self::as_sequence());
2012    }
2013}
2014
2015#[pyclass]
2016pub trait AsNumber: PyPayload {
2017    #[pyslot]
2018    fn as_number() -> &'static PyNumberMethods;
2019
2020    fn extend_slots(slots: &mut PyTypeSlots) {
2021        slots.as_number.copy_from(Self::as_number());
2022    }
2023
2024    fn clone_exact(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyRef<Self> {
2025        // not all AsNumber requires this implementation.
2026        unimplemented!()
2027    }
2028
2029    #[inline]
2030    fn number_downcast(num: PyNumber<'_>) -> &Py<Self> {
2031        unsafe { num.obj.downcast_unchecked_ref() }
2032    }
2033
2034    #[inline]
2035    fn number_downcast_exact(num: PyNumber<'_>, vm: &VirtualMachine) -> PyRef<Self> {
2036        if let Some(zelf) = num.downcast_ref_if_exact::<Self>(vm) {
2037            zelf.to_owned()
2038        } else {
2039            Self::clone_exact(Self::number_downcast(num), vm)
2040        }
2041    }
2042}
2043
2044#[pyclass]
2045pub trait Iterable: PyPayload {
2046    #[pyslot]
2047    fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
2048        let zelf = zelf
2049            .downcast()
2050            .map_err(|_| vm.new_type_error("unexpected payload for __iter__"))?;
2051        Self::iter(zelf, vm)
2052    }
2053
2054    fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult;
2055
2056    fn extend_slots(_slots: &mut PyTypeSlots) {}
2057}
2058
2059// `Iterator` fits better, but to avoid confusion with rust core::iter::Iterator
2060#[pyclass(with(Iterable))]
2061pub trait IterNext: PyPayload + Iterable {
2062    #[pyslot]
2063    fn slot_iternext(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
2064        let zelf = zelf
2065            .downcast_ref()
2066            .ok_or_else(|| vm.new_type_error("unexpected payload for __next__"))?;
2067        Self::next(zelf, vm)
2068    }
2069
2070    fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn>;
2071}
2072
2073pub trait SelfIter: PyPayload {}
2074
2075impl<T> Iterable for T
2076where
2077    T: SelfIter,
2078{
2079    #[cold]
2080    fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
2081        let repr = zelf.repr(vm)?;
2082        unreachable!("slot must be overridden for {}", repr.as_wtf8());
2083    }
2084
2085    #[cold]
2086    fn iter(_zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult {
2087        unreachable!("slot_iter is implemented");
2088    }
2089
2090    fn extend_slots(slots: &mut PyTypeSlots) {
2091        let prev = slots.iter.swap(Some(self_iter));
2092        debug_assert!(prev.is_some()); // slot_iter would be set
2093    }
2094}