Skip to main content

rustpython_vm/protocol/
number.rs

1use core::ops::Deref;
2
3use crossbeam_utils::atomic::AtomicCell;
4
5use crate::{
6    AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject,
7    VirtualMachine,
8    builtins::{
9        PyBaseExceptionRef, PyByteArray, PyBytes, PyComplex, PyFloat, PyInt, PyIntRef, PyStr, int,
10    },
11    common::int::{BytesToIntError, bytes_to_int},
12    function::ArgBytesLike,
13    object::{Traverse, TraverseFn},
14    stdlib::_warnings,
15};
16
17pub type PyNumberUnaryFunc<R = PyObjectRef> = fn(PyNumber<'_>, &VirtualMachine) -> PyResult<R>;
18pub type PyNumberBinaryFunc = fn(&PyObject, &PyObject, &VirtualMachine) -> PyResult;
19pub type PyNumberTernaryFunc = fn(&PyObject, &PyObject, &PyObject, &VirtualMachine) -> PyResult;
20
21impl PyObject {
22    #[inline]
23    pub const fn number(&self) -> PyNumber<'_> {
24        PyNumber { obj: self }
25    }
26
27    pub fn try_index_opt(&self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
28        if let Some(i) = self.downcast_ref_if_exact::<PyInt>(vm) {
29            Some(Ok(i.to_owned()))
30        } else if let Some(i) = self.downcast_ref::<PyInt>() {
31            Some(Ok(vm.ctx.new_bigint(i.as_bigint())))
32        } else {
33            self.number().index(vm)
34        }
35    }
36
37    #[inline]
38    pub fn try_index(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
39        self.try_index_opt(vm).transpose()?.ok_or_else(|| {
40            vm.new_type_error(format!(
41                "'{}' object cannot be interpreted as an integer",
42                self.class()
43            ))
44        })
45    }
46
47    pub fn try_int(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
48        fn try_convert(obj: &PyObject, lit: &[u8], vm: &VirtualMachine) -> PyResult<PyIntRef> {
49            let base = 10;
50            let digit_limit = vm.state.int_max_str_digits.load();
51
52            let i = bytes_to_int(lit, base, digit_limit)
53                .map_err(|e| handle_bytes_to_int_err(e, obj, vm))?;
54            Ok(PyInt::from(i).into_ref(&vm.ctx))
55        }
56
57        if let Some(i) = self.downcast_ref_if_exact::<PyInt>(vm) {
58            Ok(i.to_owned())
59        } else if let Some(i) = self.number().int(vm).or_else(|| self.try_index_opt(vm)) {
60            i
61        } else if let Ok(Some(f)) = vm.get_special_method(self, identifier!(vm, __trunc__)) {
62            _warnings::warn(
63                vm.ctx.exceptions.deprecation_warning,
64                "The delegation of int() to __trunc__ is deprecated.".to_owned(),
65                1,
66                vm,
67            )?;
68            let ret = f.invoke((), vm)?;
69            ret.try_index(vm).map_err(|_| {
70                vm.new_type_error(format!(
71                    "__trunc__ returned non-Integral (type {})",
72                    ret.class()
73                ))
74            })
75        } else if let Some(s) = self.downcast_ref::<PyStr>() {
76            try_convert(self, s.as_wtf8().trim().as_bytes(), vm)
77        } else if let Some(bytes) = self.downcast_ref::<PyBytes>() {
78            try_convert(self, bytes, vm)
79        } else if let Some(bytearray) = self.downcast_ref::<PyByteArray>() {
80            try_convert(self, &bytearray.borrow_buf(), vm)
81        } else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self) {
82            // TODO: replace to PyBuffer
83            try_convert(self, &buffer.borrow_buf(), vm)
84        } else {
85            Err(vm.new_type_error(format!(
86                "int() argument must be a string, a bytes-like object or a real number, not '{}'",
87                self.class()
88            )))
89        }
90    }
91
92    pub fn try_float_opt(&self, vm: &VirtualMachine) -> Option<PyResult<PyRef<PyFloat>>> {
93        if let Some(float) = self.downcast_ref_if_exact::<PyFloat>(vm) {
94            Some(Ok(float.to_owned()))
95        } else if let Some(f) = self.number().float(vm) {
96            Some(f)
97        } else {
98            self.try_index_opt(vm)
99                .map(|i| Ok(vm.ctx.new_float(int::try_to_float(i?.as_bigint(), vm)?)))
100        }
101    }
102
103    #[inline]
104    pub fn try_float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> {
105        self.try_float_opt(vm).ok_or_else(|| {
106            vm.new_type_error(format!("must be real number, not {}", self.class()))
107        })?
108    }
109}
110
111#[derive(Default)]
112pub struct PyNumberMethods {
113    /* Number implementations must check *both*
114    arguments for proper type and implement the necessary conversions
115    in the slot functions themselves. */
116    pub add: Option<PyNumberBinaryFunc>,
117    pub subtract: Option<PyNumberBinaryFunc>,
118    pub multiply: Option<PyNumberBinaryFunc>,
119    pub remainder: Option<PyNumberBinaryFunc>,
120    pub divmod: Option<PyNumberBinaryFunc>,
121    pub power: Option<PyNumberTernaryFunc>,
122    pub negative: Option<PyNumberUnaryFunc>,
123    pub positive: Option<PyNumberUnaryFunc>,
124    pub absolute: Option<PyNumberUnaryFunc>,
125    pub boolean: Option<PyNumberUnaryFunc<bool>>,
126    pub invert: Option<PyNumberUnaryFunc>,
127    pub lshift: Option<PyNumberBinaryFunc>,
128    pub rshift: Option<PyNumberBinaryFunc>,
129    pub and: Option<PyNumberBinaryFunc>,
130    pub xor: Option<PyNumberBinaryFunc>,
131    pub or: Option<PyNumberBinaryFunc>,
132    pub int: Option<PyNumberUnaryFunc>,
133    pub float: Option<PyNumberUnaryFunc>,
134
135    pub inplace_add: Option<PyNumberBinaryFunc>,
136    pub inplace_subtract: Option<PyNumberBinaryFunc>,
137    pub inplace_multiply: Option<PyNumberBinaryFunc>,
138    pub inplace_remainder: Option<PyNumberBinaryFunc>,
139    pub inplace_power: Option<PyNumberTernaryFunc>,
140    pub inplace_lshift: Option<PyNumberBinaryFunc>,
141    pub inplace_rshift: Option<PyNumberBinaryFunc>,
142    pub inplace_and: Option<PyNumberBinaryFunc>,
143    pub inplace_xor: Option<PyNumberBinaryFunc>,
144    pub inplace_or: Option<PyNumberBinaryFunc>,
145
146    pub floor_divide: Option<PyNumberBinaryFunc>,
147    pub true_divide: Option<PyNumberBinaryFunc>,
148    pub inplace_floor_divide: Option<PyNumberBinaryFunc>,
149    pub inplace_true_divide: Option<PyNumberBinaryFunc>,
150
151    pub index: Option<PyNumberUnaryFunc>,
152
153    pub matrix_multiply: Option<PyNumberBinaryFunc>,
154    pub inplace_matrix_multiply: Option<PyNumberBinaryFunc>,
155}
156
157impl PyNumberMethods {
158    /// this is NOT a global variable
159    pub const NOT_IMPLEMENTED: Self = Self {
160        add: None,
161        subtract: None,
162        multiply: None,
163        remainder: None,
164        divmod: None,
165        power: None,
166        negative: None,
167        positive: None,
168        absolute: None,
169        boolean: None,
170        invert: None,
171        lshift: None,
172        rshift: None,
173        and: None,
174        xor: None,
175        or: None,
176        int: None,
177        float: None,
178        inplace_add: None,
179        inplace_subtract: None,
180        inplace_multiply: None,
181        inplace_remainder: None,
182        inplace_power: None,
183        inplace_lshift: None,
184        inplace_rshift: None,
185        inplace_and: None,
186        inplace_xor: None,
187        inplace_or: None,
188        floor_divide: None,
189        true_divide: None,
190        inplace_floor_divide: None,
191        inplace_true_divide: None,
192        index: None,
193        matrix_multiply: None,
194        inplace_matrix_multiply: None,
195    };
196
197    pub fn not_implemented() -> &'static Self {
198        static GLOBAL_NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods::NOT_IMPLEMENTED;
199        &GLOBAL_NOT_IMPLEMENTED
200    }
201}
202
203/// Matches the NB_* constants ordering from opcode.h / BinaryOperator.
204#[derive(Copy, Clone)]
205pub enum PyNumberBinaryOp {
206    Add,
207    And,
208    FloorDivide,
209    Lshift,
210    MatrixMultiply,
211    Multiply,
212    Remainder,
213    Or,
214    Rshift,
215    Subtract,
216    TrueDivide,
217    Xor,
218    InplaceAdd,
219    InplaceAnd,
220    InplaceFloorDivide,
221    InplaceLshift,
222    InplaceMatrixMultiply,
223    InplaceMultiply,
224    InplaceRemainder,
225    InplaceOr,
226    InplaceRshift,
227    InplaceSubtract,
228    InplaceTrueDivide,
229    InplaceXor,
230    Divmod,
231}
232
233impl PyNumberBinaryOp {
234    /// Returns `None` for in-place ops which don't have right-side variants.
235    pub fn right_method_name(
236        self,
237        vm: &VirtualMachine,
238    ) -> Option<&'static crate::builtins::PyStrInterned> {
239        use PyNumberBinaryOp::*;
240        Some(match self {
241            Add => identifier!(vm, __radd__),
242            Subtract => identifier!(vm, __rsub__),
243            Multiply => identifier!(vm, __rmul__),
244            Remainder => identifier!(vm, __rmod__),
245            Divmod => identifier!(vm, __rdivmod__),
246            Lshift => identifier!(vm, __rlshift__),
247            Rshift => identifier!(vm, __rrshift__),
248            And => identifier!(vm, __rand__),
249            Xor => identifier!(vm, __rxor__),
250            Or => identifier!(vm, __ror__),
251            FloorDivide => identifier!(vm, __rfloordiv__),
252            TrueDivide => identifier!(vm, __rtruediv__),
253            MatrixMultiply => identifier!(vm, __rmatmul__),
254            // In-place ops don't have right-side variants
255            InplaceAdd
256            | InplaceSubtract
257            | InplaceMultiply
258            | InplaceRemainder
259            | InplaceLshift
260            | InplaceRshift
261            | InplaceAnd
262            | InplaceXor
263            | InplaceOr
264            | InplaceFloorDivide
265            | InplaceTrueDivide
266            | InplaceMatrixMultiply => return None,
267        })
268    }
269}
270
271#[derive(Copy, Clone)]
272pub enum PyNumberTernaryOp {
273    Power,
274    InplacePower,
275}
276
277impl PyNumberTernaryOp {
278    /// Returns `None` for in-place ops which don't have right-side variants.
279    pub fn right_method_name(
280        self,
281        vm: &VirtualMachine,
282    ) -> Option<&'static crate::builtins::PyStrInterned> {
283        Some(match self {
284            PyNumberTernaryOp::Power => identifier!(vm, __rpow__),
285            PyNumberTernaryOp::InplacePower => return None,
286        })
287    }
288}
289
290#[derive(Default)]
291pub struct PyNumberSlots {
292    pub add: AtomicCell<Option<PyNumberBinaryFunc>>,
293    pub subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
294    pub multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
295    pub remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
296    pub divmod: AtomicCell<Option<PyNumberBinaryFunc>>,
297    pub power: AtomicCell<Option<PyNumberTernaryFunc>>,
298    pub negative: AtomicCell<Option<PyNumberUnaryFunc>>,
299    pub positive: AtomicCell<Option<PyNumberUnaryFunc>>,
300    pub absolute: AtomicCell<Option<PyNumberUnaryFunc>>,
301    pub boolean: AtomicCell<Option<PyNumberUnaryFunc<bool>>>,
302    pub invert: AtomicCell<Option<PyNumberUnaryFunc>>,
303    pub lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
304    pub rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
305    pub and: AtomicCell<Option<PyNumberBinaryFunc>>,
306    pub xor: AtomicCell<Option<PyNumberBinaryFunc>>,
307    pub or: AtomicCell<Option<PyNumberBinaryFunc>>,
308    pub int: AtomicCell<Option<PyNumberUnaryFunc>>,
309    pub float: AtomicCell<Option<PyNumberUnaryFunc>>,
310
311    // Right variants (internal - not exposed in SlotAccessor)
312    pub right_add: AtomicCell<Option<PyNumberBinaryFunc>>,
313    pub right_subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
314    pub right_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
315    pub right_remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
316    pub right_divmod: AtomicCell<Option<PyNumberBinaryFunc>>,
317    pub right_power: AtomicCell<Option<PyNumberTernaryFunc>>,
318    pub right_lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
319    pub right_rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
320    pub right_and: AtomicCell<Option<PyNumberBinaryFunc>>,
321    pub right_xor: AtomicCell<Option<PyNumberBinaryFunc>>,
322    pub right_or: AtomicCell<Option<PyNumberBinaryFunc>>,
323
324    pub inplace_add: AtomicCell<Option<PyNumberBinaryFunc>>,
325    pub inplace_subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
326    pub inplace_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
327    pub inplace_remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
328    pub inplace_power: AtomicCell<Option<PyNumberTernaryFunc>>,
329    pub inplace_lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
330    pub inplace_rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
331    pub inplace_and: AtomicCell<Option<PyNumberBinaryFunc>>,
332    pub inplace_xor: AtomicCell<Option<PyNumberBinaryFunc>>,
333    pub inplace_or: AtomicCell<Option<PyNumberBinaryFunc>>,
334
335    pub floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
336    pub true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
337    pub right_floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
338    pub right_true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
339    pub inplace_floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
340    pub inplace_true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
341
342    pub index: AtomicCell<Option<PyNumberUnaryFunc>>,
343
344    pub matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
345    pub right_matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
346    pub inplace_matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
347}
348
349impl From<&PyNumberMethods> for PyNumberSlots {
350    fn from(value: &PyNumberMethods) -> Self {
351        // right_* slots use the same function as left ops for native types
352        Self {
353            add: AtomicCell::new(value.add),
354            subtract: AtomicCell::new(value.subtract),
355            multiply: AtomicCell::new(value.multiply),
356            remainder: AtomicCell::new(value.remainder),
357            divmod: AtomicCell::new(value.divmod),
358            power: AtomicCell::new(value.power),
359            negative: AtomicCell::new(value.negative),
360            positive: AtomicCell::new(value.positive),
361            absolute: AtomicCell::new(value.absolute),
362            boolean: AtomicCell::new(value.boolean),
363            invert: AtomicCell::new(value.invert),
364            lshift: AtomicCell::new(value.lshift),
365            rshift: AtomicCell::new(value.rshift),
366            and: AtomicCell::new(value.and),
367            xor: AtomicCell::new(value.xor),
368            or: AtomicCell::new(value.or),
369            int: AtomicCell::new(value.int),
370            float: AtomicCell::new(value.float),
371            right_add: AtomicCell::new(value.add),
372            right_subtract: AtomicCell::new(value.subtract),
373            right_multiply: AtomicCell::new(value.multiply),
374            right_remainder: AtomicCell::new(value.remainder),
375            right_divmod: AtomicCell::new(value.divmod),
376            right_power: AtomicCell::new(value.power),
377            right_lshift: AtomicCell::new(value.lshift),
378            right_rshift: AtomicCell::new(value.rshift),
379            right_and: AtomicCell::new(value.and),
380            right_xor: AtomicCell::new(value.xor),
381            right_or: AtomicCell::new(value.or),
382            inplace_add: AtomicCell::new(value.inplace_add),
383            inplace_subtract: AtomicCell::new(value.inplace_subtract),
384            inplace_multiply: AtomicCell::new(value.inplace_multiply),
385            inplace_remainder: AtomicCell::new(value.inplace_remainder),
386            inplace_power: AtomicCell::new(value.inplace_power),
387            inplace_lshift: AtomicCell::new(value.inplace_lshift),
388            inplace_rshift: AtomicCell::new(value.inplace_rshift),
389            inplace_and: AtomicCell::new(value.inplace_and),
390            inplace_xor: AtomicCell::new(value.inplace_xor),
391            inplace_or: AtomicCell::new(value.inplace_or),
392            floor_divide: AtomicCell::new(value.floor_divide),
393            true_divide: AtomicCell::new(value.true_divide),
394            right_floor_divide: AtomicCell::new(value.floor_divide),
395            right_true_divide: AtomicCell::new(value.true_divide),
396            inplace_floor_divide: AtomicCell::new(value.inplace_floor_divide),
397            inplace_true_divide: AtomicCell::new(value.inplace_true_divide),
398            index: AtomicCell::new(value.index),
399            matrix_multiply: AtomicCell::new(value.matrix_multiply),
400            right_matrix_multiply: AtomicCell::new(value.matrix_multiply),
401            inplace_matrix_multiply: AtomicCell::new(value.inplace_matrix_multiply),
402        }
403    }
404}
405
406impl PyNumberSlots {
407    /// Copy from static PyNumberMethods
408    pub fn copy_from(&self, methods: &PyNumberMethods) {
409        if let Some(f) = methods.add {
410            self.add.store(Some(f));
411            self.right_add.store(Some(f));
412        }
413        if let Some(f) = methods.subtract {
414            self.subtract.store(Some(f));
415            self.right_subtract.store(Some(f));
416        }
417        if let Some(f) = methods.multiply {
418            self.multiply.store(Some(f));
419            self.right_multiply.store(Some(f));
420        }
421        if let Some(f) = methods.remainder {
422            self.remainder.store(Some(f));
423            self.right_remainder.store(Some(f));
424        }
425        if let Some(f) = methods.divmod {
426            self.divmod.store(Some(f));
427            self.right_divmod.store(Some(f));
428        }
429        if let Some(f) = methods.power {
430            self.power.store(Some(f));
431            self.right_power.store(Some(f));
432        }
433        if let Some(f) = methods.negative {
434            self.negative.store(Some(f));
435        }
436        if let Some(f) = methods.positive {
437            self.positive.store(Some(f));
438        }
439        if let Some(f) = methods.absolute {
440            self.absolute.store(Some(f));
441        }
442        if let Some(f) = methods.boolean {
443            self.boolean.store(Some(f));
444        }
445        if let Some(f) = methods.invert {
446            self.invert.store(Some(f));
447        }
448        if let Some(f) = methods.lshift {
449            self.lshift.store(Some(f));
450            self.right_lshift.store(Some(f));
451        }
452        if let Some(f) = methods.rshift {
453            self.rshift.store(Some(f));
454            self.right_rshift.store(Some(f));
455        }
456        if let Some(f) = methods.and {
457            self.and.store(Some(f));
458            self.right_and.store(Some(f));
459        }
460        if let Some(f) = methods.xor {
461            self.xor.store(Some(f));
462            self.right_xor.store(Some(f));
463        }
464        if let Some(f) = methods.or {
465            self.or.store(Some(f));
466            self.right_or.store(Some(f));
467        }
468        if let Some(f) = methods.int {
469            self.int.store(Some(f));
470        }
471        if let Some(f) = methods.float {
472            self.float.store(Some(f));
473        }
474        if let Some(f) = methods.inplace_add {
475            self.inplace_add.store(Some(f));
476        }
477        if let Some(f) = methods.inplace_subtract {
478            self.inplace_subtract.store(Some(f));
479        }
480        if let Some(f) = methods.inplace_multiply {
481            self.inplace_multiply.store(Some(f));
482        }
483        if let Some(f) = methods.inplace_remainder {
484            self.inplace_remainder.store(Some(f));
485        }
486        if let Some(f) = methods.inplace_power {
487            self.inplace_power.store(Some(f));
488        }
489        if let Some(f) = methods.inplace_lshift {
490            self.inplace_lshift.store(Some(f));
491        }
492        if let Some(f) = methods.inplace_rshift {
493            self.inplace_rshift.store(Some(f));
494        }
495        if let Some(f) = methods.inplace_and {
496            self.inplace_and.store(Some(f));
497        }
498        if let Some(f) = methods.inplace_xor {
499            self.inplace_xor.store(Some(f));
500        }
501        if let Some(f) = methods.inplace_or {
502            self.inplace_or.store(Some(f));
503        }
504        if let Some(f) = methods.floor_divide {
505            self.floor_divide.store(Some(f));
506            self.right_floor_divide.store(Some(f));
507        }
508        if let Some(f) = methods.true_divide {
509            self.true_divide.store(Some(f));
510            self.right_true_divide.store(Some(f));
511        }
512        if let Some(f) = methods.inplace_floor_divide {
513            self.inplace_floor_divide.store(Some(f));
514        }
515        if let Some(f) = methods.inplace_true_divide {
516            self.inplace_true_divide.store(Some(f));
517        }
518        if let Some(f) = methods.index {
519            self.index.store(Some(f));
520        }
521        if let Some(f) = methods.matrix_multiply {
522            self.matrix_multiply.store(Some(f));
523            self.right_matrix_multiply.store(Some(f));
524        }
525        if let Some(f) = methods.inplace_matrix_multiply {
526            self.inplace_matrix_multiply.store(Some(f));
527        }
528    }
529
530    pub fn left_binary_op(&self, op_slot: PyNumberBinaryOp) -> Option<PyNumberBinaryFunc> {
531        use PyNumberBinaryOp::*;
532        match op_slot {
533            Add => self.add.load(),
534            Subtract => self.subtract.load(),
535            Multiply => self.multiply.load(),
536            Remainder => self.remainder.load(),
537            Divmod => self.divmod.load(),
538            Lshift => self.lshift.load(),
539            Rshift => self.rshift.load(),
540            And => self.and.load(),
541            Xor => self.xor.load(),
542            Or => self.or.load(),
543            InplaceAdd => self.inplace_add.load(),
544            InplaceSubtract => self.inplace_subtract.load(),
545            InplaceMultiply => self.inplace_multiply.load(),
546            InplaceRemainder => self.inplace_remainder.load(),
547            InplaceLshift => self.inplace_lshift.load(),
548            InplaceRshift => self.inplace_rshift.load(),
549            InplaceAnd => self.inplace_and.load(),
550            InplaceXor => self.inplace_xor.load(),
551            InplaceOr => self.inplace_or.load(),
552            FloorDivide => self.floor_divide.load(),
553            TrueDivide => self.true_divide.load(),
554            InplaceFloorDivide => self.inplace_floor_divide.load(),
555            InplaceTrueDivide => self.inplace_true_divide.load(),
556            MatrixMultiply => self.matrix_multiply.load(),
557            InplaceMatrixMultiply => self.inplace_matrix_multiply.load(),
558        }
559    }
560
561    pub fn right_binary_op(&self, op_slot: PyNumberBinaryOp) -> Option<PyNumberBinaryFunc> {
562        use PyNumberBinaryOp::*;
563        match op_slot {
564            Add => self.right_add.load(),
565            Subtract => self.right_subtract.load(),
566            Multiply => self.right_multiply.load(),
567            Remainder => self.right_remainder.load(),
568            Divmod => self.right_divmod.load(),
569            Lshift => self.right_lshift.load(),
570            Rshift => self.right_rshift.load(),
571            And => self.right_and.load(),
572            Xor => self.right_xor.load(),
573            Or => self.right_or.load(),
574            FloorDivide => self.right_floor_divide.load(),
575            TrueDivide => self.right_true_divide.load(),
576            MatrixMultiply => self.right_matrix_multiply.load(),
577            _ => None,
578        }
579    }
580
581    pub fn left_ternary_op(&self, op_slot: PyNumberTernaryOp) -> Option<PyNumberTernaryFunc> {
582        use PyNumberTernaryOp::*;
583        match op_slot {
584            Power => self.power.load(),
585            InplacePower => self.inplace_power.load(),
586        }
587    }
588
589    pub fn right_ternary_op(&self, op_slot: PyNumberTernaryOp) -> Option<PyNumberTernaryFunc> {
590        use PyNumberTernaryOp::*;
591        match op_slot {
592            Power => self.right_power.load(),
593            _ => None,
594        }
595    }
596}
597#[derive(Copy, Clone)]
598pub struct PyNumber<'a> {
599    pub obj: &'a PyObject,
600}
601
602unsafe impl Traverse for PyNumber<'_> {
603    fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
604        self.obj.traverse(tracer_fn)
605    }
606}
607
608impl Deref for PyNumber<'_> {
609    type Target = PyObject;
610
611    fn deref(&self) -> &Self::Target {
612        self.obj
613    }
614}
615
616impl<'a> PyNumber<'a> {
617    // PyNumber_Check - slots are now inherited
618    pub fn check(obj: &PyObject) -> bool {
619        let methods = &obj.class().slots.as_number;
620        let has_number = methods.int.load().is_some()
621            || methods.index.load().is_some()
622            || methods.float.load().is_some();
623        has_number || obj.downcastable::<PyComplex>()
624    }
625}
626
627impl PyNumber<'_> {
628    // PyIndex_Check
629    pub fn is_index(self) -> bool {
630        self.class().slots.as_number.index.load().is_some()
631    }
632
633    #[inline]
634    pub fn int(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
635        self.class().slots.as_number.int.load().map(|f| {
636            let ret = f(self, vm)?;
637
638            if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
639                return Ok(ret.to_owned());
640            }
641
642            let ret_class = ret.class().to_owned();
643            if let Some(ret) = ret.downcast_ref::<PyInt>() {
644                _warnings::warn(
645                    vm.ctx.exceptions.deprecation_warning,
646                    format!(
647                        "__int__ returned non-int (type {ret_class}).  \
648                    The ability to return an instance of a strict subclass of int \
649                    is deprecated, and may be removed in a future version of Python."
650                    ),
651                    1,
652                    vm,
653                )?;
654
655                Ok(ret.to_owned())
656            } else {
657                Err(vm.new_type_error(format!(
658                    "{}.__int__ returned non-int(type {})",
659                    self.class(),
660                    ret_class
661                )))
662            }
663        })
664    }
665
666    #[inline]
667    pub fn index(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
668        self.class().slots.as_number.index.load().map(|f| {
669            let ret = f(self, vm)?;
670
671            if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
672                return Ok(ret.to_owned());
673            }
674
675            let ret_class = ret.class().to_owned();
676            if let Some(ret) = ret.downcast_ref::<PyInt>() {
677                _warnings::warn(
678                    vm.ctx.exceptions.deprecation_warning,
679                    format!(
680                        "__index__ returned non-int (type {ret_class}).  \
681                    The ability to return an instance of a strict subclass of int \
682                    is deprecated, and may be removed in a future version of Python."
683                    ),
684                    1,
685                    vm,
686                )?;
687
688                Ok(ret.to_owned())
689            } else {
690                Err(vm.new_type_error(format!(
691                    "{}.__index__ returned non-int(type {})",
692                    self.class(),
693                    ret_class
694                )))
695            }
696        })
697    }
698
699    #[inline]
700    pub fn float(self, vm: &VirtualMachine) -> Option<PyResult<PyRef<PyFloat>>> {
701        self.class().slots.as_number.float.load().map(|f| {
702            let ret = f(self, vm)?;
703
704            if let Some(ret) = ret.downcast_ref_if_exact::<PyFloat>(vm) {
705                return Ok(ret.to_owned());
706            }
707
708            let ret_class = ret.class().to_owned();
709            if let Some(ret) = ret.downcast_ref::<PyFloat>() {
710                _warnings::warn(
711                    vm.ctx.exceptions.deprecation_warning,
712                    format!(
713                        "__float__ returned non-float (type {ret_class}).  \
714                    The ability to return an instance of a strict subclass of float \
715                    is deprecated, and may be removed in a future version of Python."
716                    ),
717                    1,
718                    vm,
719                )?;
720
721                Ok(ret.to_owned())
722            } else {
723                Err(vm.new_type_error(format!(
724                    "{}.__float__ returned non-float(type {})",
725                    self.class(),
726                    ret_class
727                )))
728            }
729        })
730    }
731}
732
733pub fn handle_bytes_to_int_err(
734    e: BytesToIntError,
735    obj: &PyObject,
736    vm: &VirtualMachine,
737) -> PyBaseExceptionRef {
738    match e {
739        BytesToIntError::InvalidLiteral { base } => vm.new_value_error(format!(
740            "invalid literal for int() with base {base}: {}",
741            match obj.repr(vm) {
742                Ok(v) => v,
743                Err(err) => return err,
744            },
745        )),
746        BytesToIntError::InvalidBase => {
747            vm.new_value_error("int() base must be >= 2 and <= 36, or 0")
748        }
749        BytesToIntError::DigitLimit { got, limit } => vm.new_value_error(format!(
750"Exceeds the limit ({limit} digits) for integer string conversion: value has {got} digits; use sys.set_int_max_str_digits() to increase the limit"
751                )),
752    }
753}