From 26f104be4cdb3645199d685b667d983e2b17a723 Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Wed, 8 Mar 2023 00:26:46 +0900
Subject: [PATCH 01/11] Add PyNumberSlots to support right binary ops

---
 vm/src/types/slot.rs | 409 +++++++++++++++++++++++++++++++------------
 vm/src/vm/vm_ops.rs  |  20 +--
 2 files changed, 309 insertions(+), 120 deletions(-)

diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index c7c23eec9b..72c021ec41 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -1,14 +1,13 @@
-use crate::common::{hash::PyHash, lock::PyRwLock};
-use crate::convert::ToPyObject;
 use crate::{
     builtins::{type_::PointerSlot, PyFloat, PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef},
     bytecode::ComparisonOperator,
-    convert::ToPyResult,
+    common::{hash::PyHash, lock::PyRwLock},
+    convert::{ToPyObject, ToPyResult},
     function::{Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
     identifier,
     protocol::{
-        PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberMethods, PySequence,
-        PySequenceMethods,
+        PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberBinaryOpSlot,
+        PyNumberMethods, PySequence, PySequenceMethods,
     },
     vm::Context,
     AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -85,6 +84,7 @@ pub struct PyTypeSlots {
 
     // The count of tp_members.
     pub member_count: usize,
+    pub number: PyNumberSlots,
 }
 
 impl PyTypeSlots {
@@ -102,6 +102,131 @@ impl std::fmt::Debug for PyTypeSlots {
     }
 }
 
+pub(crate) type NumberUnaryFunc<R = PyObjectRef> = fn(PyNumber, &VirtualMachine) -> PyResult<R>;
+pub(crate) type NumberBinaryFunc = fn(PyNumber, &PyObject, &VirtualMachine) -> PyResult;
+
+#[derive(Default)]
+pub struct PyNumberSlots {
+    pub add: AtomicCell<Option<NumberBinaryFunc>>,
+    pub subtract: AtomicCell<Option<NumberBinaryFunc>>,
+    pub multiply: AtomicCell<Option<NumberBinaryFunc>>,
+    pub remainder: AtomicCell<Option<NumberBinaryFunc>>,
+    pub divmod: AtomicCell<Option<NumberBinaryFunc>>,
+    pub power: AtomicCell<Option<NumberBinaryFunc>>,
+    pub negative: AtomicCell<Option<NumberUnaryFunc>>,
+    pub positive: AtomicCell<Option<NumberUnaryFunc>>,
+    pub absolute: AtomicCell<Option<NumberUnaryFunc>>,
+    pub boolean: AtomicCell<Option<NumberUnaryFunc<bool>>>,
+    pub invert: AtomicCell<Option<NumberUnaryFunc>>,
+    pub lshift: AtomicCell<Option<NumberBinaryFunc>>,
+    pub rshift: AtomicCell<Option<NumberBinaryFunc>>,
+    pub and: AtomicCell<Option<NumberBinaryFunc>>,
+    pub xor: AtomicCell<Option<NumberBinaryFunc>>,
+    pub or: AtomicCell<Option<NumberBinaryFunc>>,
+    pub int: AtomicCell<Option<NumberUnaryFunc<PyRef<PyInt>>>>,
+    pub float: AtomicCell<Option<NumberUnaryFunc<PyRef<PyFloat>>>>,
+
+    pub right_add: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_subtract: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_multiply: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_remainder: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_divmod: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_power: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_lshift: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_rshift: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_and: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_xor: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_or: AtomicCell<Option<NumberBinaryFunc>>,
+
+    pub inplace_add: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_subtract: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_multiply: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_remainder: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_power: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_lshift: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_rshift: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_and: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_xor: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_or: AtomicCell<Option<NumberBinaryFunc>>,
+
+    pub floor_divide: AtomicCell<Option<NumberBinaryFunc>>,
+    pub true_divide: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_floor_divide: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_true_divide: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_floor_divide: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_true_divide: AtomicCell<Option<NumberBinaryFunc>>,
+
+    pub index: AtomicCell<Option<NumberUnaryFunc<PyRef<PyInt>>>>,
+
+    pub matrix_multiply: AtomicCell<Option<NumberBinaryFunc>>,
+    pub right_matrix_multiply: AtomicCell<Option<NumberBinaryFunc>>,
+    pub inplace_matrix_multiply: AtomicCell<Option<NumberBinaryFunc>>,
+}
+
+impl PyNumberSlots {
+    pub fn get_left_binary_op(
+        &self,
+        op_slot: &PyNumberBinaryOpSlot,
+    ) -> PyResult<Option<NumberBinaryFunc>> {
+        use PyNumberBinaryOpSlot::*;
+        let binary_op = match op_slot {
+            Add => self.add.load(),
+            Subtract => self.subtract.load(),
+            Multiply => self.multiply.load(),
+            Remainder => self.remainder.load(),
+            Divmod => self.divmod.load(),
+            Power => self.power.load(),
+            Lshift => self.lshift.load(),
+            Rshift => self.rshift.load(),
+            And => self.and.load(),
+            Xor => self.xor.load(),
+            Or => self.or.load(),
+            InplaceAdd => self.inplace_add.load(),
+            InplaceSubtract => self.inplace_subtract.load(),
+            InplaceMultiply => self.inplace_multiply.load(),
+            InplaceRemainder => self.inplace_remainder.load(),
+            InplacePower => self.inplace_power.load(),
+            InplaceLshift => self.inplace_lshift.load(),
+            InplaceRshift => self.inplace_rshift.load(),
+            InplaceAnd => self.inplace_and.load(),
+            InplaceXor => self.inplace_xor.load(),
+            InplaceOr => self.inplace_or.load(),
+            FloorDivide => self.floor_divide.load(),
+            TrueDivide => self.true_divide.load(),
+            InplaceFloorDivide => self.inplace_floor_divide.load(),
+            InplaceTrueDivide => self.inplace_true_divide.load(),
+            MatrixMultiply => self.matrix_multiply.load(),
+            InplaceMatrixMultiply => self.inplace_matrix_multiply.load(),
+        };
+        Ok(binary_op)
+    }
+
+    pub fn get_right_binary_op(
+        &self,
+        op_slot: &PyNumberBinaryOpSlot,
+    ) -> PyResult<Option<NumberBinaryFunc>> {
+        use PyNumberBinaryOpSlot::*;
+        let binary_op = match op_slot {
+            Add => self.right_add.load(),
+            Subtract => self.right_subtract.load(),
+            Multiply => self.right_multiply.load(),
+            Remainder => self.right_remainder.load(),
+            Divmod => self.right_divmod.load(),
+            Power => self.right_power.load(),
+            Lshift => self.right_lshift.load(),
+            Rshift => self.right_rshift.load(),
+            And => self.right_and.load(),
+            Xor => self.right_xor.load(),
+            Or => self.right_or.load(),
+            FloorDivide => self.right_floor_divide.load(),
+            TrueDivide => self.right_true_divide.load(),
+            MatrixMultiply => self.right_matrix_multiply.load(),
+            _ => None,
+        };
+        Ok(binary_op)
+    }
+}
+
 bitflags! {
     #[non_exhaustive]
     pub struct PyTypeFlags: u64 {
@@ -373,6 +498,15 @@ impl PyType {
             }};
         }
 
+        macro_rules! toggle_subslot {
+            ($group:ident, $name:ident, $func:expr) => {
+                self.slots
+                    .$group
+                    .$name
+                    .store(if ADD { Some($func) } else { None });
+            };
+        }
+
         macro_rules! update_slot {
             ($name:ident, $func:expr) => {{
                 self.slots.$name.store(Some($func));
@@ -497,192 +631,179 @@ impl PyType {
                 update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __add__) => {
-                toggle_ext_func!(number_methods, add, number_binary_op_wrapper!(__add__));
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, add, number_binary_op_wrapper!(__add__));
+            }
+            _ if name == identifier!(ctx, __radd__) => {
+                toggle_subslot!(number, right_add, number_binary_op_wrapper!(__radd__));
             }
             _ if name == identifier!(ctx, __iadd__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    inplace_add,
-                    number_binary_op_wrapper!(__iadd__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, inplace_add, number_binary_op_wrapper!(__iadd__));
             }
             _ if name == identifier!(ctx, __sub__) => {
-                toggle_ext_func!(number_methods, subtract, number_binary_op_wrapper!(__sub__));
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, subtract, number_binary_op_wrapper!(__sub__));
+            }
+            _ if name == identifier!(ctx, __rsub__) => {
+                toggle_subslot!(number, right_subtract, number_binary_op_wrapper!(__rsub__));
             }
             _ if name == identifier!(ctx, __isub__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_subtract,
                     number_binary_op_wrapper!(__isub__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __mul__) => {
-                toggle_ext_func!(number_methods, multiply, number_binary_op_wrapper!(__mul__));
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, multiply, number_binary_op_wrapper!(__mul__));
+            }
+            _ if name == identifier!(ctx, __rmul__) => {
+                toggle_subslot!(number, right_multiply, number_binary_op_wrapper!(__rmul__));
             }
             _ if name == identifier!(ctx, __imul__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_multiply,
                     number_binary_op_wrapper!(__imul__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __mod__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    remainder,
-                    number_binary_op_wrapper!(__mod__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, remainder, number_binary_op_wrapper!(__mod__));
+            }
+            _ if name == identifier!(ctx, __rmod__) => {
+                toggle_subslot!(number, right_remainder, number_binary_op_wrapper!(__rmod__));
             }
             _ if name == identifier!(ctx, __imod__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_remainder,
                     number_binary_op_wrapper!(__imod__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __divmod__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    divmod,
-                    number_binary_op_wrapper!(__divmod__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, divmod, number_binary_op_wrapper!(__divmod__));
+            }
+            _ if name == identifier!(ctx, __rdivmod__) => {
+                toggle_subslot!(number, right_divmod, number_binary_op_wrapper!(__rdivmod__));
             }
             _ if name == identifier!(ctx, __pow__) => {
-                toggle_ext_func!(number_methods, power, number_binary_op_wrapper!(__pow__));
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, power, number_binary_op_wrapper!(__pow__));
+            }
+            _ if name == identifier!(ctx, __rpow__) => {
+                toggle_subslot!(number, right_power, number_binary_op_wrapper!(__rpow__));
             }
             _ if name == identifier!(ctx, __ipow__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    inplace_power,
-                    number_binary_op_wrapper!(__ipow__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, inplace_power, number_binary_op_wrapper!(__ipow__));
             }
             _ if name == identifier!(ctx, __lshift__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    lshift,
-                    number_binary_op_wrapper!(__lshift__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, lshift, number_binary_op_wrapper!(__lshift__));
+            }
+            _ if name == identifier!(ctx, __rlshift__) => {
+                toggle_subslot!(number, right_lshift, number_binary_op_wrapper!(__rlshift__));
             }
             _ if name == identifier!(ctx, __ilshift__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_lshift,
                     number_binary_op_wrapper!(__ilshift__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __rshift__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    rshift,
-                    number_binary_op_wrapper!(__rshift__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, rshift, number_binary_op_wrapper!(__rshift__));
+            }
+            _ if name == identifier!(ctx, __rrshift__) => {
+                toggle_subslot!(number, right_rshift, number_binary_op_wrapper!(__rrshift__));
             }
             _ if name == identifier!(ctx, __irshift__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_rshift,
                     number_binary_op_wrapper!(__irshift__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __and__) => {
-                toggle_ext_func!(number_methods, and, number_binary_op_wrapper!(__and__));
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, and, number_binary_op_wrapper!(__and__));
+            }
+            _ if name == identifier!(ctx, __rand__) => {
+                toggle_subslot!(number, right_and, number_binary_op_wrapper!(__rand__));
             }
             _ if name == identifier!(ctx, __iand__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    inplace_and,
-                    number_binary_op_wrapper!(__iand__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, inplace_and, number_binary_op_wrapper!(__iand__));
             }
             _ if name == identifier!(ctx, __xor__) => {
-                toggle_ext_func!(number_methods, xor, number_binary_op_wrapper!(__xor__));
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, xor, number_binary_op_wrapper!(__xor__));
+            }
+            _ if name == identifier!(ctx, __rxor__) => {
+                toggle_subslot!(number, right_xor, number_binary_op_wrapper!(__rxor__));
             }
             _ if name == identifier!(ctx, __ixor__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    inplace_xor,
-                    number_binary_op_wrapper!(__ixor__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, inplace_xor, number_binary_op_wrapper!(__ixor__));
             }
             _ if name == identifier!(ctx, __or__) => {
-                toggle_ext_func!(number_methods, or, number_binary_op_wrapper!(__or__));
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, or, number_binary_op_wrapper!(__or__));
+            }
+            _ if name == identifier!(ctx, __ror__) => {
+                toggle_subslot!(number, right_or, number_binary_op_wrapper!(__ror__));
             }
             _ if name == identifier!(ctx, __ior__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    inplace_or,
-                    number_binary_op_wrapper!(__ior__)
-                );
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, inplace_or, number_binary_op_wrapper!(__ior__));
             }
             _ if name == identifier!(ctx, __floordiv__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     floor_divide,
                     number_binary_op_wrapper!(__floordiv__)
                 );
-                update_pointer_slot!(as_number, number_methods);
+            }
+            _ if name == identifier!(ctx, __rfloordiv__) => {
+                toggle_subslot!(
+                    number,
+                    right_floor_divide,
+                    number_binary_op_wrapper!(__rfloordiv__)
+                );
             }
             _ if name == identifier!(ctx, __ifloordiv__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_floor_divide,
                     number_binary_op_wrapper!(__ifloordiv__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __truediv__) => {
-                toggle_ext_func!(
-                    number_methods,
-                    true_divide,
-                    number_binary_op_wrapper!(__truediv__)
+                toggle_subslot!(number, true_divide, number_binary_op_wrapper!(__truediv__));
+            }
+            _ if name == identifier!(ctx, __rtruediv__) => {
+                toggle_subslot!(
+                    number,
+                    right_true_divide,
+                    number_binary_op_wrapper!(__rtruediv__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __itruediv__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_true_divide,
                     number_binary_op_wrapper!(__itruediv__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ if name == identifier!(ctx, __matmul__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     matrix_multiply,
                     number_binary_op_wrapper!(__matmul__)
                 );
-                update_pointer_slot!(as_number, number_methods);
+            }
+            _ if name == identifier!(ctx, __rmatmul__) => {
+                toggle_subslot!(
+                    number,
+                    right_matrix_multiply,
+                    number_binary_op_wrapper!(__rmatmul__)
+                );
             }
             _ if name == identifier!(ctx, __imatmul__) => {
-                toggle_ext_func!(
-                    number_methods,
+                toggle_subslot!(
+                    number,
                     inplace_matrix_multiply,
                     number_binary_op_wrapper!(__imatmul__)
                 );
-                update_pointer_slot!(as_number, number_methods);
             }
             _ => {}
         }
@@ -1161,6 +1282,24 @@ pub trait AsSequence: PyPayload {
     }
 }
 
+macro_rules! extend_number_slot {
+    ($slots:ident, $methods:ident, $method:ident, $right_method:ident, $op_slot:ident) => {
+        if $methods.$method.load().is_some() {
+            $slots.number.$method.store($methods.$method.load());
+            $slots.number.$right_method.store(Some(|num, other, vm| {
+                num.get_binary_op(&PyNumberBinaryOpSlot::$op_slot)?
+                    .load()
+                    .unwrap()(other.to_number(), num.obj, vm)
+            }));
+        }
+    };
+    ($slots:ident, $methods:ident, $method:ident) => {
+        if $methods.$method.load().is_some() {
+            $slots.number.$method.store($methods.$method.load());
+        }
+    };
+}
+
 #[pyclass]
 pub trait AsNumber: PyPayload {
     #[pyslot]
@@ -1184,6 +1323,60 @@ pub trait AsNumber: PyPayload {
             Self::clone_exact(Self::number_downcast(number), vm)
         }
     }
+
+    fn extend_slots(slots: &mut PyTypeSlots) {
+        let methods = Self::as_number();
+
+        extend_number_slot!(slots, methods, add, right_add, Add);
+        extend_number_slot!(slots, methods, subtract, right_subtract, Subtract);
+        extend_number_slot!(slots, methods, multiply, right_multiply, Multiply);
+        extend_number_slot!(slots, methods, remainder, right_remainder, Remainder);
+        extend_number_slot!(slots, methods, divmod, right_divmod, Divmod);
+        extend_number_slot!(slots, methods, power, right_power, Power);
+        extend_number_slot!(slots, methods, lshift, right_lshift, Lshift);
+        extend_number_slot!(slots, methods, rshift, right_rshift, Rshift);
+        extend_number_slot!(slots, methods, and, right_and, And);
+        extend_number_slot!(slots, methods, xor, right_xor, Xor);
+        extend_number_slot!(slots, methods, or, right_or, Or);
+        extend_number_slot!(
+            slots,
+            methods,
+            floor_divide,
+            right_floor_divide,
+            FloorDivide
+        );
+        extend_number_slot!(slots, methods, true_divide, right_true_divide, TrueDivide);
+        extend_number_slot!(
+            slots,
+            methods,
+            matrix_multiply,
+            right_matrix_multiply,
+            MatrixMultiply
+        );
+
+        extend_number_slot!(slots, methods, negative);
+        extend_number_slot!(slots, methods, positive);
+        extend_number_slot!(slots, methods, absolute);
+        extend_number_slot!(slots, methods, boolean);
+        extend_number_slot!(slots, methods, invert);
+        extend_number_slot!(slots, methods, int);
+        extend_number_slot!(slots, methods, float);
+        extend_number_slot!(slots, methods, index);
+
+        extend_number_slot!(slots, methods, inplace_add);
+        extend_number_slot!(slots, methods, inplace_subtract);
+        extend_number_slot!(slots, methods, inplace_multiply);
+        extend_number_slot!(slots, methods, inplace_remainder);
+        extend_number_slot!(slots, methods, inplace_power);
+        extend_number_slot!(slots, methods, inplace_lshift);
+        extend_number_slot!(slots, methods, inplace_rshift);
+        extend_number_slot!(slots, methods, inplace_and);
+        extend_number_slot!(slots, methods, inplace_xor);
+        extend_number_slot!(slots, methods, inplace_or);
+        extend_number_slot!(slots, methods, inplace_floor_divide);
+        extend_number_slot!(slots, methods, inplace_true_divide);
+        extend_number_slot!(slots, methods, inplace_matrix_multiply);
+    }
 }
 
 #[pyclass]
diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs
index fb524bc16b..6f892b828d 100644
--- a/vm/src/vm/vm_ops.rs
+++ b/vm/src/vm/vm_ops.rs
@@ -128,18 +128,15 @@ impl VirtualMachine {
     /// Calling scheme used for binary operations:
     ///
     /// Order operations are tried until either a valid result or error:
-    ///   b.op(a,b)[*], a.op(a,b), b.op(a,b)
+    ///   b.rop(b,a)[*], a.op(a,b), b.rop(b,a)
     ///
     /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a)
     fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: &PyNumberBinaryOpSlot) -> PyResult {
-        let num_a = a.to_number();
-        let num_b = b.to_number();
-
-        let slot_a = num_a.get_binary_op(op_slot)?.load();
+        let slot_a = a.class().slots.number.get_left_binary_op(op_slot)?;
         let mut slot_b = if b.class().is(a.class()) {
             None
         } else {
-            match num_b.get_binary_op(op_slot)?.load() {
+            match b.class().slots.number.get_right_binary_op(op_slot)? {
                 Some(slot_b)
                     if slot_b as usize == slot_a.map(|s| s as usize).unwrap_or_default() =>
                 {
@@ -152,21 +149,21 @@ impl VirtualMachine {
         if let Some(slot_a) = slot_a {
             if let Some(slot_bb) = slot_b {
                 if b.fast_isinstance(a.class()) {
-                    let x = slot_bb(num_a, b, self)?;
+                    let x = slot_bb(b.to_number(), a, self)?;
                     if !x.is(&self.ctx.not_implemented) {
                         return Ok(x);
                     }
                     slot_b = None;
                 }
             }
-            let x = slot_a(num_a, b, self)?;
+            let x = slot_a(a.to_number(), b, self)?;
             if !x.is(&self.ctx.not_implemented) {
                 return Ok(x);
             }
         }
 
         if let Some(slot_b) = slot_b {
-            let x = slot_b(num_a, b, self)?;
+            let x = slot_b(b.to_number(), a, self)?;
             if !x.is(&self.ctx.not_implemented) {
                 return Ok(x);
             }
@@ -209,9 +206,8 @@ impl VirtualMachine {
         iop_slot: &PyNumberBinaryOpSlot,
         op_slot: &PyNumberBinaryOpSlot,
     ) -> PyResult {
-        let num_a = a.to_number();
-        if let Some(slot) = num_a.get_binary_op(iop_slot)?.load() {
-            let x = slot(num_a, b, self)?;
+        if let Some(slot) = a.class().slots.number.get_left_binary_op(iop_slot)? {
+            let x = slot(a.to_number(), b, self)?;
             if !x.is(&self.ctx.not_implemented) {
                 return Ok(x);
             }

From 6a79e15c5e053469f9d183e280ea502b18ccfa7f Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Wed, 8 Mar 2023 03:33:45 +0900
Subject: [PATCH 02/11] Remove AtomicCell from PyNumberMethods fields

---
 vm/src/builtins/bytearray.rs    |   2 +-
 vm/src/builtins/bytes.rs        |   2 +-
 vm/src/builtins/complex.rs      |  27 +++--
 vm/src/builtins/dict.rs         |   6 +-
 vm/src/builtins/float.rs        |  50 +++-------
 vm/src/builtins/int.rs          |  64 ++++--------
 vm/src/builtins/mappingproxy.rs |   7 +-
 vm/src/builtins/set.rs          |  24 ++---
 vm/src/builtins/singletons.rs   |   6 +-
 vm/src/builtins/str.rs          |   2 +-
 vm/src/builtins/type.rs         |   3 +-
 vm/src/builtins/union.rs        |   5 +-
 vm/src/protocol/mod.rs          |   4 +-
 vm/src/protocol/number.rs       | 169 ++++++++++++++++----------------
 vm/src/types/slot.rs            | 144 +++++++++++++--------------
 15 files changed, 229 insertions(+), 286 deletions(-)

diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs
index 48639b818e..d9ab7482c9 100644
--- a/vm/src/builtins/bytearray.rs
+++ b/vm/src/builtins/bytearray.rs
@@ -860,7 +860,7 @@ impl AsSequence for PyByteArray {
 impl AsNumber for PyByteArray {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            remainder: atomic_func!(|number, other, vm| {
+            remainder: Some(|number, other, vm| {
                 PyByteArray::number_downcast(number)
                     .mod_(other.to_owned(), vm)
                     .to_pyresult(vm)
diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs
index df18ac8457..de5d835e14 100644
--- a/vm/src/builtins/bytes.rs
+++ b/vm/src/builtins/bytes.rs
@@ -630,7 +630,7 @@ impl AsSequence for PyBytes {
 impl AsNumber for PyBytes {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            remainder: atomic_func!(|number, other, vm| {
+            remainder: Some(|number, other, vm| {
                 PyBytes::number_downcast(number)
                     .mod_(other.to_owned(), vm)
                     .to_pyresult(vm)
diff --git a/vm/src/builtins/complex.rs b/vm/src/builtins/complex.rs
index c288c8e135..dac334bcfb 100644
--- a/vm/src/builtins/complex.rs
+++ b/vm/src/builtins/complex.rs
@@ -1,6 +1,5 @@
 use super::{float, PyStr, PyType, PyTypeRef};
 use crate::{
-    atomic_func,
     class::PyClassImpl,
     convert::{ToPyObject, ToPyResult},
     function::{
@@ -455,33 +454,29 @@ impl Hashable for PyComplex {
 impl AsNumber for PyComplex {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            add: atomic_func!(|number, other, vm| {
+            add: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, |a, b, _vm| a + b, vm)
             }),
-            subtract: atomic_func!(|number, other, vm| {
+            subtract: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, |a, b, _vm| a - b, vm)
             }),
-            multiply: atomic_func!(|number, other, vm| {
+            multiply: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, |a, b, _vm| a * b, vm)
             }),
-            power: atomic_func!(|number, other, vm| PyComplex::number_op(
-                number, other, inner_pow, vm
-            )),
-            negative: atomic_func!(|number, vm| {
+            power: Some(|number, other, vm| PyComplex::number_op(number, other, inner_pow, vm)),
+            negative: Some(|number, vm| {
                 let value = PyComplex::number_downcast(number).value;
                 (-value).to_pyresult(vm)
             }),
-            positive: atomic_func!(
-                |number, vm| PyComplex::number_downcast_exact(number, vm).to_pyresult(vm)
-            ),
-            absolute: atomic_func!(|number, vm| {
+            positive: Some(|number, vm| {
+                PyComplex::number_downcast_exact(number, vm).to_pyresult(vm)
+            }),
+            absolute: Some(|number, vm| {
                 let value = PyComplex::number_downcast(number).value;
                 value.norm().to_pyresult(vm)
             }),
-            boolean: atomic_func!(|number, _vm| Ok(PyComplex::number_downcast(number)
-                .value
-                .is_zero())),
-            true_divide: atomic_func!(|number, other, vm| {
+            boolean: Some(|number, _vm| Ok(PyComplex::number_downcast(number).value.is_zero())),
+            true_divide: Some(|number, other, vm| {
                 PyComplex::number_op(number, other, inner_div, vm)
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs
index 5590f856be..764f0c130c 100644
--- a/vm/src/builtins/dict.rs
+++ b/vm/src/builtins/dict.rs
@@ -480,10 +480,8 @@ impl AsSequence for PyDict {
 impl AsNumber for PyDict {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, args, vm| {
-                PyDict::number_downcast(num).or(args.to_pyobject(vm), vm)
-            }),
-            inplace_or: atomic_func!(|num, args, vm| {
+            or: Some(|num, args, vm| PyDict::number_downcast(num).or(args.to_pyobject(vm), vm)),
+            inplace_or: Some(|num, args, vm| {
                 PyDict::ior(
                     PyDict::number_downcast(num).to_owned(),
                     args.to_pyobject(vm),
diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs
index 38e1521393..12fbb6f480 100644
--- a/vm/src/builtins/float.rs
+++ b/vm/src/builtins/float.rs
@@ -2,7 +2,6 @@ use super::{
     try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef,
 };
 use crate::{
-    atomic_func,
     class::PyClassImpl,
     common::format::FormatSpec,
     common::{float_ops, hash},
@@ -546,50 +545,29 @@ impl Hashable for PyFloat {
 impl AsNumber for PyFloat {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            add: atomic_func!(|num, other, vm| PyFloat::number_op(
-                num,
-                other,
-                |a, b, _vm| a + b,
-                vm
-            )),
-            subtract: atomic_func!(|num, other, vm| PyFloat::number_op(
-                num,
-                other,
-                |a, b, _vm| a - b,
-                vm
-            )),
-            multiply: atomic_func!(|num, other, vm| PyFloat::number_op(
-                num,
-                other,
-                |a, b, _vm| a * b,
-                vm
-            )),
-            remainder: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, inner_mod, vm)),
-            divmod: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, inner_divmod, vm)),
-            power: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, float_pow, vm)),
-            negative: atomic_func!(|num, vm| {
+            add: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a + b, vm)),
+            subtract: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a - b, vm)),
+            multiply: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a * b, vm)),
+            remainder: Some(|num, other, vm| PyFloat::number_op(num, other, inner_mod, vm)),
+            divmod: Some(|num, other, vm| PyFloat::number_op(num, other, inner_divmod, vm)),
+            power: Some(|num, other, vm| PyFloat::number_op(num, other, float_pow, vm)),
+            negative: Some(|num, vm| {
                 let value = PyFloat::number_downcast(num).value;
                 (-value).to_pyresult(vm)
             }),
-            positive: atomic_func!(
-                |num, vm| PyFloat::number_downcast_exact(num, vm).to_pyresult(vm)
-            ),
-            absolute: atomic_func!(|num, vm| {
+            positive: Some(|num, vm| PyFloat::number_downcast_exact(num, vm).to_pyresult(vm)),
+            absolute: Some(|num, vm| {
                 let value = PyFloat::number_downcast(num).value;
                 value.abs().to_pyresult(vm)
             }),
-            boolean: atomic_func!(|num, _vm| Ok(PyFloat::number_downcast(num).value.is_zero())),
-            int: atomic_func!(|num, vm| {
+            boolean: Some(|num, _vm| Ok(PyFloat::number_downcast(num).value.is_zero())),
+            int: Some(|num, vm| {
                 let value = PyFloat::number_downcast(num).value;
                 try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x))
             }),
-            float: atomic_func!(|num, vm| Ok(PyFloat::number_downcast_exact(num, vm))),
-            floor_divide: atomic_func!(|num, other, vm| {
-                PyFloat::number_op(num, other, inner_floordiv, vm)
-            }),
-            true_divide: atomic_func!(|num, other, vm| {
-                PyFloat::number_op(num, other, inner_div, vm)
-            }),
+            float: Some(|num, vm| Ok(PyFloat::number_downcast_exact(num, vm))),
+            floor_divide: Some(|num, other, vm| PyFloat::number_op(num, other, inner_floordiv, vm)),
+            true_divide: Some(|num, other, vm| PyFloat::number_op(num, other, inner_div, vm)),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
         &AS_NUMBER
diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs
index 7e9501f693..9ca1416d69 100644
--- a/vm/src/builtins/int.rs
+++ b/vm/src/builtins/int.rs
@@ -1,6 +1,5 @@
 use super::{float, PyByteArray, PyBytes, PyStr, PyType, PyTypeRef};
 use crate::{
-    atomic_func,
     builtins::PyStrRef,
     bytesinner::PyBytesInner,
     class::PyClassImpl,
@@ -730,51 +729,30 @@ impl Hashable for PyInt {
 impl AsNumber for PyInt {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            add: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)),
-            subtract: atomic_func!(|num, other, vm| PyInt::number_op(
-                num,
-                other,
-                |a, b, _vm| a - b,
-                vm
-            )),
-            multiply: atomic_func!(|num, other, vm| PyInt::number_op(
-                num,
-                other,
-                |a, b, _vm| a * b,
-                vm
-            )),
-            remainder: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)),
-            divmod: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)),
-            power: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)),
-            negative: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value)
-                .neg()
-                .to_pyresult(vm)),
-            positive: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())),
-            absolute: atomic_func!(|num, vm| PyInt::number_downcast(num)
-                .value
-                .abs()
-                .to_pyresult(vm)),
-            boolean: atomic_func!(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())),
-            invert: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value)
-                .not()
-                .to_pyresult(vm)),
-            lshift: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)),
-            rshift: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)),
-            and: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)),
-            xor: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)),
-            or: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)),
-            int: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
-            float: atomic_func!(|num, vm| {
+            add: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)),
+            subtract: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a - b, vm)),
+            multiply: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a * b, vm)),
+            remainder: Some(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)),
+            divmod: Some(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)),
+            power: Some(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)),
+            negative: Some(|num, vm| (&PyInt::number_downcast(num).value).neg().to_pyresult(vm)),
+            positive: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())),
+            absolute: Some(|num, vm| PyInt::number_downcast(num).value.abs().to_pyresult(vm)),
+            boolean: Some(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())),
+            invert: Some(|num, vm| (&PyInt::number_downcast(num).value).not().to_pyresult(vm)),
+            lshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)),
+            rshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)),
+            and: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)),
+            xor: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)),
+            or: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)),
+            int: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
+            float: Some(|num, vm| {
                 let zelf = PyInt::number_downcast(num);
                 try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x))
             }),
-            floor_divide: atomic_func!(|num, other, vm| {
-                PyInt::number_op(num, other, inner_floordiv, vm)
-            }),
-            true_divide: atomic_func!(|num, other, vm| {
-                PyInt::number_op(num, other, inner_truediv, vm)
-            }),
-            index: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
+            floor_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_floordiv, vm)),
+            true_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_truediv, vm)),
+            index: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
         &AS_NUMBER
diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index 023abdd246..293cd1d50a 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -1,5 +1,3 @@
-use once_cell::sync::Lazy;
-
 use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef};
 use crate::{
     atomic_func,
@@ -10,6 +8,7 @@ use crate::{
     types::{AsMapping, AsNumber, AsSequence, Comparable, Constructor, Iterable, PyComparisonOp},
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
 };
+use once_cell::sync::Lazy;
 
 #[pyclass(module = false, name = "mappingproxy")]
 #[derive(Debug)]
@@ -233,10 +232,10 @@ impl AsSequence for PyMappingProxy {
 impl AsNumber for PyMappingProxy {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, args, vm| {
+            or: Some(|num, args, vm| {
                 PyMappingProxy::number_downcast(num).or(args.to_pyobject(vm), vm)
             }),
-            inplace_or: atomic_func!(|num, args, vm| {
+            inplace_or: Some(|num, args, vm| {
                 PyMappingProxy::number_downcast(num).ior(args.to_pyobject(vm), vm)
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs
index 0814e17644..df0f240789 100644
--- a/vm/src/builtins/set.rs
+++ b/vm/src/builtins/set.rs
@@ -806,27 +806,27 @@ impl Iterable for PySet {
 impl AsNumber for PySet {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            subtract: atomic_func!(|number, other, vm| {
+            subtract: Some(|number, other, vm| {
                 PySet::number_downcast(number)
                     .sub(other.to_owned(), vm)
                     .to_pyresult(vm)
             }),
-            and: atomic_func!(|number, other, vm| {
+            and: Some(|number, other, vm| {
                 PySet::number_downcast(number)
                     .and(other.to_owned(), vm)
                     .to_pyresult(vm)
             }),
-            xor: atomic_func!(|number, other, vm| {
+            xor: Some(|number, other, vm| {
                 PySet::number_downcast(number)
                     .xor(other.to_owned(), vm)
                     .to_pyresult(vm)
             }),
-            or: atomic_func!(|number, other, vm| {
+            or: Some(|number, other, vm| {
                 PySet::number_downcast(number)
                     .or(other.to_owned(), vm)
                     .to_pyresult(vm)
             }),
-            inplace_subtract: atomic_func!(|number, other, vm| {
+            inplace_subtract: Some(|number, other, vm| {
                 PySet::isub(
                     PySet::number_downcast(number).to_owned(),
                     AnySet::try_from_object(vm, other.to_owned())?,
@@ -834,7 +834,7 @@ impl AsNumber for PySet {
                 )
                 .to_pyresult(vm)
             }),
-            inplace_and: atomic_func!(|number, other, vm| {
+            inplace_and: Some(|number, other, vm| {
                 PySet::iand(
                     PySet::number_downcast(number).to_owned(),
                     AnySet::try_from_object(vm, other.to_owned())?,
@@ -842,7 +842,7 @@ impl AsNumber for PySet {
                 )
                 .to_pyresult(vm)
             }),
-            inplace_xor: atomic_func!(|number, other, vm| {
+            inplace_xor: Some(|number, other, vm| {
                 PySet::ixor(
                     PySet::number_downcast(number).to_owned(),
                     AnySet::try_from_object(vm, other.to_owned())?,
@@ -850,7 +850,7 @@ impl AsNumber for PySet {
                 )
                 .to_pyresult(vm)
             }),
-            inplace_or: atomic_func!(|number, other, vm| {
+            inplace_or: Some(|number, other, vm| {
                 PySet::ior(
                     PySet::number_downcast(number).to_owned(),
                     AnySet::try_from_object(vm, other.to_owned())?,
@@ -1106,22 +1106,22 @@ impl Iterable for PyFrozenSet {
 impl AsNumber for PyFrozenSet {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            subtract: atomic_func!(|number, other, vm| {
+            subtract: Some(|number, other, vm| {
                 PyFrozenSet::number_downcast(number)
                     .sub(other.to_owned(), vm)
                     .to_pyresult(vm)
             }),
-            and: atomic_func!(|number, other, vm| {
+            and: Some(|number, other, vm| {
                 PyFrozenSet::number_downcast(number)
                     .and(other.to_owned(), vm)
                     .to_pyresult(vm)
             }),
-            xor: atomic_func!(|number, other, vm| {
+            xor: Some(|number, other, vm| {
                 PyFrozenSet::number_downcast(number)
                     .xor(other.to_owned(), vm)
                     .to_pyresult(vm)
             }),
-            or: atomic_func!(|number, other, vm| {
+            or: Some(|number, other, vm| {
                 PyFrozenSet::number_downcast(number)
                     .or(other.to_owned(), vm)
                     .to_pyresult(vm)
diff --git a/vm/src/builtins/singletons.rs b/vm/src/builtins/singletons.rs
index 65081a0d46..ae01048a87 100644
--- a/vm/src/builtins/singletons.rs
+++ b/vm/src/builtins/singletons.rs
@@ -1,14 +1,12 @@
-use once_cell::sync::Lazy;
-
 use super::{PyType, PyTypeRef};
 use crate::{
-    atomic_func,
     class::PyClassImpl,
     convert::ToPyObject,
     protocol::PyNumberMethods,
     types::{AsNumber, Constructor},
     Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
 };
+use once_cell::sync::Lazy;
 
 #[pyclass(module = false, name = "NoneType")]
 #[derive(Debug)]
@@ -61,7 +59,7 @@ impl PyNone {
 impl AsNumber for PyNone {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            boolean: atomic_func!(|_number, _vm| Ok(false)),
+            boolean: Some(|_number, _vm| Ok(false)),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
         &AS_NUMBER
diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs
index 514db2bfc5..5dea618e30 100644
--- a/vm/src/builtins/str.rs
+++ b/vm/src/builtins/str.rs
@@ -1302,7 +1302,7 @@ impl AsMapping for PyStr {
 impl AsNumber for PyStr {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            remainder: atomic_func!(|number, other, vm| {
+            remainder: Some(|number, other, vm| {
                 PyStr::number_downcast(number)
                     .modulo(other.to_owned(), vm)
                     .to_pyresult(vm)
diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs
index 8aef931687..3e54a297c0 100644
--- a/vm/src/builtins/type.rs
+++ b/vm/src/builtins/type.rs
@@ -3,7 +3,6 @@ use super::{
     PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
 };
 use crate::{
-    atomic_func,
     builtins::{
         descriptor::{
             DescrObject, MemberDef, MemberDescrObject, MemberGetter, MemberKind, MemberSetter,
@@ -1062,7 +1061,7 @@ impl Callable for PyType {
 impl AsNumber for PyType {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, other, vm| {
+            or: Some(|num, other, vm| {
                 or_(num.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs
index ad1899a04c..66242cbbdc 100644
--- a/vm/src/builtins/union.rs
+++ b/vm/src/builtins/union.rs
@@ -1,5 +1,3 @@
-use once_cell::sync::Lazy;
-
 use super::{genericalias, type_};
 use crate::{
     atomic_func,
@@ -13,6 +11,7 @@ use crate::{
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
     VirtualMachine,
 };
+use once_cell::sync::Lazy;
 use std::fmt;
 
 const CLS_ATTRS: &[&str] = &["__module__"];
@@ -258,7 +257,7 @@ impl AsMapping for PyUnion {
 impl AsNumber for PyUnion {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: atomic_func!(|num, other, vm| {
+            or: Some(|num, other, vm| {
                 PyUnion::or(num.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs
index 7cde1a8757..9c7aa71073 100644
--- a/vm/src/protocol/mod.rs
+++ b/vm/src/protocol/mod.rs
@@ -10,5 +10,7 @@ pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, V
 pub use callable::PyCallable;
 pub use iter::{PyIter, PyIterIter, PyIterReturn};
 pub use mapping::{PyMapping, PyMappingMethods};
-pub use number::{PyNumber, PyNumberBinaryOpSlot, PyNumberMethods};
+pub use number::{
+    PyNumber, PyNumberBinaryFunc, PyNumberBinaryOpSlot, PyNumberMethods, PyNumberUnaryFunc,
+};
 pub use sequence::{PySequence, PySequenceMethods};
diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs
index a7e9efd56d..6841c81918 100644
--- a/vm/src/protocol/number.rs
+++ b/vm/src/protocol/number.rs
@@ -7,11 +7,9 @@ use crate::{
     AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject,
     VirtualMachine,
 };
-use crossbeam_utils::atomic::AtomicCell;
 
-type UnaryFunc<R = PyObjectRef> = AtomicCell<Option<fn(PyNumber, &VirtualMachine) -> PyResult<R>>>;
-type BinaryFunc<R = PyObjectRef> =
-    AtomicCell<Option<fn(PyNumber, &PyObject, &VirtualMachine) -> PyResult<R>>>;
+pub type PyNumberUnaryFunc<R = PyObjectRef> = fn(PyNumber, &VirtualMachine) -> PyResult<R>;
+pub type PyNumberBinaryFunc = fn(PyNumber, &PyObject, &VirtualMachine) -> PyResult;
 
 impl PyObject {
     #[inline]
@@ -113,45 +111,45 @@ pub struct PyNumberMethods {
     /* Number implementations must check *both*
     arguments for proper type and implement the necessary conversions
     in the slot functions themselves. */
-    pub add: BinaryFunc,
-    pub subtract: BinaryFunc,
-    pub multiply: BinaryFunc,
-    pub remainder: BinaryFunc,
-    pub divmod: BinaryFunc,
-    pub power: BinaryFunc,
-    pub negative: UnaryFunc,
-    pub positive: UnaryFunc,
-    pub absolute: UnaryFunc,
-    pub boolean: UnaryFunc<bool>,
-    pub invert: UnaryFunc,
-    pub lshift: BinaryFunc,
-    pub rshift: BinaryFunc,
-    pub and: BinaryFunc,
-    pub xor: BinaryFunc,
-    pub or: BinaryFunc,
-    pub int: UnaryFunc<PyRef<PyInt>>,
-    pub float: UnaryFunc<PyRef<PyFloat>>,
+    pub add: Option<PyNumberBinaryFunc>,
+    pub subtract: Option<PyNumberBinaryFunc>,
+    pub multiply: Option<PyNumberBinaryFunc>,
+    pub remainder: Option<PyNumberBinaryFunc>,
+    pub divmod: Option<PyNumberBinaryFunc>,
+    pub power: Option<PyNumberBinaryFunc>,
+    pub negative: Option<PyNumberUnaryFunc>,
+    pub positive: Option<PyNumberUnaryFunc>,
+    pub absolute: Option<PyNumberUnaryFunc>,
+    pub boolean: Option<PyNumberUnaryFunc<bool>>,
+    pub invert: Option<PyNumberUnaryFunc>,
+    pub lshift: Option<PyNumberBinaryFunc>,
+    pub rshift: Option<PyNumberBinaryFunc>,
+    pub and: Option<PyNumberBinaryFunc>,
+    pub xor: Option<PyNumberBinaryFunc>,
+    pub or: Option<PyNumberBinaryFunc>,
+    pub int: Option<PyNumberUnaryFunc<PyRef<PyInt>>>,
+    pub float: Option<PyNumberUnaryFunc<PyRef<PyFloat>>>,
 
-    pub inplace_add: BinaryFunc,
-    pub inplace_subtract: BinaryFunc,
-    pub inplace_multiply: BinaryFunc,
-    pub inplace_remainder: BinaryFunc,
-    pub inplace_power: BinaryFunc,
-    pub inplace_lshift: BinaryFunc,
-    pub inplace_rshift: BinaryFunc,
-    pub inplace_and: BinaryFunc,
-    pub inplace_xor: BinaryFunc,
-    pub inplace_or: BinaryFunc,
+    pub inplace_add: Option<PyNumberBinaryFunc>,
+    pub inplace_subtract: Option<PyNumberBinaryFunc>,
+    pub inplace_multiply: Option<PyNumberBinaryFunc>,
+    pub inplace_remainder: Option<PyNumberBinaryFunc>,
+    pub inplace_power: Option<PyNumberBinaryFunc>,
+    pub inplace_lshift: Option<PyNumberBinaryFunc>,
+    pub inplace_rshift: Option<PyNumberBinaryFunc>,
+    pub inplace_and: Option<PyNumberBinaryFunc>,
+    pub inplace_xor: Option<PyNumberBinaryFunc>,
+    pub inplace_or: Option<PyNumberBinaryFunc>,
 
-    pub floor_divide: BinaryFunc,
-    pub true_divide: BinaryFunc,
-    pub inplace_floor_divide: BinaryFunc,
-    pub inplace_true_divide: BinaryFunc,
+    pub floor_divide: Option<PyNumberBinaryFunc>,
+    pub true_divide: Option<PyNumberBinaryFunc>,
+    pub inplace_floor_divide: Option<PyNumberBinaryFunc>,
+    pub inplace_true_divide: Option<PyNumberBinaryFunc>,
 
-    pub index: UnaryFunc<PyRef<PyInt>>,
+    pub index: Option<PyNumberUnaryFunc<PyRef<PyInt>>>,
 
-    pub matrix_multiply: BinaryFunc,
-    pub inplace_matrix_multiply: BinaryFunc,
+    pub matrix_multiply: Option<PyNumberBinaryFunc>,
+    pub inplace_matrix_multiply: Option<PyNumberBinaryFunc>,
 }
 
 impl PyNumberMethods {
@@ -159,44 +157,47 @@ impl PyNumberMethods {
     // TODO: weak order read for performance
     #[allow(clippy::declare_interior_mutable_const)]
     pub const NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods {
-        add: AtomicCell::new(None),
-        subtract: AtomicCell::new(None),
-        multiply: AtomicCell::new(None),
-        remainder: AtomicCell::new(None),
-        divmod: AtomicCell::new(None),
-        power: AtomicCell::new(None),
-        negative: AtomicCell::new(None),
-        positive: AtomicCell::new(None),
-        absolute: AtomicCell::new(None),
-        boolean: AtomicCell::new(None),
-        invert: AtomicCell::new(None),
-        lshift: AtomicCell::new(None),
-        rshift: AtomicCell::new(None),
-        and: AtomicCell::new(None),
-        xor: AtomicCell::new(None),
-        or: AtomicCell::new(None),
-        int: AtomicCell::new(None),
-        float: AtomicCell::new(None),
-        inplace_add: AtomicCell::new(None),
-        inplace_subtract: AtomicCell::new(None),
-        inplace_multiply: AtomicCell::new(None),
-        inplace_remainder: AtomicCell::new(None),
-        inplace_power: AtomicCell::new(None),
-        inplace_lshift: AtomicCell::new(None),
-        inplace_rshift: AtomicCell::new(None),
-        inplace_and: AtomicCell::new(None),
-        inplace_xor: AtomicCell::new(None),
-        inplace_or: AtomicCell::new(None),
-        floor_divide: AtomicCell::new(None),
-        true_divide: AtomicCell::new(None),
-        inplace_floor_divide: AtomicCell::new(None),
-        inplace_true_divide: AtomicCell::new(None),
-        index: AtomicCell::new(None),
-        matrix_multiply: AtomicCell::new(None),
-        inplace_matrix_multiply: AtomicCell::new(None),
+        add: None,
+        subtract: None,
+        multiply: None,
+        remainder: None,
+        divmod: None,
+        power: None,
+        negative: None,
+        positive: None,
+        absolute: None,
+        boolean: None,
+        invert: None,
+        lshift: None,
+        rshift: None,
+        and: None,
+        xor: None,
+        or: None,
+        int: None,
+        float: None,
+        inplace_add: None,
+        inplace_subtract: None,
+        inplace_multiply: None,
+        inplace_remainder: None,
+        inplace_power: None,
+        inplace_lshift: None,
+        inplace_rshift: None,
+        inplace_and: None,
+        inplace_xor: None,
+        inplace_or: None,
+        floor_divide: None,
+        true_divide: None,
+        inplace_floor_divide: None,
+        inplace_true_divide: None,
+        index: None,
+        matrix_multiply: None,
+        inplace_matrix_multiply: None,
     };
 
-    pub fn get_binary_op(&self, op_slot: &PyNumberBinaryOpSlot) -> PyResult<&BinaryFunc> {
+    pub fn get_binary_op(
+        &self,
+        op_slot: &PyNumberBinaryOpSlot,
+    ) -> PyResult<&Option<PyNumberBinaryFunc>> {
         use PyNumberBinaryOpSlot::*;
         let binary_op = match op_slot {
             Add => &self.add,
@@ -287,16 +288,16 @@ impl PyNumber<'_> {
         self.methods
     }
 
-    pub fn get_binary_op(&self, op_slot: &PyNumberBinaryOpSlot) -> PyResult<&BinaryFunc> {
+    pub fn get_binary_op(
+        &self,
+        op_slot: &PyNumberBinaryOpSlot,
+    ) -> PyResult<&Option<PyNumberBinaryFunc>> {
         self.methods().get_binary_op(op_slot)
     }
 
     // PyNumber_Check
     pub fn check(obj: &PyObject) -> bool {
-        let Some(methods) = Self::find_methods(obj) else {
-            return false;
-        };
-        let methods = methods.as_ref();
+        let methods = &obj.class().slots.number;
         methods.int.load().is_some()
             || methods.index.load().is_some()
             || methods.float.load().is_some()
@@ -305,12 +306,12 @@ impl PyNumber<'_> {
 
     // PyIndex_Check
     pub fn is_index(&self) -> bool {
-        self.methods().index.load().is_some()
+        self.obj.class().slots.number.index.load().is_some()
     }
 
     #[inline]
     pub fn int(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
-        self.methods().int.load().map(|f| {
+        self.obj.class().slots.number.int.load().map(|f| {
             let ret = f(self, vm)?;
             let value = if !ret.class().is(PyInt::class(vm)) {
                 warnings::warn(
@@ -334,7 +335,7 @@ impl PyNumber<'_> {
 
     #[inline]
     pub fn index(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
-        self.methods().index.load().map(|f| {
+        self.obj.class().slots.number.index.load().map(|f| {
             let ret = f(self, vm)?;
             let value = if !ret.class().is(PyInt::class(vm)) {
                 warnings::warn(
@@ -358,7 +359,7 @@ impl PyNumber<'_> {
 
     #[inline]
     pub fn float(self, vm: &VirtualMachine) -> Option<PyResult<PyRef<PyFloat>>> {
-        self.methods().float.load().map(|f| {
+        self.obj.class().slots.number.float.load().map(|f| {
             let ret = f(self, vm)?;
             let value = if !ret.class().is(PyFloat::class(vm)) {
                 warnings::warn(
diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index 72c021ec41..01900daeb3 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -6,8 +6,8 @@ use crate::{
     function::{Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
     identifier,
     protocol::{
-        PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberBinaryOpSlot,
-        PyNumberMethods, PySequence, PySequenceMethods,
+        PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberBinaryFunc,
+        PyNumberBinaryOpSlot, PyNumberMethods, PyNumberUnaryFunc, PySequence, PySequenceMethods,
     },
     vm::Context,
     AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -102,72 +102,69 @@ impl std::fmt::Debug for PyTypeSlots {
     }
 }
 
-pub(crate) type NumberUnaryFunc<R = PyObjectRef> = fn(PyNumber, &VirtualMachine) -> PyResult<R>;
-pub(crate) type NumberBinaryFunc = fn(PyNumber, &PyObject, &VirtualMachine) -> PyResult;
-
 #[derive(Default)]
 pub struct PyNumberSlots {
-    pub add: AtomicCell<Option<NumberBinaryFunc>>,
-    pub subtract: AtomicCell<Option<NumberBinaryFunc>>,
-    pub multiply: AtomicCell<Option<NumberBinaryFunc>>,
-    pub remainder: AtomicCell<Option<NumberBinaryFunc>>,
-    pub divmod: AtomicCell<Option<NumberBinaryFunc>>,
-    pub power: AtomicCell<Option<NumberBinaryFunc>>,
-    pub negative: AtomicCell<Option<NumberUnaryFunc>>,
-    pub positive: AtomicCell<Option<NumberUnaryFunc>>,
-    pub absolute: AtomicCell<Option<NumberUnaryFunc>>,
-    pub boolean: AtomicCell<Option<NumberUnaryFunc<bool>>>,
-    pub invert: AtomicCell<Option<NumberUnaryFunc>>,
-    pub lshift: AtomicCell<Option<NumberBinaryFunc>>,
-    pub rshift: AtomicCell<Option<NumberBinaryFunc>>,
-    pub and: AtomicCell<Option<NumberBinaryFunc>>,
-    pub xor: AtomicCell<Option<NumberBinaryFunc>>,
-    pub or: AtomicCell<Option<NumberBinaryFunc>>,
-    pub int: AtomicCell<Option<NumberUnaryFunc<PyRef<PyInt>>>>,
-    pub float: AtomicCell<Option<NumberUnaryFunc<PyRef<PyFloat>>>>,
-
-    pub right_add: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_subtract: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_multiply: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_remainder: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_divmod: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_power: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_lshift: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_rshift: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_and: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_xor: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_or: AtomicCell<Option<NumberBinaryFunc>>,
-
-    pub inplace_add: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_subtract: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_multiply: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_remainder: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_power: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_lshift: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_rshift: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_and: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_xor: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_or: AtomicCell<Option<NumberBinaryFunc>>,
-
-    pub floor_divide: AtomicCell<Option<NumberBinaryFunc>>,
-    pub true_divide: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_floor_divide: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_true_divide: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_floor_divide: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_true_divide: AtomicCell<Option<NumberBinaryFunc>>,
-
-    pub index: AtomicCell<Option<NumberUnaryFunc<PyRef<PyInt>>>>,
-
-    pub matrix_multiply: AtomicCell<Option<NumberBinaryFunc>>,
-    pub right_matrix_multiply: AtomicCell<Option<NumberBinaryFunc>>,
-    pub inplace_matrix_multiply: AtomicCell<Option<NumberBinaryFunc>>,
+    pub add: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub divmod: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub power: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub negative: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub positive: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub absolute: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub boolean: AtomicCell<Option<PyNumberUnaryFunc<bool>>>,
+    pub invert: AtomicCell<Option<PyNumberUnaryFunc>>,
+    pub lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub and: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub xor: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub or: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub int: AtomicCell<Option<PyNumberUnaryFunc<PyRef<PyInt>>>>,
+    pub float: AtomicCell<Option<PyNumberUnaryFunc<PyRef<PyFloat>>>>,
+
+    pub right_add: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_divmod: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_power: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_and: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_xor: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_or: AtomicCell<Option<PyNumberBinaryFunc>>,
+
+    pub inplace_add: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_subtract: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_remainder: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_power: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_lshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_rshift: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_and: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_xor: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_or: AtomicCell<Option<PyNumberBinaryFunc>>,
+
+    pub floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_floor_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_true_divide: AtomicCell<Option<PyNumberBinaryFunc>>,
+
+    pub index: AtomicCell<Option<PyNumberUnaryFunc<PyRef<PyInt>>>>,
+
+    pub matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub right_matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
+    pub inplace_matrix_multiply: AtomicCell<Option<PyNumberBinaryFunc>>,
 }
 
 impl PyNumberSlots {
     pub fn get_left_binary_op(
         &self,
         op_slot: &PyNumberBinaryOpSlot,
-    ) -> PyResult<Option<NumberBinaryFunc>> {
+    ) -> PyResult<Option<PyNumberBinaryFunc>> {
         use PyNumberBinaryOpSlot::*;
         let binary_op = match op_slot {
             Add => self.add.load(),
@@ -204,7 +201,7 @@ impl PyNumberSlots {
     pub fn get_right_binary_op(
         &self,
         op_slot: &PyNumberBinaryOpSlot,
-    ) -> PyResult<Option<NumberBinaryFunc>> {
+    ) -> PyResult<Option<PyNumberBinaryFunc>> {
         use PyNumberBinaryOpSlot::*;
         let binary_op = match op_slot {
             Add => self.right_add.load(),
@@ -619,16 +616,13 @@ impl PyType {
                 toggle_slot!(del, del_wrapper);
             }
             _ if name == identifier!(ctx, __int__) => {
-                toggle_ext_func!(number_methods, int, int_wrapper);
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, int, int_wrapper);
             }
             _ if name == identifier!(ctx, __index__) => {
-                toggle_ext_func!(number_methods, index, index_wrapper);
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, index, index_wrapper);
             }
             _ if name == identifier!(ctx, __float__) => {
-                toggle_ext_func!(number_methods, float, float_wrapper);
-                update_pointer_slot!(as_number, number_methods);
+                toggle_subslot!(number, float, float_wrapper);
             }
             _ if name == identifier!(ctx, __add__) => {
                 toggle_subslot!(number, add, number_binary_op_wrapper!(__add__));
@@ -1284,18 +1278,20 @@ pub trait AsSequence: PyPayload {
 
 macro_rules! extend_number_slot {
     ($slots:ident, $methods:ident, $method:ident, $right_method:ident, $op_slot:ident) => {
-        if $methods.$method.load().is_some() {
-            $slots.number.$method.store($methods.$method.load());
+        if $methods.$method.is_some() {
+            $slots.number.$method.store($methods.$method);
             $slots.number.$right_method.store(Some(|num, other, vm| {
-                num.get_binary_op(&PyNumberBinaryOpSlot::$op_slot)?
-                    .load()
-                    .unwrap()(other.to_number(), num.obj, vm)
+                num.get_binary_op(&PyNumberBinaryOpSlot::$op_slot)?.unwrap()(
+                    other.to_number(),
+                    num.obj,
+                    vm,
+                )
             }));
         }
     };
     ($slots:ident, $methods:ident, $method:ident) => {
-        if $methods.$method.load().is_some() {
-            $slots.number.$method.store($methods.$method.load());
+        if $methods.$method.is_some() {
+            $slots.number.$method.store($methods.$method);
         }
     };
 }

From fc6b86b24b11880e9267ade21f1d6d8c1a47f3c0 Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Wed, 8 Mar 2023 03:59:11 +0900
Subject: [PATCH 03/11] Temp: Implement Number Protocol for PyBool

---
 vm/src/builtins/bool.rs | 52 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 50 insertions(+), 2 deletions(-)

diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs
index ed21290f65..010212e67e 100644
--- a/vm/src/builtins/bool.rs
+++ b/vm/src/builtins/bool.rs
@@ -1,11 +1,17 @@
 use super::{PyInt, PyStrRef, PyType, PyTypeRef};
 use crate::{
-    class::PyClassImpl, convert::ToPyObject, function::OptionalArg, identifier, types::Constructor,
+    class::PyClassImpl,
+    convert::{ToPyObject, ToPyResult},
+    function::OptionalArg,
+    identifier,
+    protocol::PyNumberMethods,
+    types::{AsNumber, Constructor},
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject,
     VirtualMachine,
 };
 use num_bigint::Sign;
 use num_traits::Zero;
+use once_cell::sync::Lazy;
 use std::fmt::{Debug, Formatter};
 
 impl ToPyObject for bool {
@@ -102,7 +108,7 @@ impl Constructor for PyBool {
     }
 }
 
-#[pyclass(with(Constructor))]
+#[pyclass(with(Constructor, AsNumber))]
 impl PyBool {
     #[pymethod(magic)]
     fn repr(zelf: bool, vm: &VirtualMachine) -> PyStrRef {
@@ -166,6 +172,48 @@ impl PyBool {
     }
 }
 
+macro_rules! int_method {
+    ($method:ident) => {
+        PyInt::as_number().$method
+    };
+}
+
+impl AsNumber for PyBool {
+    fn as_number() -> &'static PyNumberMethods {
+        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
+            add: int_method!(add),
+            subtract: int_method!(subtract),
+            multiply: int_method!(multiply),
+            remainder: int_method!(remainder),
+            divmod: int_method!(divmod),
+            power: int_method!(power),
+            negative: int_method!(negative),
+            positive: int_method!(positive),
+            absolute: int_method!(absolute),
+            boolean: int_method!(boolean),
+            invert: int_method!(invert),
+            lshift: int_method!(lshift),
+            rshift: int_method!(rshift),
+            and: Some(|number, other, vm| {
+                PyBool::and(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
+            }),
+            xor: Some(|number, other, vm| {
+                PyBool::xor(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
+            }),
+            or: Some(|number, other, vm| {
+                PyBool::or(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm)
+            }),
+            int: int_method!(int),
+            float: int_method!(float),
+            floor_divide: int_method!(floor_divide),
+            true_divide: int_method!(true_divide),
+            index: int_method!(index),
+            ..PyNumberMethods::NOT_IMPLEMENTED
+        });
+        &AS_NUMBER
+    }
+}
+
 pub(crate) fn init(context: &Context) {
     PyBool::extend_class(context, context.types.bool_type);
 }

From 6529fa9b8b8c459dffc8713df1565ef35b29783a Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Thu, 9 Mar 2023 07:02:02 +0900
Subject: [PATCH 04/11] Temp: Implement Number Protocol for PyArray

---
 stdlib/src/array.rs | 88 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 85 insertions(+), 3 deletions(-)

diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs
index 69a317c828..0ed9a6f134 100644
--- a/stdlib/src/array.rs
+++ b/stdlib/src/array.rs
@@ -51,7 +51,7 @@ mod array {
             },
             protocol::{
                 BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, PyIterReturn,
-                PyMappingMethods, PySequenceMethods,
+                PyMappingMethods, PyNumberMethods, PySequenceMethods,
             },
             sequence::{OptionalRangeArgs, SequenceExt, SequenceMutExt},
             sliceable::{
@@ -59,7 +59,7 @@ mod array {
                 SliceableSequenceOp,
             },
             types::{
-                AsBuffer, AsMapping, AsSequence, Comparable, Constructor, IterNext,
+                AsBuffer, AsMapping, AsNumber, AsSequence, Comparable, Constructor, IterNext,
                 IterNextIterable, Iterable, PyComparisonOp,
             },
             AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -67,6 +67,7 @@ mod array {
     };
     use itertools::Itertools;
     use num_traits::ToPrimitive;
+    use once_cell::sync::Lazy;
     use std::{cmp::Ordering, fmt, os::raw};
 
     macro_rules! def_array_enum {
@@ -710,7 +711,7 @@ mod array {
 
     #[pyclass(
         flags(BASETYPE),
-        with(Comparable, AsBuffer, AsMapping, Iterable, Constructor)
+        with(Comparable, AsBuffer, AsMapping, AsNumber, Iterable, Constructor)
     )]
     impl PyArray {
         fn read(&self) -> PyRwLockReadGuard<'_, ArrayContentType> {
@@ -1323,6 +1324,87 @@ mod array {
         }
     }
 
+    impl AsNumber for PyArray {
+        fn as_number() -> &'static PyNumberMethods {
+            static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
+                add: Some(|number, other, vm| {
+                    if let Some(number) = number.obj.downcast_ref::<PyArray>() {
+                        number.add(other.to_owned(), vm).to_pyresult(vm)
+                    } else {
+                        Ok(vm.ctx.not_implemented())
+                    }
+                }),
+                multiply: Some(|number, other, vm| {
+                    if let Some(number) = number.obj.downcast_ref::<PyArray>() {
+                        number
+                            .mul(
+                                other.try_index(vm)?.as_bigint().to_isize().ok_or_else(|| {
+                                    vm.new_overflow_error("repeated array is too long".to_owned())
+                                })?,
+                                vm,
+                            )
+                            .to_pyresult(vm)
+                    } else if let Some(other) = other.downcast_ref::<PyArray>() {
+                        other
+                            .mul(
+                                number
+                                    .obj
+                                    .try_index(vm)?
+                                    .as_bigint()
+                                    .to_isize()
+                                    .ok_or_else(|| {
+                                        vm.new_overflow_error(
+                                            "repeated array is too long".to_owned(),
+                                        )
+                                    })?,
+                                vm,
+                            )
+                            .to_pyresult(vm)
+                    } else {
+                        Ok(vm.ctx.not_implemented())
+                    }
+                }),
+                inplace_add: Some(|number, other, vm| {
+                    if let Some(number) = number.obj.downcast_ref::<PyArray>() {
+                        PyArray::iadd(number.to_owned(), other.to_owned(), vm).to_pyresult(vm)
+                    } else {
+                        Ok(vm.ctx.not_implemented())
+                    }
+                }),
+                inplace_multiply: Some(|number, other, vm| {
+                    if let Some(number) = number.obj.downcast_ref::<PyArray>() {
+                        PyArray::imul(
+                            number.to_owned(),
+                            other.try_index(vm)?.as_bigint().to_isize().ok_or_else(|| {
+                                vm.new_overflow_error("repeated array is too long".to_owned())
+                            })?,
+                            vm,
+                        )
+                        .to_pyresult(vm)
+                    } else if let Some(other) = other.downcast_ref::<PyArray>() {
+                        PyArray::imul(
+                            other.to_owned(),
+                            number
+                                .obj
+                                .try_index(vm)?
+                                .as_bigint()
+                                .to_isize()
+                                .ok_or_else(|| {
+                                    vm.new_overflow_error("repeated array is too long".to_owned())
+                                })?,
+                            vm,
+                        )
+                        .to_pyresult(vm)
+                    } else {
+                        Ok(vm.ctx.not_implemented())
+                    }
+                }),
+                ..PyNumberMethods::NOT_IMPLEMENTED
+            });
+            &AS_NUMBER
+        }
+    }
+
     impl AsSequence for PyArray {
         fn as_sequence() -> &'static PySequenceMethods {
             static AS_SEQUENCE: PySequenceMethods = PySequenceMethods {

From 163c47b7b5aceaf867c45b4d06d338c16ab2c027 Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Thu, 9 Mar 2023 14:30:24 +0900
Subject: [PATCH 05/11] Temp: Remove usage of AsNumber::number_downcast from
 binary methods of Number Protocol

---
 vm/src/builtins/bytearray.rs    |   8 +-
 vm/src/builtins/bytes.rs        |   8 +-
 vm/src/builtins/dict.rs         |  19 +++--
 vm/src/builtins/mappingproxy.rs |  12 ++-
 vm/src/builtins/set.rs          | 128 ++++++++++++++++++++------------
 vm/src/builtins/str.rs          |   8 +-
 6 files changed, 117 insertions(+), 66 deletions(-)

diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs
index d9ab7482c9..3a123c8b72 100644
--- a/vm/src/builtins/bytearray.rs
+++ b/vm/src/builtins/bytearray.rs
@@ -861,9 +861,11 @@ impl AsNumber for PyByteArray {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
             remainder: Some(|number, other, vm| {
-                PyByteArray::number_downcast(number)
-                    .mod_(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PyByteArray>() {
+                    number.mod_(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs
index de5d835e14..905deea8e9 100644
--- a/vm/src/builtins/bytes.rs
+++ b/vm/src/builtins/bytes.rs
@@ -631,9 +631,11 @@ impl AsNumber for PyBytes {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
             remainder: Some(|number, other, vm| {
-                PyBytes::number_downcast(number)
-                    .mod_(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PyBytes>() {
+                    number.mod_(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs
index 764f0c130c..666d12983c 100644
--- a/vm/src/builtins/dict.rs
+++ b/vm/src/builtins/dict.rs
@@ -480,14 +480,19 @@ impl AsSequence for PyDict {
 impl AsNumber for PyDict {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
-            or: Some(|num, args, vm| PyDict::number_downcast(num).or(args.to_pyobject(vm), vm)),
+            or: Some(|num, args, vm| {
+                if let Some(num) = num.obj.downcast_ref::<PyDict>() {
+                    PyDict::or(num, args.to_pyobject(vm), vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
+            }),
             inplace_or: Some(|num, args, vm| {
-                PyDict::ior(
-                    PyDict::number_downcast(num).to_owned(),
-                    args.to_pyobject(vm),
-                    vm,
-                )
-                .map(|d| d.into())
+                if let Some(num) = num.obj.downcast_ref::<PyDict>() {
+                    PyDict::ior(num.to_owned(), args.to_pyobject(vm), vm).map(|d| d.into())
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index 293cd1d50a..a7118a230c 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -233,10 +233,18 @@ impl AsNumber for PyMappingProxy {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
             or: Some(|num, args, vm| {
-                PyMappingProxy::number_downcast(num).or(args.to_pyobject(vm), vm)
+                if let Some(num) = num.obj.downcast_ref::<PyMappingProxy>() {
+                    num.or(args.to_pyobject(vm), vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             inplace_or: Some(|num, args, vm| {
-                PyMappingProxy::number_downcast(num).ior(args.to_pyobject(vm), vm)
+                if let Some(num) = num.obj.downcast_ref::<PyMappingProxy>() {
+                    num.ior(args.to_pyobject(vm), vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs
index df0f240789..4ee20fd597 100644
--- a/vm/src/builtins/set.rs
+++ b/vm/src/builtins/set.rs
@@ -807,56 +807,80 @@ impl AsNumber for PySet {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
             subtract: Some(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .sub(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.sub(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             and: Some(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .and(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.and(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             xor: Some(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .xor(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.xor(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             or: Some(|number, other, vm| {
-                PySet::number_downcast(number)
-                    .or(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    number.or(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             inplace_subtract: Some(|number, other, vm| {
-                PySet::isub(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::isub(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             inplace_and: Some(|number, other, vm| {
-                PySet::iand(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::iand(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             inplace_xor: Some(|number, other, vm| {
-                PySet::ixor(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::ixor(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             inplace_or: Some(|number, other, vm| {
-                PySet::ior(
-                    PySet::number_downcast(number).to_owned(),
-                    AnySet::try_from_object(vm, other.to_owned())?,
-                    vm,
-                )
-                .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PySet>() {
+                    PySet::ior(
+                        number.to_owned(),
+                        AnySet::try_from_object(vm, other.to_owned())?,
+                        vm,
+                    )
+                    .to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
@@ -1107,24 +1131,32 @@ impl AsNumber for PyFrozenSet {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
             subtract: Some(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .sub(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.sub(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             and: Some(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .and(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.and(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             xor: Some(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .xor(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.xor(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             or: Some(|number, other, vm| {
-                PyFrozenSet::number_downcast(number)
-                    .or(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PyFrozenSet>() {
+                    number.or(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });
diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs
index 5dea618e30..f45248484b 100644
--- a/vm/src/builtins/str.rs
+++ b/vm/src/builtins/str.rs
@@ -1303,9 +1303,11 @@ impl AsNumber for PyStr {
     fn as_number() -> &'static PyNumberMethods {
         static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
             remainder: Some(|number, other, vm| {
-                PyStr::number_downcast(number)
-                    .modulo(other.to_owned(), vm)
-                    .to_pyresult(vm)
+                if let Some(number) = number.obj.downcast_ref::<PyStr>() {
+                    number.modulo(other.to_owned(), vm).to_pyresult(vm)
+                } else {
+                    Ok(vm.ctx.not_implemented())
+                }
             }),
             ..PyNumberMethods::NOT_IMPLEMENTED
         });

From 53c930b61f3c46b3c0a9b23718c1052f058107a1 Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Thu, 9 Mar 2023 15:37:33 +0900
Subject: [PATCH 06/11] Temp: Implement Number Protocol for PyDictKeys and
 PyDictItems

---
 vm/src/builtins/dict.rs | 128 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 126 insertions(+), 2 deletions(-)

diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs
index 666d12983c..a23108822f 100644
--- a/vm/src/builtins/dict.rs
+++ b/vm/src/builtins/dict.rs
@@ -1092,7 +1092,15 @@ trait ViewSetOps: DictView {
 }
 
 impl ViewSetOps for PyDictKeys {}
-#[pyclass(with(DictView, Constructor, Comparable, Iterable, ViewSetOps, AsSequence))]
+#[pyclass(with(
+    DictView,
+    Constructor,
+    Comparable,
+    Iterable,
+    ViewSetOps,
+    AsSequence,
+    AsNumber
+))]
 impl PyDictKeys {
     #[pymethod(magic)]
     fn contains(zelf: PyRef<Self>, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
@@ -1133,8 +1141,70 @@ impl AsSequence for PyDictKeys {
     }
 }
 
+impl AsNumber for PyDictKeys {
+    fn as_number() -> &'static PyNumberMethods {
+        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
+            subtract: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .difference(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            and: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .intersection(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            xor: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.symmetric_difference(
+                        ArgIterable::try_from_object(vm, args.to_owned())?,
+                        vm,
+                    )?,
+                }
+                .into_pyobject(vm))
+            }),
+            or: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.union(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            ..PyNumberMethods::NOT_IMPLEMENTED
+        });
+        &AS_NUMBER
+    }
+}
+
 impl ViewSetOps for PyDictItems {}
-#[pyclass(with(DictView, Constructor, Comparable, Iterable, ViewSetOps, AsSequence))]
+#[pyclass(with(
+    DictView,
+    Constructor,
+    Comparable,
+    Iterable,
+    ViewSetOps,
+    AsSequence,
+    AsNumber
+))]
 impl PyDictItems {
     #[pymethod(magic)]
     fn contains(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
@@ -1189,6 +1259,60 @@ impl AsSequence for PyDictItems {
     }
 }
 
+impl AsNumber for PyDictItems {
+    fn as_number() -> &'static PyNumberMethods {
+        static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods {
+            subtract: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .difference(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            and: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num
+                        .intersection(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            xor: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.symmetric_difference(
+                        ArgIterable::try_from_object(vm, args.to_owned())?,
+                        vm,
+                    )?,
+                }
+                .into_pyobject(vm))
+            }),
+            or: Some(|num, args, vm| {
+                let num = PySetInner::from_iter(
+                    ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?,
+                    vm,
+                )?;
+                Ok(PySet {
+                    inner: num.union(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?,
+                }
+                .into_pyobject(vm))
+            }),
+            ..PyNumberMethods::NOT_IMPLEMENTED
+        });
+        &AS_NUMBER
+    }
+}
+
 #[pyclass(with(DictView, Constructor, Iterable, AsSequence))]
 impl PyDictValues {
     #[pygetset]

From 30e310c50923afbd058bba3b7c51e182926f58b0 Mon Sep 17 00:00:00 2001
From: Jeong YunWon <jeong@youknowone.org>
Date: Thu, 9 Mar 2023 19:42:19 +0900
Subject: [PATCH 07/11] temporary fill up missing number protocols

---
 derive-impl/src/pyclass.rs |  29 +++++++---
 stdlib/src/array.rs        |   1 +
 vm/src/builtins/type.rs    | 108 +++++++++++++++++++++++++++++--------
 vm/src/stdlib/builtins.rs  |   5 ++
 vm/src/vm/context.rs       |   9 +++-
 5 files changed, 121 insertions(+), 31 deletions(-)

diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs
index 07df8b31f6..45c366746c 100644
--- a/derive-impl/src/pyclass.rs
+++ b/derive-impl/src/pyclass.rs
@@ -548,14 +548,27 @@ where
                     other
                 ),
             };
-            quote_spanned! { ident.span() =>
-                class.set_str_attr(
-                    #py_name,
-                    ctx.make_funcdef(#py_name, Self::#ident)
-                        #doc
-                        #build_func,
-                    ctx,
-                );
+            if py_name.starts_with("__") && py_name.ends_with("__") {
+                let name_ident = Ident::new(&py_name, ident.span());
+                quote_spanned! { ident.span() =>
+                    class.set_attr(
+                        ctx.names.#name_ident,
+                        ctx.make_funcdef(#py_name, Self::#ident)
+                            #doc
+                            #build_func
+                        .into(),
+                    );
+                }
+            } else {
+                quote_spanned! { ident.span() =>
+                    class.set_str_attr(
+                        #py_name,
+                        ctx.make_funcdef(#py_name, Self::#ident)
+                            #doc
+                            #build_func,
+                        ctx,
+                    );
+                }
             }
         };
 
diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs
index 0ed9a6f134..8f8b735b2f 100644
--- a/stdlib/src/array.rs
+++ b/stdlib/src/array.rs
@@ -6,6 +6,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef {
     let array = module
         .get_attr("array", vm)
         .expect("Expect array has array type.");
+    array.init_builtin_number_slots(&vm.ctx);
 
     let collections_abc = vm
         .import("collections.abc", None, 0)
diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs
index 3e54a297c0..75ec71f966 100644
--- a/vm/src/builtins/type.rs
+++ b/vm/src/builtins/type.rs
@@ -186,25 +186,6 @@ impl PyType {
 
         *slots.name.get_mut() = Some(String::from(name));
 
-        #[allow(clippy::mutable_key_type)]
-        let mut slot_name_set = HashSet::new();
-
-        for cls in mro.iter() {
-            for &name in cls.attributes.read().keys() {
-                if name != identifier!(ctx, __new__)
-                    && name.as_str().starts_with("__")
-                    && name.as_str().ends_with("__")
-                {
-                    slot_name_set.insert(name);
-                }
-            }
-        }
-        for &name in attrs.keys() {
-            if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
-                slot_name_set.insert(name);
-            }
-        }
-
         let new_type = PyRef::new_ref(
             PyType {
                 base: Some(base),
@@ -219,9 +200,7 @@ impl PyType {
             None,
         );
 
-        for attr_name in slot_name_set {
-            new_type.update_slot::<true>(attr_name, ctx);
-        }
+        new_type.init_slots(ctx);
 
         let weakref_type = super::PyWeak::static_type();
         for base in &new_type.bases {
@@ -279,6 +258,30 @@ impl PyType {
         Ok(new_type)
     }
 
+    pub(crate) fn init_slots(&self, ctx: &Context) {
+        #[allow(clippy::mutable_key_type)]
+        let mut slot_name_set = std::collections::HashSet::new();
+
+        for cls in self.mro.iter() {
+            for &name in cls.attributes.read().keys() {
+                if name == identifier!(ctx, __new__) {
+                    continue;
+                }
+                if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
+                    slot_name_set.insert(name);
+                }
+            }
+        }
+        for &name in self.attributes.read().keys() {
+            if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
+                slot_name_set.insert(name);
+            }
+        }
+        for attr_name in slot_name_set {
+            self.update_slot::<true>(attr_name, ctx);
+        }
+    }
+
     pub fn slot_name(&self) -> String {
         self.slots.name.read().as_ref().unwrap().to_string()
     }
@@ -1327,3 +1330,64 @@ mod tests {
         );
     }
 }
+
+impl crate::PyObject {
+    // temporary tool to fill missing number protocols for builtin types
+    pub fn init_builtin_number_slots(&self, ctx: &Context) {
+        let typ = self
+            .downcast_ref::<PyType>()
+            .expect("not called from a type");
+        macro_rules! call_update_slot {
+            ($name:ident) => {
+                let id = identifier!(ctx, $name);
+                if typ.has_attr(id) {
+                    typ.update_slot::<true>(identifier!(ctx, $name), ctx);
+                }
+            };
+        }
+        call_update_slot!(__add__);
+        call_update_slot!(__radd__);
+        call_update_slot!(__iadd__);
+        call_update_slot!(__sub__);
+        call_update_slot!(__rsub__);
+        call_update_slot!(__isub__);
+        call_update_slot!(__mul__);
+        call_update_slot!(__rmul__);
+        call_update_slot!(__imul__);
+        call_update_slot!(__mod__);
+        call_update_slot!(__rmod__);
+        call_update_slot!(__imod__);
+        call_update_slot!(__div__);
+        call_update_slot!(__rdiv__);
+        call_update_slot!(__idiv__);
+        call_update_slot!(__divmod__);
+        call_update_slot!(__rdivmod__);
+        call_update_slot!(__pow__);
+        call_update_slot!(__rpow__);
+        call_update_slot!(__ipow__);
+        call_update_slot!(__lshift__);
+        call_update_slot!(__rlshift__);
+        call_update_slot!(__ilshift__);
+        call_update_slot!(__rshift__);
+        call_update_slot!(__rrshift__);
+        call_update_slot!(__irshift__);
+        call_update_slot!(__and__);
+        call_update_slot!(__rand__);
+        call_update_slot!(__iand__);
+        call_update_slot!(__xor__);
+        call_update_slot!(__rxor__);
+        call_update_slot!(__ixor__);
+        call_update_slot!(__or__);
+        call_update_slot!(__ror__);
+        call_update_slot!(__ior__);
+        call_update_slot!(__floordiv__);
+        call_update_slot!(__rfloordiv__);
+        call_update_slot!(__ifloordiv__);
+        call_update_slot!(__truediv__);
+        call_update_slot!(__rtruediv__);
+        call_update_slot!(__itruediv__);
+        call_update_slot!(__matmul__);
+        call_update_slot!(__rmatmul__);
+        call_update_slot!(__imatmul__);
+    }
+}
diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs
index e382546b6e..d25bb404ee 100644
--- a/vm/src/stdlib/builtins.rs
+++ b/vm/src/stdlib/builtins.rs
@@ -948,6 +948,11 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
     crate::protocol::VecBuffer::make_class(&vm.ctx);
 
     builtins::extend_module(vm, &module);
+    use crate::AsObject;
+    ctx.types
+        .generic_alias_type
+        .as_object()
+        .init_builtin_number_slots(&vm.ctx);
 
     let debug_mode: bool = vm.state.settings.optimize == 0;
     extend_module!(vm, module, {
diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs
index 9db628f76d..95b8da260d 100644
--- a/vm/src/vm/context.rs
+++ b/vm/src/vm/context.rs
@@ -78,6 +78,7 @@ declare_const_name! {
     __aenter__,
     __aexit__,
     __aiter__,
+    __alloc__,
     __all__,
     __and__,
     __anext__,
@@ -121,7 +122,9 @@ declare_const_name! {
     __get__,
     __getattr__,
     __getattribute__,
+    __getformat__,
     __getitem__,
+    __getnewargs__,
     __gt__,
     __hash__,
     __iadd__,
@@ -146,6 +149,7 @@ declare_const_name! {
     __iter__,
     __itruediv__,
     __ixor__,
+    __jit__,  // RustPython dialect
     __le__,
     __len__,
     __length_hint__,
@@ -195,13 +199,16 @@ declare_const_name! {
     __rtruediv__,
     __rxor__,
     __set__,
-    __set_name__,
     __setattr__,
     __setitem__,
+    __setstate__,
+    __set_name__,
     __slots__,
     __str__,
     __sub__,
     __subclasscheck__,
+    __subclasshook__,
+    __subclasses__,
     __sizeof__,
     __truediv__,
     __trunc__,

From 3397b652316b237dbac34b92f0f132f320f5138f Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Thu, 9 Mar 2023 19:55:00 +0900
Subject: [PATCH 08/11] Temp: Do not skip
 test_xml_dom_minicompat.EmptyNodeListTestCase.test_emptynodelist___radd__

---
 Lib/test/test_xml_dom_minicompat.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/Lib/test/test_xml_dom_minicompat.py b/Lib/test/test_xml_dom_minicompat.py
index e2931256ab..c90a01d2e4 100644
--- a/Lib/test/test_xml_dom_minicompat.py
+++ b/Lib/test/test_xml_dom_minicompat.py
@@ -35,8 +35,6 @@ def test_emptynodelist___add__(self):
         node_list = EmptyNodeList() + NodeList()
         self.assertEqual(node_list, NodeList())
 
-    # TODO: RUSTPYTHON
-    @unittest.expectedFailure
     def test_emptynodelist___radd__(self):
         node_list = [1,2] + EmptyNodeList()
         self.assertEqual(node_list, [1,2])

From 5724408fb55c623bb8d5746af504efe3dcc95ba7 Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Thu, 9 Mar 2023 18:37:36 +0900
Subject: [PATCH 09/11] Temp: Skip
 test_collections.TestChainMap.test_union_operators

---
 Lib/test/test_collections.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 68dee5ce67..c8f6880d09 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -259,6 +259,8 @@ def __contains__(self, key):
         d = c.new_child(b=20, c=30)
         self.assertEqual(d.maps, [{'b': 20, 'c': 30}, {'a': 1, 'b': 2}])
 
+    # TODO: RUSTPYTHON
+    @unittest.expectedFailure
     def test_union_operators(self):
         cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4))
         cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4))

From f8cd730af4f114657e7aa008909617f4869cf6c4 Mon Sep 17 00:00:00 2001
From: Zhiyan Xiao <zhiyan.xiao@linecorp.com>
Date: Thu, 9 Mar 2023 19:25:02 +0900
Subject: [PATCH 10/11] Temp: Skip
 test_mmap.MmapTests.test_concat_repeat_exception

---
 Lib/test/test_mmap.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index fa371a291d..8de3c394d6 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -721,6 +721,8 @@ def test_resize_past_pos(self):
         self.assertRaises(ValueError, m.write_byte, 42)
         self.assertRaises(ValueError, m.write, b'abc')
 
+    # TODO: RUSTPYTHON
+    @unittest.skip
     def test_concat_repeat_exception(self):
         m = mmap.mmap(-1, 16)
         with self.assertRaises(TypeError):

From 88895755fb6b92adf0c1a4ef8ef131525a53c97d Mon Sep 17 00:00:00 2001
From: Jeong YunWon <jeong@youknowone.org>
Date: Thu, 9 Mar 2023 22:43:15 +0900
Subject: [PATCH 11/11] Fix sequence protocol concat

---
 Lib/test/test_mmap.py       |  2 --
 vm/src/protocol/number.rs   |  1 +
 vm/src/protocol/sequence.rs | 10 +++++++---
 vm/src/vm/vm_ops.rs         |  7 ++++++-
 4 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py
index 8de3c394d6..fa371a291d 100644
--- a/Lib/test/test_mmap.py
+++ b/Lib/test/test_mmap.py
@@ -721,8 +721,6 @@ def test_resize_past_pos(self):
         self.assertRaises(ValueError, m.write_byte, 42)
         self.assertRaises(ValueError, m.write, b'abc')
 
-    # TODO: RUSTPYTHON
-    @unittest.skip
     def test_concat_repeat_exception(self):
         m = mmap.mmap(-1, 16)
         with self.assertRaises(TypeError):
diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs
index 6841c81918..a3fae13e3e 100644
--- a/vm/src/protocol/number.rs
+++ b/vm/src/protocol/number.rs
@@ -232,6 +232,7 @@ impl PyNumberMethods {
     }
 }
 
+#[derive(Copy, Clone)]
 pub enum PyNumberBinaryOpSlot {
     Add,
     Subtract,
diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs
index 529ef2d1cb..32c65083e5 100644
--- a/vm/src/protocol/sequence.rs
+++ b/vm/src/protocol/sequence.rs
@@ -2,7 +2,7 @@ use crate::{
     builtins::{type_::PointerSlot, PyList, PyListRef, PySlice, PyTuple, PyTupleRef},
     convert::ToPyObject,
     function::PyArithmeticValue,
-    protocol::PyMapping,
+    protocol::{PyMapping, PyNumberBinaryOpSlot},
     AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
 };
 use crossbeam_utils::atomic::AtomicCell;
@@ -118,7 +118,7 @@ impl PySequence<'_> {
 
         // if both arguments apear to be sequences, try fallback to __add__
         if self.check() && other.to_sequence(vm).check() {
-            let ret = vm._add(self.obj, other)?;
+            let ret = vm.binary_op1(self.obj, other, &PyNumberBinaryOpSlot::Add)?;
             if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
                 return Ok(ret);
             }
@@ -137,7 +137,11 @@ impl PySequence<'_> {
 
         // fallback to __mul__
         if self.check() {
-            let ret = vm._mul(self.obj, &n.to_pyobject(vm))?;
+            let ret = vm.binary_op1(
+                self.obj,
+                &n.to_pyobject(vm),
+                &PyNumberBinaryOpSlot::Multiply,
+            )?;
             if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
                 return Ok(ret);
             }
diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs
index 6f892b828d..9832982f90 100644
--- a/vm/src/vm/vm_ops.rs
+++ b/vm/src/vm/vm_ops.rs
@@ -131,7 +131,12 @@ impl VirtualMachine {
     ///   b.rop(b,a)[*], a.op(a,b), b.rop(b,a)
     ///
     /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a)
-    fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: &PyNumberBinaryOpSlot) -> PyResult {
+    pub fn binary_op1(
+        &self,
+        a: &PyObject,
+        b: &PyObject,
+        op_slot: &PyNumberBinaryOpSlot,
+    ) -> PyResult {
         let slot_a = a.class().slots.number.get_left_binary_op(op_slot)?;
         let mut slot_b = if b.class().is(a.class()) {
             None