Skip to main content

rustpython_vm/protocol/
number.rs

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