From d05645ea585a6195499a6a3f039daa323f2a6cc6 Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Fri, 27 May 2022 15:43:27 +0900
Subject: [PATCH 1/9] update syn-ext

---
 derive/Cargo.toml               |  2 +-
 derive/src/pyclass.rs           | 21 +++++++++++---------
 stdlib/src/array.rs             | 10 ++--------
 vm/src/builtins/bytearray.rs    | 34 ++++++++++++++-------------------
 vm/src/builtins/bytes.rs        | 16 +++++-----------
 vm/src/builtins/dict.rs         | 32 ++++++++++++++-----------------
 vm/src/builtins/genericalias.rs | 10 ++--------
 vm/src/builtins/list.rs         | 30 ++++++++++++-----------------
 vm/src/builtins/mappingproxy.rs | 10 ++--------
 vm/src/builtins/memory.rs       | 10 ++--------
 vm/src/builtins/range.rs        | 18 +++++++----------
 vm/src/builtins/str.rs          |  8 +-------
 vm/src/builtins/tuple.rs        | 10 ++--------
 vm/src/builtins/union.rs        | 10 +++-------
 vm/src/stdlib/sre.rs            | 10 +++-------
 15 files changed, 82 insertions(+), 149 deletions(-)

diff --git a/derive/Cargo.toml b/derive/Cargo.toml
index 576f221e4f..afe1de788e 100644
--- a/derive/Cargo.toml
+++ b/derive/Cargo.toml
@@ -12,7 +12,7 @@ proc-macro = true
 
 [dependencies]
 syn = { version = "1.0.91", features = ["full", "extra-traits"] }
-syn-ext = { version = "0.3.1", features = ["full"] }
+syn-ext = { version = "0.4.0", features = ["full"] }
 quote = "1.0.18"
 proc-macro2 = "1.0.37"
 rustpython-compiler = { path = "../compiler/porcelain", version = "0.1.1" }
diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs
index c394556609..d940f7611f 100644
--- a/derive/src/pyclass.rs
+++ b/derive/src/pyclass.rs
@@ -548,25 +548,28 @@ where
     Item: ItemLike + ToTokens + GetIdent,
 {
     fn gen_impl_item(&self, args: ImplItemArgs<'_, Item>) -> Result<()> {
-        let func = args
-            .item
-            .function_or_method()
-            .map_err(|_| self.new_syn_error(args.item.span(), "can only be on a method"))?;
-        let ident = &func.sig().ident;
+        let (ident, span) = if let Ok(c) = args.item.constant() {
+            (c.ident(), c.span())
+        } else if let Ok(f) = args.item.function_or_method() {
+            (&f.sig().ident, f.span())
+        } else {
+            return Err(self.new_syn_error(args.item.span(), "can only be on a method"));
+        };
 
         let item_attr = args.attrs.remove(self.index());
         let item_meta = SlotItemMeta::from_attr(ident.clone(), &item_attr)?;
 
         let slot_ident = item_meta.slot_name()?;
+        let slot_ident = Ident::new(&slot_ident.to_string().to_lowercase(), slot_ident.span());
         let slot_name = slot_ident.to_string();
         let tokens = {
             const NON_ATOMIC_SLOTS: &[&str] = &["as_buffer"];
             if NON_ATOMIC_SLOTS.contains(&slot_name.as_str()) {
-                quote_spanned! { func.span() =>
+                quote_spanned! { span =>
                     slots.#slot_ident = Some(Self::#ident as _);
                 }
             } else {
-                quote_spanned! { func.span() =>
+                quote_spanned! { span =>
                     slots.#slot_ident.store(Some(Self::#ident as _));
                 }
             }
@@ -599,11 +602,11 @@ where
             Ok(py_name)
         };
         let (ident, py_name, tokens) =
-            if args.item.function_or_method().is_ok() || args.item.is_const() {
+            if args.item.function_or_method().is_ok() || args.item.constant().is_ok() {
                 let ident = args.item.get_ident().unwrap();
                 let py_name = get_py_name(&attr, ident)?;
 
-                let value = if args.item.is_const() {
+                let value = if args.item.constant().is_ok() {
                     // TODO: ctx.new_value
                     quote_spanned!(ident.span() => ctx.new_int(Self::#ident).into())
                 } else {
diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs
index 3b6fe0a3a7..ee9bef2a82 100644
--- a/stdlib/src/array.rs
+++ b/stdlib/src/array.rs
@@ -1238,8 +1238,8 @@ mod array {
         },
     };
 
-    impl PyArray {
-        const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+    impl AsMapping for PyArray {
+        const AS_MAPPING: PyMappingMethods = PyMappingMethods {
             length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
             subscript: Some(|mapping, needle, vm| {
                 Self::mapping_downcast(mapping)._getitem(needle, vm)
@@ -1255,12 +1255,6 @@ mod array {
         };
     }
 
-    impl AsMapping for PyArray {
-        fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-            Self::MAPPING_METHODS
-        }
-    }
-
     impl Iterable for PyArray {
         fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
             Ok(PyArrayIter {
diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs
index 04762df510..1a6d314463 100644
--- a/vm/src/builtins/bytearray.rs
+++ b/vm/src/builtins/bytearray.rs
@@ -690,23 +690,6 @@ impl PyByteArray {
     }
 }
 
-impl PyByteArray {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
-        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
-        subscript: Some(|mapping, needle, vm| {
-            Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
-        }),
-        ass_subscript: Some(|mapping, needle, value, vm| {
-            let zelf = Self::mapping_downcast(mapping);
-            if let Some(value) = value {
-                Self::setitem(zelf.to_owned(), needle.to_owned(), value, vm)
-            } else {
-                zelf.delitem(needle.to_owned(), vm)
-            }
-        }),
-    };
-}
-
 impl Constructor for PyByteArray {
     type Args = FuncArgs;
 
@@ -784,9 +767,20 @@ impl<'a> BufferResizeGuard<'a> for PyByteArray {
 }
 
 impl AsMapping for PyByteArray {
-    fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
+        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
+        subscript: Some(|mapping, needle, vm| {
+            Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
+        }),
+        ass_subscript: Some(|mapping, needle, value, vm| {
+            let zelf = Self::mapping_downcast(mapping);
+            if let Some(value) = value {
+                Self::setitem(zelf.to_owned(), needle.to_owned(), value, vm)
+            } else {
+                zelf.delitem(needle.to_owned(), vm)
+            }
+        }),
+    };
 }
 
 impl AsSequence for PyByteArray {
diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs
index ccfdb88f80..e680eded4c 100644
--- a/vm/src/builtins/bytes.rs
+++ b/vm/src/builtins/bytes.rs
@@ -550,14 +550,6 @@ impl PyBytes {
     }
 }
 
-impl PyBytes {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
-        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
-        subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)),
-        ass_subscript: None,
-    };
-}
-
 static BUFFER_METHODS: BufferMethods = BufferMethods {
     obj_bytes: |buffer| buffer.obj_as::<PyBytes>().as_bytes().into(),
     obj_bytes_mut: |_| panic!(),
@@ -577,9 +569,11 @@ impl AsBuffer for PyBytes {
 }
 
 impl AsMapping for PyBytes {
-    fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
+        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
+        subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)),
+        ass_subscript: None,
+    };
 }
 
 impl AsSequence for PyBytes {
diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs
index cd5066057b..dd2d3c6947 100644
--- a/vm/src/builtins/dict.rs
+++ b/vm/src/builtins/dict.rs
@@ -212,21 +212,6 @@ impl PyDict {
     pub fn size(&self) -> dictdatatype::DictSize {
         self.entries.size()
     }
-
-    pub(crate) const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
-        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
-        subscript: Some(|mapping, needle, vm| {
-            Self::mapping_downcast(mapping).inner_getitem(needle, vm)
-        }),
-        ass_subscript: Some(|mapping, needle, value, vm| {
-            let zelf = Self::mapping_downcast(mapping);
-            if let Some(value) = value {
-                zelf.inner_setitem(needle, value, vm)
-            } else {
-                zelf.inner_delitem(needle, vm)
-            }
-        }),
-    };
 }
 
 // Python dict methods:
@@ -476,9 +461,20 @@ impl Initializer for PyDict {
 }
 
 impl AsMapping for PyDict {
-    fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
+        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
+        subscript: Some(|mapping, needle, vm| {
+            Self::mapping_downcast(mapping).inner_getitem(needle, vm)
+        }),
+        ass_subscript: Some(|mapping, needle, value, vm| {
+            let zelf = Self::mapping_downcast(mapping);
+            if let Some(value) = value {
+                zelf.inner_setitem(needle, value, vm)
+            } else {
+                zelf.inner_delitem(needle, vm)
+            }
+        }),
+    };
 }
 
 impl AsSequence for PyDict {
diff --git a/vm/src/builtins/genericalias.rs b/vm/src/builtins/genericalias.rs
index 9c585124c9..4f1d6fedde 100644
--- a/vm/src/builtins/genericalias.rs
+++ b/vm/src/builtins/genericalias.rs
@@ -306,8 +306,8 @@ pub fn subs_parameters<F: Fn(&VirtualMachine) -> PyResult<String>>(
     Ok(PyTuple::new_ref(new_args, &vm.ctx))
 }
 
-impl PyGenericAlias {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+impl AsMapping for PyGenericAlias {
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
         length: None,
         subscript: Some(|mapping, needle, vm| {
             Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
@@ -316,12 +316,6 @@ impl PyGenericAlias {
     };
 }
 
-impl AsMapping for PyGenericAlias {
-    fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
-}
-
 impl Callable for PyGenericAlias {
     type Args = FuncArgs;
     fn call(zelf: &crate::Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs
index f6187ad9e0..46ceaac074 100644
--- a/vm/src/builtins/list.rs
+++ b/vm/src/builtins/list.rs
@@ -352,21 +352,6 @@ impl PyList {
     }
 }
 
-impl PyList {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
-        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
-        subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)),
-        ass_subscript: Some(|mapping, needle, value, vm| {
-            let zelf = Self::mapping_downcast(mapping);
-            if let Some(value) = value {
-                zelf._setitem(needle, value, vm)
-            } else {
-                zelf._delitem(needle, vm)
-            }
-        }),
-    };
-}
-
 impl<'a> MutObjectSequenceOp<'a> for PyList {
     type Guard = PyMappedRwLockReadGuard<'a, [PyObjectRef]>;
 
@@ -404,9 +389,18 @@ impl Initializer for PyList {
 }
 
 impl AsMapping for PyList {
-    fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
+        length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
+        subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)),
+        ass_subscript: Some(|mapping, needle, value, vm| {
+            let zelf = Self::mapping_downcast(mapping);
+            if let Some(value) = value {
+                zelf._setitem(needle, value, vm)
+            } else {
+                zelf._delitem(needle, vm)
+            }
+        }),
+    };
 }
 
 impl AsSequence for PyList {
diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index 4ab71a1dff..d5ebeec0ea 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -157,8 +157,8 @@ impl PyMappingProxy {
     }
 }
 
-impl PyMappingProxy {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+impl AsMapping for PyMappingProxy {
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
         length: None,
         subscript: Some(|mapping, needle, vm| {
             Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
@@ -167,12 +167,6 @@ impl PyMappingProxy {
     };
 }
 
-impl AsMapping for PyMappingProxy {
-    fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
-}
-
 impl AsSequence for PyMappingProxy {
     fn as_sequence(
         _zelf: &crate::Py<Self>,
diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs
index 14bcbca790..cba6789f5b 100644
--- a/vm/src/builtins/memory.rs
+++ b/vm/src/builtins/memory.rs
@@ -957,8 +957,8 @@ impl Drop for PyMemoryView {
     }
 }
 
-impl PyMemoryView {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+impl AsMapping for PyMemoryView {
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
         length: Some(|mapping, vm| Self::mapping_downcast(mapping).len(vm)),
         subscript: Some(|mapping, needle, vm| {
             let zelf = Self::mapping_downcast(mapping);
@@ -975,12 +975,6 @@ impl PyMemoryView {
     };
 }
 
-impl AsMapping for PyMemoryView {
-    fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
-}
-
 impl AsSequence for PyMemoryView {
     fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
         Cow::Borrowed(&Self::SEQUENCE_METHODS)
diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs
index 1348d7654a..5de3d78fae 100644
--- a/vm/src/builtins/range.rs
+++ b/vm/src/builtins/range.rs
@@ -390,14 +390,6 @@ impl PyRange {
             .map(|x| x as usize)
     }
 
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
-        length: Some(|mapping, vm| Self::mapping_downcast(mapping).protocol_length(vm)),
-        subscript: Some(|mapping, needle, vm| {
-            Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
-        }),
-        ass_subscript: None,
-    };
-
     const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, vm| Self::sequence_downcast(seq).protocol_length(vm)),
         item: Some(|seq, i, vm| {
@@ -414,9 +406,13 @@ impl PyRange {
 }
 
 impl AsMapping for PyRange {
-    fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
+        length: Some(|mapping, vm| Self::mapping_downcast(mapping).protocol_length(vm)),
+        subscript: Some(|mapping, needle, vm| {
+            Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
+        }),
+        ass_subscript: None,
+    };
 }
 
 impl AsSequence for PyRange {
diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs
index 8a58f680d2..0562bcd548 100644
--- a/vm/src/builtins/str.rs
+++ b/vm/src/builtins/str.rs
@@ -1301,13 +1301,7 @@ impl Iterable for PyStr {
 }
 
 impl AsMapping for PyStr {
-    fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
-}
-
-impl PyStr {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
         length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
         subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)),
         ass_subscript: None,
diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs
index 84865a0916..976a5aaefb 100644
--- a/vm/src/builtins/tuple.rs
+++ b/vm/src/builtins/tuple.rs
@@ -345,20 +345,14 @@ impl PyTuple {
     }
 }
 
-impl PyTuple {
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+impl AsMapping for PyTuple {
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
         length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())),
         subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)),
         ass_subscript: None,
     };
 }
 
-impl AsMapping for PyTuple {
-    fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
-}
-
 impl AsSequence for PyTuple {
     fn as_sequence(
         _zelf: &crate::Py<Self>,
diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs
index e2072a3425..813974b53c 100644
--- a/vm/src/builtins/union.rs
+++ b/vm/src/builtins/union.rs
@@ -224,8 +224,10 @@ impl PyUnion {
 
         Ok(res)
     }
+}
 
-    const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+impl AsMapping for PyUnion {
+    const AS_MAPPING: PyMappingMethods = PyMappingMethods {
         length: None,
         subscript: Some(|mapping, needle, vm| {
             Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm)
@@ -234,12 +236,6 @@ impl PyUnion {
     };
 }
 
-impl AsMapping for PyUnion {
-    fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::MAPPING_METHODS
-    }
-}
-
 impl Comparable for PyUnion {
     fn cmp(
         zelf: &crate::Py<Self>,
diff --git a/vm/src/stdlib/sre.rs b/vm/src/stdlib/sre.rs
index 7ffbb35dca..2b6b88d9f2 100644
--- a/vm/src/stdlib/sre.rs
+++ b/vm/src/stdlib/sre.rs
@@ -780,8 +780,10 @@ mod _sre {
         fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
             PyGenericAlias::new(cls, args, vm)
         }
+    }
 
-        const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
+    impl AsMapping for Match {
+        const AS_MAPPING: PyMappingMethods = PyMappingMethods {
             length: None,
             subscript: Some(|mapping, needle, vm| {
                 Self::mapping_downcast(mapping)
@@ -792,12 +794,6 @@ mod _sre {
         };
     }
 
-    impl AsMapping for Match {
-        fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
-            Self::MAPPING_METHODS
-        }
-    }
-
     #[pyattr]
     #[pyclass(name = "SRE_Scanner")]
     #[derive(Debug, PyPayload)]

From 97c2d187f68c5939278e0cce47d1073c4610fc2b Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sat, 28 May 2022 01:38:30 +0900
Subject: [PATCH 2/9] AsMapping::AS_MAPPING

---
 Cargo.lock                  | 13 +++++++++++--
 vm/src/function/protocol.rs |  3 ++-
 vm/src/types/slot.rs        |  9 ++++-----
 3 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index eb986773c8..f376072567 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1518,7 +1518,7 @@ dependencies = [
  "proc-macro2",
  "quote",
  "syn",
- "syn-ext",
+ "syn-ext 0.3.1",
 ]
 
 [[package]]
@@ -1645,7 +1645,7 @@ dependencies = [
  "rustpython-compiler",
  "rustpython-doc",
  "syn",
- "syn-ext",
+ "syn-ext 0.4.0",
  "textwrap 0.15.0",
 ]
 
@@ -2120,6 +2120,15 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "syn-ext"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b86cb2b68c5b3c078cac02588bc23f3c04bb828c5d3aedd17980876ec6a7be6"
+dependencies = [
+ "syn",
+]
+
 [[package]]
 name = "system-configuration"
 version = "0.5.0"
diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs
index de18517ce3..02c179b4af 100644
--- a/vm/src/function/protocol.rs
+++ b/vm/src/function/protocol.rs
@@ -4,6 +4,7 @@ use crate::{
     convert::ToPyObject,
     identifier,
     protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods},
+    types::AsMapping,
     AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
 };
 use std::{borrow::Borrow, marker::PhantomData};
@@ -110,7 +111,7 @@ impl ArgMapping {
     pub fn from_dict_exact(dict: PyDictRef) -> Self {
         Self {
             obj: dict.into(),
-            mapping_methods: PyDict::MAPPING_METHODS,
+            mapping_methods: PyDict::AS_MAPPING,
         }
     }
 
diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index b626327e91..d7bbaed1f1 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -896,15 +896,14 @@ pub trait AsBuffer: PyPayload {
 
 #[pyimpl]
 pub trait AsMapping: PyPayload {
+    const AS_MAPPING: PyMappingMethods;
+
     #[inline]
     #[pyslot]
-    fn slot_as_mapping(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods {
-        let zelf = unsafe { zelf.downcast_unchecked_ref::<Self>() };
-        Self::as_mapping(zelf, vm)
+    fn as_mapping(_zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods {
+        Self::AS_MAPPING
     }
 
-    fn as_mapping(zelf: &Py<Self>, vm: &VirtualMachine) -> PyMappingMethods;
-
     fn mapping_downcast<'a>(mapping: &'a PyMapping) -> &'a Py<Self> {
         unsafe { mapping.obj.downcast_unchecked_ref() }
     }

From 6857384c338a0741b7f8448c7b12f0b9ce918c64 Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sat, 28 May 2022 23:36:10 +0900
Subject: [PATCH 3/9] new_mapping_wrapper as static slice

---
 vm/src/types/slot.rs | 76 ++++++++++++++++++++++++++++++++------------
 1 file changed, 56 insertions(+), 20 deletions(-)

diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index d7bbaed1f1..f68b99c294 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -174,8 +174,8 @@ macro_rules! then_some_closure {
     };
 }
 
-fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
-    let ret = vm.call_special_method(obj, identifier!(vm, __len__), ())?;
+fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> {
+    let ret = vm.call_special_method(obj.to_owned(), identifier!(vm, __len__), ())?;
     let len = ret.payload::<PyInt>().ok_or_else(|| {
         vm.new_type_error(format!(
             "'{}' object cannot be interpreted as an integer",
@@ -192,26 +192,30 @@ fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
     Ok(len as usize)
 }
 
-fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods {
+const fn new_as_mapping_wrapper(
+    has_length: bool,
+    has_subscript: bool,
+    has_ass_subscript: bool,
+) -> PyMappingMethods {
     PyMappingMethods {
-        length: then_some_closure!(
-            zelf.class().has_attr(identifier!(vm, __len__)),
-            |mapping, vm| { length_wrapper(mapping.obj.to_owned(), vm) }
-        ),
-        subscript: then_some_closure!(
-            zelf.class().has_attr(identifier!(vm, __getitem__)),
-            |mapping, needle, vm| {
+        length: if has_length {
+            Some(|mapping, vm| length_wrapper(&mapping.obj, vm))
+        } else {
+            None
+        },
+        subscript: if has_subscript {
+            Some(|mapping, needle, vm| {
                 vm.call_special_method(
                     mapping.obj.to_owned(),
                     identifier!(vm, __getitem__),
                     (needle.to_owned(),),
                 )
-            }
-        ),
-        ass_subscript: then_some_closure!(
-            zelf.class().has_attr(identifier!(vm, __setitem__))
-                | zelf.class().has_attr(identifier!(vm, __delitem__)),
-            |mapping, needle, value, vm| match value {
+            })
+        } else {
+            None
+        },
+        ass_subscript: if has_ass_subscript {
+            Some(|mapping, needle, value, vm| match value {
                 Some(value) => vm
                     .call_special_method(
                         mapping.obj.to_owned(),
@@ -223,12 +227,44 @@ fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods
                     .call_special_method(
                         mapping.obj.to_owned(),
                         identifier!(vm, __delitem__),
-                        (needle.to_owned(),)
+                        (needle.to_owned(),),
                     )
                     .map(|_| Ok(()))?,
-            }
-        ),
+            })
+        } else {
+            None
+        },
+    }
+}
+
+fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods {
+    static MAPPING_METHODS: &[PyMappingMethods] = &[
+        new_as_mapping_wrapper(false, false, false),
+        new_as_mapping_wrapper(true, false, false),
+        new_as_mapping_wrapper(false, true, false),
+        new_as_mapping_wrapper(true, true, false),
+        new_as_mapping_wrapper(false, false, true),
+        new_as_mapping_wrapper(true, false, true),
+        new_as_mapping_wrapper(false, true, true),
+        new_as_mapping_wrapper(true, true, true),
+    ];
+    const fn bool_int(v: bool) -> usize {
+        if v {
+            1
+        } else {
+            0
+        }
     }
+    let (has_length, has_subscript, has_ass_subscript) = (
+        zelf.class().has_attr(identifier!(vm, __len__)),
+        zelf.class().has_attr(identifier!(vm, __getitem__)),
+        zelf.class().has_attr(identifier!(vm, __setitem__))
+            | zelf.class().has_attr(identifier!(vm, __delitem__)),
+    );
+    let key = (bool_int(has_length) << 0)
+        | (bool_int(has_subscript) << 1)
+        | (bool_int(has_ass_subscript) << 2);
+    MAPPING_METHODS[key].clone()
 }
 
 fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
@@ -239,7 +275,7 @@ fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PyS
     Cow::Owned(PySequenceMethods {
         length: then_some_closure!(
             zelf.class().has_attr(identifier!(vm, __len__)),
-            |seq, vm| { length_wrapper(seq.obj.to_owned(), vm) }
+            |seq, vm| { length_wrapper(&seq.obj, vm) }
         ),
         item: Some(|seq, i, vm| {
             vm.call_special_method(

From 83a146d8c83b5342d02327dec62483ac7d594a3f Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sun, 29 May 2022 00:26:49 +0900
Subject: [PATCH 4/9] AsMapping only with static reference

---
 vm/src/builtins/mappingproxy.rs |   2 +-
 vm/src/function/protocol.rs     |   6 +-
 vm/src/protocol/mapping.rs      |  78 ++++++++----------
 vm/src/protocol/object.rs       |  29 ++++---
 vm/src/protocol/sequence.rs     |   4 +-
 vm/src/types/slot.rs            | 140 ++++++++++++++++++--------------
 6 files changed, 137 insertions(+), 122 deletions(-)

diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index d5ebeec0ea..fae4c55890 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -39,7 +39,7 @@ impl Constructor for PyMappingProxy {
     type Args = PyObjectRef;
 
     fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult {
-        if !PyMapping::from(mapping.as_ref()).check(vm)
+        if !PyMapping::check(mapping.as_ref(), vm)
             || mapping.payload_if_subclass::<PyList>(vm).is_some()
             || mapping.payload_if_subclass::<PyTuple>(vm).is_some()
         {
diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs
index 02c179b4af..30816e11f9 100644
--- a/vm/src/function/protocol.rs
+++ b/vm/src/function/protocol.rs
@@ -103,7 +103,7 @@ where
 #[derive(Clone)]
 pub struct ArgMapping {
     obj: PyObjectRef,
-    mapping_methods: PyMappingMethods,
+    mapping_methods: &'static PyMappingMethods,
 }
 
 impl ArgMapping {
@@ -111,7 +111,7 @@ impl ArgMapping {
     pub fn from_dict_exact(dict: PyDictRef) -> Self {
         Self {
             obj: dict.into(),
-            mapping_methods: PyDict::AS_MAPPING,
+            mapping_methods: &PyDict::AS_MAPPING,
         }
     }
 
@@ -152,7 +152,7 @@ impl ToPyObject for ArgMapping {
 impl TryFromObject for ArgMapping {
     fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
         let mapping = PyMapping::try_protocol(&obj, vm)?;
-        let mapping_methods = *mapping.methods(vm);
+        let mapping_methods = mapping.methods;
         Ok(Self {
             obj,
             mapping_methods,
diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs
index a9389db473..84ff355472 100644
--- a/vm/src/protocol/mapping.rs
+++ b/vm/src/protocol/mapping.rs
@@ -3,7 +3,6 @@ use crate::{
         dict::{PyDictItems, PyDictKeys, PyDictValues},
         PyDict, PyStrInterned,
     },
-    common::lock::OnceCell,
     convert::ToPyResult,
     AsObject, PyObject, PyObjectRef, PyResult, VirtualMachine,
 };
@@ -11,7 +10,6 @@ use crate::{
 // Mapping protocol
 // https://docs.python.org/3/c-api/mapping.html
 #[allow(clippy::type_complexity)]
-#[derive(Default, Copy, Clone)]
 pub struct PyMappingMethods {
     pub length: Option<fn(&PyMapping, &VirtualMachine) -> PyResult<usize>>,
     pub subscript: Option<fn(&PyMapping, &PyObject, &VirtualMachine) -> PyResult>,
@@ -19,20 +17,16 @@ pub struct PyMappingMethods {
         Option<fn(&PyMapping, &PyObject, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>>,
 }
 
+impl PyMappingMethods {
+    fn check(&self) -> bool {
+        self.subscript.is_some()
+    }
+}
+
 #[derive(Clone)]
 pub struct PyMapping<'a> {
     pub obj: &'a PyObject,
-    methods: OnceCell<PyMappingMethods>,
-}
-
-impl<'a> From<&'a PyObject> for PyMapping<'a> {
-    #[inline(always)]
-    fn from(obj: &'a PyObject) -> Self {
-        Self {
-            obj,
-            methods: OnceCell::new(),
-        }
-    }
+    pub methods: &'static PyMappingMethods,
 }
 
 impl AsRef<PyObject> for PyMapping<'_> {
@@ -43,47 +37,47 @@ impl AsRef<PyObject> for PyMapping<'_> {
 }
 
 impl<'a> PyMapping<'a> {
+    #[inline]
+    pub fn new(obj: &'a PyObject, vm: &VirtualMachine) -> Option<Self> {
+        let methods = Self::find_methods(obj, vm)?;
+        Some(Self { obj, methods })
+    }
+
     #[inline(always)]
-    pub fn with_methods(obj: &'a PyObject, methods: PyMappingMethods) -> Self {
-        Self {
-            obj,
-            methods: OnceCell::from(methods),
-        }
+    pub fn with_methods(obj: &'a PyObject, methods: &'static PyMappingMethods) -> Self {
+        Self { obj, methods }
     }
 
     pub fn try_protocol(obj: &'a PyObject, vm: &VirtualMachine) -> PyResult<Self> {
-        let zelf = Self::from(obj);
-        if zelf.check(vm) {
-            Ok(zelf)
-        } else {
-            Err(vm.new_type_error(format!("{} is not a mapping object", zelf.obj.class())))
+        if let Some(methods) = Self::find_methods(obj, vm) {
+            if methods.check() {
+                return Ok(Self::with_methods(obj, methods));
+            }
         }
+
+        Err(vm.new_type_error(format!("{} is not a mapping object", obj.class())))
     }
 }
 
 impl PyMapping<'_> {
     // PyMapping::Check
     #[inline]
-    pub fn check(&self, vm: &VirtualMachine) -> bool {
-        self.methods(vm).subscript.is_some()
-    }
-
-    pub fn methods(&self, vm: &VirtualMachine) -> &PyMappingMethods {
-        self.methods.get_or_init(|| {
-            if let Some(f) = self
-                .obj
-                .class()
-                .mro_find_map(|cls| cls.slots.as_mapping.load())
-            {
-                f(self.obj, vm)
-            } else {
-                PyMappingMethods::default()
-            }
-        })
+    pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool {
+        Self::find_methods(obj, vm)
+            .and_then(|m| m.subscript)
+            .is_some()
+    }
+
+    pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PyMappingMethods> {
+        if let Some(f) = obj.class().mro_find_map(|cls| cls.slots.as_mapping.load()) {
+            Some(f(obj, vm))
+        } else {
+            None
+        }
     }
 
     pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> {
-        self.methods(vm).length.map(|f| f(self, vm))
+        self.methods.length.map(|f| f(self, vm))
     }
 
     pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> {
@@ -110,7 +104,7 @@ impl PyMapping<'_> {
 
     fn _subscript(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
         let f = self
-            .methods(vm)
+            .methods
             .subscript
             .ok_or_else(|| vm.new_type_error(format!("{} is not a mapping", self.obj.class())))?;
         f(self, needle, vm)
@@ -122,7 +116,7 @@ impl PyMapping<'_> {
         value: Option<PyObjectRef>,
         vm: &VirtualMachine,
     ) -> PyResult<()> {
-        let f = self.methods(vm).ass_subscript.ok_or_else(|| {
+        let f = self.methods.ass_subscript.ok_or_else(|| {
             vm.new_type_error(format!(
                 "'{}' object does not support item assignment",
                 self.obj.class()
diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs
index d4ed959eea..31cf76b47b 100644
--- a/vm/src/protocol/object.rs
+++ b/vm/src/protocol/object.rs
@@ -524,7 +524,7 @@ impl PyObject {
     pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> {
         PySequence::from(self)
             .length_opt(vm)
-            .or_else(|| PyMapping::from(self).length_opt(vm))
+            .or_else(|| PyMapping::new(self, vm).and_then(|mapping| mapping.length_opt(vm)))
     }
 
     pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> {
@@ -570,13 +570,15 @@ impl PyObject {
             return dict.set_item(needle, value, vm);
         }
 
-        let mapping = PyMapping::from(self);
-        let seq = PySequence::from(self);
+        if let Some(mapping) = PyMapping::new(self, vm) {
+            if let Some(f) = mapping.methods.ass_subscript {
+                let needle = needle.to_pyobject(vm);
+                return f(&mapping, &needle, Some(value), vm);
+            }
+        }
 
-        if let Some(f) = mapping.methods(vm).ass_subscript {
-            let needle = needle.to_pyobject(vm);
-            f(&mapping, &needle, Some(value), vm)
-        } else if let Some(f) = seq.methods(vm).ass_item {
+        let seq = PySequence::from(self);
+        if let Some(f) = seq.methods(vm).ass_item {
             let i = needle.key_as_isize(vm)?;
             f(&seq, i, Some(value), vm)
         } else {
@@ -592,13 +594,16 @@ impl PyObject {
             return dict.del_item(needle, vm);
         }
 
-        let mapping = PyMapping::from(self);
+        if let Some(mapping) = PyMapping::new(self, vm) {
+            if let Some(f) = mapping.methods.ass_subscript {
+                let needle = needle.to_pyobject(vm);
+                return f(&mapping, &needle, None, vm);
+            }
+        }
+
         let seq = PySequence::from(self);
 
-        if let Some(f) = mapping.methods(vm).ass_subscript {
-            let needle = needle.to_pyobject(vm);
-            f(&mapping, &needle, None, vm)
-        } else if let Some(f) = seq.methods(vm).ass_item {
+        if let Some(f) = seq.methods(vm).ass_item {
             let i = needle.key_as_isize(vm)?;
             f(&seq, i, None, vm)
         } else {
diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs
index ac15be3e06..3af7a0bf98 100644
--- a/vm/src/protocol/sequence.rs
+++ b/vm/src/protocol/sequence.rs
@@ -242,8 +242,8 @@ impl PySequence<'_> {
         value: Option<PyObjectRef>,
         vm: &VirtualMachine,
     ) -> PyResult<()> {
-        let mapping = PyMapping::from(self.obj);
-        if let Some(f) = mapping.methods(vm).ass_subscript {
+        let mapping = PyMapping::new(self.obj, vm).unwrap();
+        if let Some(f) = mapping.methods.ass_subscript {
             let slice = PySlice {
                 start: Some(start.to_pyobject(vm)),
                 stop: stop.to_pyobject(vm),
diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index f68b99c294..0a112c7d7e 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -140,7 +140,7 @@ impl Default for PyTypeFlags {
 }
 
 pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult;
-pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> PyMappingMethods;
+pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> &'static PyMappingMethods;
 pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyHash>;
 // CallFunc = GenericMethod
 pub(crate) type GetattroFunc = fn(&PyObject, PyStrRef, &VirtualMachine) -> PyResult;
@@ -192,79 +192,94 @@ fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> {
     Ok(len as usize)
 }
 
-const fn new_as_mapping_wrapper(
+const fn bool_int(v: bool) -> usize {
+    if v {
+        1
+    } else {
+        0
+    }
+}
+
+pub(crate) fn static_as_mapping_generic(
     has_length: bool,
     has_subscript: bool,
     has_ass_subscript: bool,
-) -> PyMappingMethods {
-    PyMappingMethods {
-        length: if has_length {
-            Some(|mapping, vm| length_wrapper(&mapping.obj, vm))
-        } else {
-            None
-        },
-        subscript: if has_subscript {
-            Some(|mapping, needle, vm| {
-                vm.call_special_method(
+) -> &'static PyMappingMethods {
+    static METHODS: &[PyMappingMethods] = &[
+        new_generic(false, false, false),
+        new_generic(true, false, false),
+        new_generic(false, true, false),
+        new_generic(true, true, false),
+        new_generic(false, false, true),
+        new_generic(true, false, true),
+        new_generic(false, true, true),
+        new_generic(true, true, true),
+    ];
+
+    fn length(mapping: &PyMapping, vm: &VirtualMachine) -> PyResult<usize> {
+        length_wrapper(mapping.obj, vm)
+    }
+    fn subscript(mapping: &PyMapping, needle: &PyObject, vm: &VirtualMachine) -> PyResult {
+        vm.call_special_method(
+            mapping.obj.to_owned(),
+            identifier!(vm, __getitem__),
+            (needle.to_owned(),),
+        )
+    }
+    fn ass_subscript(
+        mapping: &PyMapping,
+        needle: &PyObject,
+        value: Option<PyObjectRef>,
+        vm: &VirtualMachine,
+    ) -> PyResult<()> {
+        match value {
+            Some(value) => vm
+                .call_special_method(
                     mapping.obj.to_owned(),
-                    identifier!(vm, __getitem__),
+                    identifier!(vm, __setitem__),
+                    (needle.to_owned(), value),
+                )
+                .map(|_| Ok(()))?,
+            None => vm
+                .call_special_method(
+                    mapping.obj.to_owned(),
+                    identifier!(vm, __delitem__),
                     (needle.to_owned(),),
                 )
-            })
-        } else {
-            None
-        },
-        ass_subscript: if has_ass_subscript {
-            Some(|mapping, needle, value, vm| match value {
-                Some(value) => vm
-                    .call_special_method(
-                        mapping.obj.to_owned(),
-                        identifier!(vm, __setitem__),
-                        (needle.to_owned(), value),
-                    )
-                    .map(|_| Ok(()))?,
-                None => vm
-                    .call_special_method(
-                        mapping.obj.to_owned(),
-                        identifier!(vm, __delitem__),
-                        (needle.to_owned(),),
-                    )
-                    .map(|_| Ok(()))?,
-            })
-        } else {
-            None
-        },
+                .map(|_| Ok(()))?,
+        }
     }
-}
 
-fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods {
-    static MAPPING_METHODS: &[PyMappingMethods] = &[
-        new_as_mapping_wrapper(false, false, false),
-        new_as_mapping_wrapper(true, false, false),
-        new_as_mapping_wrapper(false, true, false),
-        new_as_mapping_wrapper(true, true, false),
-        new_as_mapping_wrapper(false, false, true),
-        new_as_mapping_wrapper(true, false, true),
-        new_as_mapping_wrapper(false, true, true),
-        new_as_mapping_wrapper(true, true, true),
-    ];
-    const fn bool_int(v: bool) -> usize {
-        if v {
-            1
-        } else {
-            0
+    const fn new_generic(
+        has_length: bool,
+        has_subscript: bool,
+        has_ass_subscript: bool,
+    ) -> PyMappingMethods {
+        PyMappingMethods {
+            length: if has_length { Some(length) } else { None },
+            subscript: if has_subscript { Some(subscript) } else { None },
+            ass_subscript: if has_ass_subscript {
+                Some(ass_subscript)
+            } else {
+                None
+            },
         }
     }
+
+    let key =
+        bool_int(has_length) | (bool_int(has_subscript) << 1) | (bool_int(has_ass_subscript) << 2);
+
+    &METHODS[key]
+}
+
+fn as_mapping_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PyMappingMethods {
     let (has_length, has_subscript, has_ass_subscript) = (
         zelf.class().has_attr(identifier!(vm, __len__)),
         zelf.class().has_attr(identifier!(vm, __getitem__)),
         zelf.class().has_attr(identifier!(vm, __setitem__))
             | zelf.class().has_attr(identifier!(vm, __delitem__)),
     );
-    let key = (bool_int(has_length) << 0)
-        | (bool_int(has_subscript) << 1)
-        | (bool_int(has_ass_subscript) << 2);
-    MAPPING_METHODS[key].clone()
+    static_as_mapping_generic(has_length, has_subscript, has_ass_subscript)
 }
 
 fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
@@ -275,7 +290,7 @@ fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PyS
     Cow::Owned(PySequenceMethods {
         length: then_some_closure!(
             zelf.class().has_attr(identifier!(vm, __len__)),
-            |seq, vm| { length_wrapper(&seq.obj, vm) }
+            |seq, vm| { length_wrapper(seq.obj, vm) }
         ),
         item: Some(|seq, i, vm| {
             vm.call_special_method(
@@ -420,7 +435,7 @@ impl PyType {
         }
         match name.as_str() {
             "__len__" | "__getitem__" | "__setitem__" | "__delitem__" => {
-                update_slot!(as_mapping, as_mapping_wrapper);
+                update_slot!(as_mapping, as_mapping_generic);
                 update_slot!(as_sequence, as_sequence_wrapper);
             }
             "__hash__" => {
@@ -936,10 +951,11 @@ pub trait AsMapping: PyPayload {
 
     #[inline]
     #[pyslot]
-    fn as_mapping(_zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods {
-        Self::AS_MAPPING
+    fn as_mapping(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PyMappingMethods {
+        &Self::AS_MAPPING
     }
 
+    #[inline]
     fn mapping_downcast<'a>(mapping: &'a PyMapping) -> &'a Py<Self> {
         unsafe { mapping.obj.downcast_unchecked_ref() }
     }

From 6d0abe6915c2c00228d80318abec71703423238e Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sun, 29 May 2022 03:40:01 +0900
Subject: [PATCH 5/9] mapping proxy holds PyMapping

---
 vm/src/builtins/mappingproxy.rs | 79 +++++++++++++--------------------
 vm/src/function/protocol.rs     | 30 ++++++++-----
 vm/src/protocol/mapping.rs      |  6 +++
 3 files changed, 57 insertions(+), 58 deletions(-)

diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index fae4c55890..507375de86 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -2,7 +2,7 @@ use super::{PyDict, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef};
 use crate::{
     class::PyClassImpl,
     convert::ToPyObject,
-    function::OptionalArg,
+    function::{ArgMapping, OptionalArg},
     protocol::{PyMapping, PyMappingMethods, PySequence, PySequenceMethods},
     types::{AsMapping, AsSequence, Constructor, Iterable},
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
@@ -18,7 +18,7 @@ pub struct PyMappingProxy {
 #[derive(Debug)]
 enum MappingProxyInner {
     Class(PyTypeRef),
-    Dict(PyObjectRef),
+    Mapping(ArgMapping),
 }
 
 impl PyPayload for PyMappingProxy {
@@ -39,21 +39,21 @@ impl Constructor for PyMappingProxy {
     type Args = PyObjectRef;
 
     fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult {
-        if !PyMapping::check(mapping.as_ref(), vm)
-            || mapping.payload_if_subclass::<PyList>(vm).is_some()
-            || mapping.payload_if_subclass::<PyTuple>(vm).is_some()
-        {
-            Err(vm.new_type_error(format!(
-                "mappingproxy() argument must be a mapping, not {}",
-                mapping.class()
-            )))
-        } else {
-            Self {
-                mapping: MappingProxyInner::Dict(mapping),
+        if let Some(methods) = PyMapping::find_methods(&mapping, vm) {
+            if mapping.payload_if_subclass::<PyList>(vm).is_none()
+                && mapping.payload_if_subclass::<PyTuple>(vm).is_none()
+            {
+                return Self {
+                    mapping: MappingProxyInner::Mapping(ArgMapping::with_methods(mapping, methods)),
+                }
+                .into_ref_with_type(vm, cls)
+                .map(Into::into);
             }
-            .into_ref_with_type(vm, cls)
-            .map(Into::into)
         }
+        Err(vm.new_type_error(format!(
+            "mappingproxy() argument must be a mapping, not {}",
+            mapping.class()
+        )))
     }
 }
 
@@ -64,7 +64,7 @@ impl PyMappingProxy {
             MappingProxyInner::Class(class) => key
                 .as_interned_str(vm)
                 .and_then(|key| class.attributes.read().get(key).cloned()),
-            MappingProxyInner::Dict(obj) => obj.get_item(&*key, vm).ok(),
+            MappingProxyInner::Mapping(mapping) => mapping.mapping().subscript(&*key, vm).ok(),
         };
         Ok(opt)
     }
@@ -92,7 +92,7 @@ impl PyMappingProxy {
             MappingProxyInner::Class(class) => Ok(key
                 .as_interned_str(vm)
                 .map_or(false, |key| class.attributes.read().contains_key(key))),
-            MappingProxyInner::Dict(obj) => PySequence::from(obj.as_ref()).contains(key, vm),
+            MappingProxyInner::Mapping(obj) => PySequence::from(obj.as_ref()).contains(key, vm),
         }
     }
 
@@ -101,40 +101,34 @@ impl PyMappingProxy {
         self._contains(&key, vm)
     }
 
-    #[pymethod]
-    pub fn items(&self, vm: &VirtualMachine) -> PyResult {
-        let obj = match &self.mapping {
-            MappingProxyInner::Dict(d) => d.clone(),
+    fn to_object(&self, vm: &VirtualMachine) -> PyResult {
+        Ok(match &self.mapping {
+            MappingProxyInner::Mapping(d) => d.as_ref().to_owned(),
             MappingProxyInner::Class(c) => {
                 PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
             }
-        };
+        })
+    }
+
+    #[pymethod]
+    pub fn items(&self, vm: &VirtualMachine) -> PyResult {
+        let obj = self.to_object(vm)?;
         vm.call_method(&obj, identifier!(vm, items).as_str(), ())
     }
     #[pymethod]
     pub fn keys(&self, vm: &VirtualMachine) -> PyResult {
-        let obj = match &self.mapping {
-            MappingProxyInner::Dict(d) => d.clone(),
-            MappingProxyInner::Class(c) => {
-                PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
-            }
-        };
+        let obj = self.to_object(vm)?;
         vm.call_method(&obj, identifier!(vm, keys).as_str(), ())
     }
     #[pymethod]
     pub fn values(&self, vm: &VirtualMachine) -> PyResult {
-        let obj = match &self.mapping {
-            MappingProxyInner::Dict(d) => d.clone(),
-            MappingProxyInner::Class(c) => {
-                PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
-            }
-        };
+        let obj = self.to_object(vm)?;
         vm.call_method(&obj, identifier!(vm, values).as_str(), ())
     }
     #[pymethod]
     pub fn copy(&self, vm: &VirtualMachine) -> PyResult {
         match &self.mapping {
-            MappingProxyInner::Dict(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()),
+            MappingProxyInner::Mapping(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()),
             MappingProxyInner::Class(c) => {
                 Ok(PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm))
             }
@@ -142,12 +136,7 @@ impl PyMappingProxy {
     }
     #[pymethod(magic)]
     fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
-        let obj = match &self.mapping {
-            MappingProxyInner::Dict(d) => d.clone(),
-            MappingProxyInner::Class(c) => {
-                PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
-            }
-        };
+        let obj = self.to_object(vm)?;
         Ok(format!("mappingproxy({})", obj.repr(vm)?))
     }
 
@@ -185,13 +174,7 @@ impl PyMappingProxy {
 
 impl Iterable for PyMappingProxy {
     fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
-        let obj = match &zelf.mapping {
-            MappingProxyInner::Dict(d) => d.clone(),
-            MappingProxyInner::Class(c) => {
-                // TODO: something that's much more efficient than this
-                PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)
-            }
-        };
+        let obj = zelf.to_object(vm)?;
         let iter = obj.get_iter(vm)?;
         Ok(iter.into())
     }
diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs
index 30816e11f9..b6035ff968 100644
--- a/vm/src/function/protocol.rs
+++ b/vm/src/function/protocol.rs
@@ -7,7 +7,7 @@ use crate::{
     types::AsMapping,
     AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
 };
-use std::{borrow::Borrow, marker::PhantomData};
+use std::{borrow::Borrow, marker::PhantomData, ops::Deref};
 
 #[derive(Clone, Debug)]
 pub struct ArgCallable {
@@ -100,24 +100,29 @@ where
     }
 }
 
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct ArgMapping {
     obj: PyObjectRef,
-    mapping_methods: &'static PyMappingMethods,
+    methods: &'static PyMappingMethods,
 }
 
 impl ArgMapping {
+    #[inline]
+    pub fn with_methods(obj: PyObjectRef, methods: &'static PyMappingMethods) -> Self {
+        Self { obj, methods }
+    }
+
     #[inline(always)]
     pub fn from_dict_exact(dict: PyDictRef) -> Self {
         Self {
             obj: dict.into(),
-            mapping_methods: &PyDict::AS_MAPPING,
+            methods: &PyDict::AS_MAPPING,
         }
     }
 
     #[inline(always)]
     pub fn mapping(&self) -> PyMapping {
-        PyMapping::with_methods(&self.obj, self.mapping_methods)
+        PyMapping::with_methods(&self.obj, self.methods)
     }
 }
 
@@ -135,6 +140,14 @@ impl AsRef<PyObject> for ArgMapping {
     }
 }
 
+impl Deref for ArgMapping {
+    type Target = PyObject;
+    #[inline(always)]
+    fn deref(&self) -> &PyObject {
+        &self.obj
+    }
+}
+
 impl From<ArgMapping> for PyObjectRef {
     #[inline(always)]
     fn from(value: ArgMapping) -> PyObjectRef {
@@ -152,11 +165,8 @@ impl ToPyObject for ArgMapping {
 impl TryFromObject for ArgMapping {
     fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
         let mapping = PyMapping::try_protocol(&obj, vm)?;
-        let mapping_methods = mapping.methods;
-        Ok(Self {
-            obj,
-            mapping_methods,
-        })
+        let methods = mapping.methods;
+        Ok(Self { obj, methods })
     }
 }
 
diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs
index 84ff355472..b19818cc12 100644
--- a/vm/src/protocol/mapping.rs
+++ b/vm/src/protocol/mapping.rs
@@ -17,6 +17,12 @@ pub struct PyMappingMethods {
         Option<fn(&PyMapping, &PyObject, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>>,
 }
 
+impl std::fmt::Debug for PyMappingMethods {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "mapping methods")
+    }
+}
+
 impl PyMappingMethods {
     fn check(&self) -> bool {
         self.subscript.is_some()

From 386057cdf55bbb711e4797b6fc7d39ff325fced6 Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sun, 29 May 2022 02:05:19 +0900
Subject: [PATCH 6/9] Add fannkuch.py

---
 benches/benchmarks/fannkuch.py | 55 ++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 benches/benchmarks/fannkuch.py

diff --git a/benches/benchmarks/fannkuch.py b/benches/benchmarks/fannkuch.py
new file mode 100644
index 0000000000..6b4a5dc113
--- /dev/null
+++ b/benches/benchmarks/fannkuch.py
@@ -0,0 +1,55 @@
+"""
+The Computer Language Benchmarks Game
+http://benchmarksgame.alioth.debian.org/
+
+Contributed by Sokolov Yura, modified by Tupteq.
+"""
+
+# import pyperf
+
+
+DEFAULT_ARG = 9
+
+
+def fannkuch(n):
+    count = list(range(1, n + 1))
+    max_flips = 0
+    m = n - 1
+    r = n
+    perm1 = list(range(n))
+    perm = list(range(n))
+    perm1_ins = perm1.insert
+    perm1_pop = perm1.pop
+
+    while 1:
+        while r != 1:
+            count[r - 1] = r
+            r -= 1
+
+        if perm1[0] != 0 and perm1[m] != m:
+            perm = perm1[:]
+            flips_count = 0
+            k = perm[0]
+            while k:
+                perm[:k + 1] = perm[k::-1]
+                flips_count += 1
+                k = perm[0]
+
+            if flips_count > max_flips:
+                max_flips = flips_count
+
+        while r != n:
+            perm1_ins(r, perm1_pop(0))
+            count[r] -= 1
+            if count[r] > 0:
+                break
+            r += 1
+        else:
+            return max_flips
+
+
+if __name__ == "__main__":
+    #runner = pyperf.Runner()
+    arg = DEFAULT_ARG
+    #runner.bench_func('fannkuch', fannkuch, arg)
+    fannkuch(arg)

From 38a36d75935a30af894f655bf4be5636b9428c32 Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sun, 29 May 2022 02:22:34 +0900
Subject: [PATCH 7/9] Simplify AsSequence

---
 vm/src/builtins/bytearray.rs    | 10 ++--------
 vm/src/builtins/bytes.rs        | 10 ++--------
 vm/src/builtins/dict.rs         | 31 +++++------------------------
 vm/src/builtins/list.rs         | 12 ++---------
 vm/src/builtins/mappingproxy.rs | 12 +----------
 vm/src/builtins/memory.rs       | 10 ++--------
 vm/src/builtins/range.rs        | 35 +++++++++++++--------------------
 vm/src/builtins/set.rs          | 23 ++--------------------
 vm/src/builtins/str.rs          | 11 +----------
 vm/src/builtins/tuple.rs        | 13 ++----------
 vm/src/stdlib/collections.rs    | 11 +----------
 vm/src/types/slot.rs            |  9 ++++-----
 12 files changed, 38 insertions(+), 149 deletions(-)

diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs
index 1a6d314463..94fcdb5401 100644
--- a/vm/src/builtins/bytearray.rs
+++ b/vm/src/builtins/bytearray.rs
@@ -36,7 +36,7 @@ use crate::{
     TryFromBorrowedObject, TryFromObject, VirtualMachine,
 };
 use bstr::ByteSlice;
-use std::{borrow::Cow, mem::size_of};
+use std::mem::size_of;
 
 #[pyclass(module = false, name = "bytearray")]
 #[derive(Debug, Default)]
@@ -784,13 +784,7 @@ impl AsMapping for PyByteArray {
 }
 
 impl AsSequence for PyByteArray {
-    fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-
-impl PyByteArray {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         concat: Some(|seq, other, vm| {
             Self::sequence_downcast(seq)
diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs
index e680eded4c..3ba95683b2 100644
--- a/vm/src/builtins/bytes.rs
+++ b/vm/src/builtins/bytes.rs
@@ -25,7 +25,7 @@ use crate::{
     TryFromBorrowedObject, TryFromObject, VirtualMachine,
 };
 use bstr::ByteSlice;
-use std::{borrow::Cow, mem::size_of, ops::Deref};
+use std::{mem::size_of, ops::Deref};
 
 #[pyclass(module = false, name = "bytes")]
 #[derive(Clone, Debug)]
@@ -577,13 +577,7 @@ impl AsMapping for PyBytes {
 }
 
 impl AsSequence for PyBytes {
-    fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-
-impl PyBytes {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         concat: Some(|seq, other, vm| {
             Self::sequence_downcast(seq)
diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs
index dd2d3c6947..9d88d19187 100644
--- a/vm/src/builtins/dict.rs
+++ b/vm/src/builtins/dict.rs
@@ -25,7 +25,7 @@ use crate::{
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
 };
 use rustpython_common::lock::PyMutex;
-use std::{borrow::Cow, fmt};
+use std::fmt;
 
 pub type DictContentType = dictdatatype::Dict;
 
@@ -478,13 +478,7 @@ impl AsMapping for PyDict {
 }
 
 impl AsSequence for PyDict {
-    fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-
-impl PyDict {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         contains: Some(|seq, target, vm| Self::sequence_downcast(seq).entries.contains(vm, target)),
         ..*PySequenceMethods::not_implemented()
     };
@@ -1054,12 +1048,7 @@ impl Comparable for PyDictKeys {
 }
 
 impl AsSequence for PyDictKeys {
-    fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-impl PyDictKeys {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         contains: Some(|seq, target, vm| {
             Self::sequence_downcast(seq)
@@ -1108,12 +1097,7 @@ impl Comparable for PyDictItems {
 }
 
 impl AsSequence for PyDictItems {
-    fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-impl PyDictItems {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         contains: Some(|seq, target, vm| {
             Self::sequence_downcast(seq)
@@ -1130,12 +1114,7 @@ impl PyDictValues {}
 impl Unconstructible for PyDictValues {}
 
 impl AsSequence for PyDictValues {
-    fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-impl PyDictValues {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         ..*PySequenceMethods::not_implemented()
     };
diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs
index 46ceaac074..139b358fcf 100644
--- a/vm/src/builtins/list.rs
+++ b/vm/src/builtins/list.rs
@@ -19,7 +19,7 @@ use crate::{
     vm::VirtualMachine,
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
 };
-use std::{borrow::Cow, fmt, ops::DerefMut};
+use std::{fmt, ops::DerefMut};
 
 /// Built-in mutable sequence.
 ///
@@ -404,15 +404,7 @@ impl AsMapping for PyList {
 }
 
 impl AsSequence for PyList {
-    fn as_sequence(
-        _zelf: &crate::Py<Self>,
-        _vm: &VirtualMachine,
-    ) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHDOS)
-    }
-}
-impl PyList {
-    const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         concat: Some(|seq, other, vm| {
             Self::sequence_downcast(seq)
diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index 507375de86..d9760c798f 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -7,7 +7,6 @@ use crate::{
     types::{AsMapping, AsSequence, Constructor, Iterable},
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
 };
-use std::borrow::Cow;
 
 #[pyclass(module = false, name = "mappingproxy")]
 #[derive(Debug)]
@@ -157,16 +156,7 @@ impl AsMapping for PyMappingProxy {
 }
 
 impl AsSequence for PyMappingProxy {
-    fn as_sequence(
-        _zelf: &crate::Py<Self>,
-        _vm: &VirtualMachine,
-    ) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-
-impl PyMappingProxy {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         contains: Some(|seq, target, vm| Self::sequence_downcast(seq)._contains(target, vm)),
         ..*PySequenceMethods::not_implemented()
     };
diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs
index cba6789f5b..cd5ba4ec91 100644
--- a/vm/src/builtins/memory.rs
+++ b/vm/src/builtins/memory.rs
@@ -24,7 +24,7 @@ use crate::{
 };
 use crossbeam_utils::atomic::AtomicCell;
 use itertools::Itertools;
-use std::{borrow::Cow, cmp::Ordering, fmt::Debug, mem::ManuallyDrop, ops::Range};
+use std::{cmp::Ordering, fmt::Debug, mem::ManuallyDrop, ops::Range};
 
 #[derive(FromArgs)]
 pub struct PyMemoryViewNewArgs {
@@ -976,13 +976,7 @@ impl AsMapping for PyMemoryView {
 }
 
 impl AsSequence for PyMemoryView {
-    fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-
-impl PyMemoryView {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, vm| {
             let zelf = Self::sequence_downcast(seq);
             zelf.try_not_released(vm)?;
diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs
index 5de3d78fae..04fa9a213c 100644
--- a/vm/src/builtins/range.rs
+++ b/vm/src/builtins/range.rs
@@ -16,7 +16,7 @@ use crossbeam_utils::atomic::AtomicCell;
 use num_bigint::{BigInt, Sign};
 use num_integer::Integer;
 use num_traits::{One, Signed, ToPrimitive, Zero};
-use std::{borrow::Cow, cmp::max};
+use std::cmp::max;
 
 // Search flag passed to iter_search
 enum SearchType {
@@ -389,20 +389,6 @@ impl PyRange {
             .try_to_primitive::<isize>(vm)
             .map(|x| x as usize)
     }
-
-    const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods {
-        length: Some(|seq, vm| Self::sequence_downcast(seq).protocol_length(vm)),
-        item: Some(|seq, i, vm| {
-            Self::sequence_downcast(seq)
-                .get(&i.into())
-                .map(|x| PyInt::from(x).into_ref(vm).into())
-                .ok_or_else(|| vm.new_index_error("index out of range".to_owned()))
-        }),
-        contains: Some(|seq, needle, vm| {
-            Ok(Self::sequence_downcast(seq).contains(needle.to_owned(), vm))
-        }),
-        ..*PySequenceMethods::not_implemented()
-    };
 }
 
 impl AsMapping for PyRange {
@@ -416,12 +402,19 @@ impl AsMapping for PyRange {
 }
 
 impl AsSequence for PyRange {
-    fn as_sequence(
-        _zelf: &crate::Py<Self>,
-        _vm: &VirtualMachine,
-    ) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHDOS)
-    }
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
+        length: Some(|seq, vm| Self::sequence_downcast(seq).protocol_length(vm)),
+        item: Some(|seq, i, vm| {
+            Self::sequence_downcast(seq)
+                .get(&i.into())
+                .map(|x| PyInt::from(x).into_ref(vm).into())
+                .ok_or_else(|| vm.new_index_error("index out of range".to_owned()))
+        }),
+        contains: Some(|seq, needle, vm| {
+            Ok(Self::sequence_downcast(seq).contains(needle.to_owned(), vm))
+        }),
+        ..*PySequenceMethods::not_implemented()
+    };
 }
 
 impl Hashable for PyRange {
diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs
index eac707c3ba..3c9ec107af 100644
--- a/vm/src/builtins/set.rs
+++ b/vm/src/builtins/set.rs
@@ -20,7 +20,6 @@ use crate::{
     vm::VirtualMachine,
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
 };
-use std::borrow::Cow;
 use std::{fmt, ops::Deref};
 
 pub type SetContentType = dictdatatype::Dict<()>;
@@ -663,16 +662,7 @@ impl Initializer for PySet {
 }
 
 impl AsSequence for PySet {
-    fn as_sequence(
-        _zelf: &crate::Py<Self>,
-        _vm: &VirtualMachine,
-    ) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-
-impl PySet {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)),
         ..*PySequenceMethods::not_implemented()
@@ -904,16 +894,7 @@ impl PyFrozenSet {
 }
 
 impl AsSequence for PyFrozenSet {
-    fn as_sequence(
-        _zelf: &crate::Py<Self>,
-        _vm: &VirtualMachine,
-    ) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHODS)
-    }
-}
-
-impl PyFrozenSet {
-    const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)),
         ..*PySequenceMethods::not_implemented()
diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs
index 0562bcd548..a6da111a71 100644
--- a/vm/src/builtins/str.rs
+++ b/vm/src/builtins/str.rs
@@ -1309,16 +1309,7 @@ impl AsMapping for PyStr {
 }
 
 impl AsSequence for PyStr {
-    fn as_sequence(
-        _zelf: &Py<Self>,
-        _vm: &VirtualMachine,
-    ) -> std::borrow::Cow<'static, PySequenceMethods> {
-        std::borrow::Cow::Borrowed(&Self::SEQUENCE_METHDOS)
-    }
-}
-
-impl PyStr {
-    const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         concat: Some(|seq, other, vm| {
             let zelf = Self::sequence_downcast(seq);
diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs
index 976a5aaefb..acf7a692eb 100644
--- a/vm/src/builtins/tuple.rs
+++ b/vm/src/builtins/tuple.rs
@@ -17,7 +17,7 @@ use crate::{
     vm::VirtualMachine,
     AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
 };
-use std::{borrow::Cow, fmt, marker::PhantomData};
+use std::{fmt, marker::PhantomData};
 
 /// tuple() -> empty tuple
 /// tuple(iterable) -> tuple initialized from iterable's items
@@ -354,16 +354,7 @@ impl AsMapping for PyTuple {
 }
 
 impl AsSequence for PyTuple {
-    fn as_sequence(
-        _zelf: &crate::Py<Self>,
-        _vm: &VirtualMachine,
-    ) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::SEQUENCE_METHDOS)
-    }
-}
-
-impl PyTuple {
-    const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods {
+    const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         concat: Some(|seq, other, vm| {
             let zelf = Self::sequence_downcast(seq);
diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs
index 508f1b4119..59efa9a674 100644
--- a/vm/src/stdlib/collections.rs
+++ b/vm/src/stdlib/collections.rs
@@ -518,16 +518,7 @@ mod _collections {
     }
 
     impl AsSequence for PyDeque {
-        fn as_sequence(
-            _zelf: &crate::Py<Self>,
-            _vm: &VirtualMachine,
-        ) -> std::borrow::Cow<'static, PySequenceMethods> {
-            std::borrow::Cow::Borrowed(&Self::SEQUENCE_METHDOS)
-        }
-    }
-
-    impl PyDeque {
-        const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods {
+        const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
             length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
             concat: Some(|seq, other, vm| {
                 Self::sequence_downcast(seq)
diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index 0a112c7d7e..287f0d6b69 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -963,15 +963,14 @@ pub trait AsMapping: PyPayload {
 
 #[pyimpl]
 pub trait AsSequence: PyPayload {
+    const AS_SEQUENCE: PySequenceMethods;
+
     #[inline]
     #[pyslot]
-    fn slot_as_sequence(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        let zelf = unsafe { zelf.downcast_unchecked_ref::<Self>() };
-        Self::as_sequence(zelf, vm)
+    fn as_sequence(_zelf: &PyObject, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
+        Cow::Borrowed(&Self::AS_SEQUENCE)
     }
 
-    fn as_sequence(zelf: &Py<Self>, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods>;
-
     fn sequence_downcast<'a>(seq: &'a PySequence) -> &'a Py<Self> {
         unsafe { seq.obj.downcast_unchecked_ref() }
     }

From 4d02fe0aa66022242b36743f76601ecabdf26812 Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sun, 29 May 2022 02:26:45 +0900
Subject: [PATCH 8/9] static_as_sequence_generic

---
 vm/src/builtins/bytes.rs        |   2 +-
 vm/src/builtins/dict.rs         |   8 +--
 vm/src/builtins/mappingproxy.rs |   2 +-
 vm/src/builtins/memory.rs       |   2 +-
 vm/src/builtins/range.rs        |   2 +-
 vm/src/builtins/set.rs          |   4 +-
 vm/src/builtins/str.rs          |   2 +-
 vm/src/builtins/tuple.rs        |   2 +-
 vm/src/protocol/sequence.rs     |  26 +++----
 vm/src/types/slot.rs            | 116 ++++++++++++++++++--------------
 10 files changed, 90 insertions(+), 76 deletions(-)

diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs
index 3ba95683b2..faa8dc5ac0 100644
--- a/vm/src/builtins/bytes.rs
+++ b/vm/src/builtins/bytes.rs
@@ -602,7 +602,7 @@ impl AsSequence for PyBytes {
             let other = <Either<PyBytesInner, PyIntRef>>::try_from_object(vm, other.to_owned())?;
             Self::sequence_downcast(seq).contains(other, vm)
         }),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs
index 9d88d19187..8be379ee1e 100644
--- a/vm/src/builtins/dict.rs
+++ b/vm/src/builtins/dict.rs
@@ -480,7 +480,7 @@ impl AsMapping for PyDict {
 impl AsSequence for PyDict {
     const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         contains: Some(|seq, target, vm| Self::sequence_downcast(seq).entries.contains(vm, target)),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
@@ -1056,7 +1056,7 @@ impl AsSequence for PyDictKeys {
                 .entries
                 .contains(vm, target)
         }),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
@@ -1105,7 +1105,7 @@ impl AsSequence for PyDictItems {
                 .entries
                 .contains(vm, target)
         }),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
@@ -1116,7 +1116,7 @@ impl Unconstructible for PyDictValues {}
 impl AsSequence for PyDictValues {
     const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index d9760c798f..18495155ba 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -158,7 +158,7 @@ impl AsMapping for PyMappingProxy {
 impl AsSequence for PyMappingProxy {
     const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         contains: Some(|seq, target, vm| Self::sequence_downcast(seq)._contains(target, vm)),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs
index cd5ba4ec91..61b28e3bc5 100644
--- a/vm/src/builtins/memory.rs
+++ b/vm/src/builtins/memory.rs
@@ -987,7 +987,7 @@ impl AsSequence for PyMemoryView {
             zelf.try_not_released(vm)?;
             zelf.getitem_by_idx(i, vm)
         }),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs
index 04fa9a213c..312a46efe0 100644
--- a/vm/src/builtins/range.rs
+++ b/vm/src/builtins/range.rs
@@ -413,7 +413,7 @@ impl AsSequence for PyRange {
         contains: Some(|seq, needle, vm| {
             Ok(Self::sequence_downcast(seq).contains(needle.to_owned(), vm))
         }),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs
index 3c9ec107af..a637b3b6ef 100644
--- a/vm/src/builtins/set.rs
+++ b/vm/src/builtins/set.rs
@@ -665,7 +665,7 @@ impl AsSequence for PySet {
     const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
@@ -897,7 +897,7 @@ impl AsSequence for PyFrozenSet {
     const AS_SEQUENCE: PySequenceMethods = PySequenceMethods {
         length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())),
         contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs
index a6da111a71..1700795d6a 100644
--- a/vm/src/builtins/str.rs
+++ b/vm/src/builtins/str.rs
@@ -1325,7 +1325,7 @@ impl AsSequence for PyStr {
                 .map(|x| zelf.new_substr(x.to_string()).into_ref(vm).into())
         }),
         contains: Some(|seq, needle, vm| Self::sequence_downcast(seq)._contains(needle, vm)),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs
index acf7a692eb..59ea9a6447 100644
--- a/vm/src/builtins/tuple.rs
+++ b/vm/src/builtins/tuple.rs
@@ -378,7 +378,7 @@ impl AsSequence for PyTuple {
             let zelf = Self::sequence_downcast(seq);
             zelf._contains(needle, vm)
         }),
-        ..*PySequenceMethods::not_implemented()
+        ..PySequenceMethods::NOT_IMPLEMENTED
     };
 }
 
diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs
index 3af7a0bf98..331d788695 100644
--- a/vm/src/protocol/sequence.rs
+++ b/vm/src/protocol/sequence.rs
@@ -30,9 +30,16 @@ pub struct PySequenceMethods {
 }
 
 impl PySequenceMethods {
-    pub const fn not_implemented() -> &'static Self {
-        &NOT_IMPLEMENTED
-    }
+    pub const NOT_IMPLEMENTED: PySequenceMethods = PySequenceMethods {
+        length: None,
+        concat: None,
+        repeat: None,
+        item: None,
+        ass_item: None,
+        contains: None,
+        inplace_concat: None,
+        inplace_repeat: None,
+    };
 }
 
 impl Debug for PySequenceMethods {
@@ -101,7 +108,7 @@ impl PySequence<'_> {
                     return f(self.obj, vm);
                 }
             }
-            Cow::Borrowed(PySequenceMethods::not_implemented())
+            Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED)
         })
     }
 
@@ -393,14 +400,3 @@ impl PySequence<'_> {
         }
     }
 }
-
-const NOT_IMPLEMENTED: PySequenceMethods = PySequenceMethods {
-    length: None,
-    concat: None,
-    repeat: None,
-    item: None,
-    ass_item: None,
-    contains: None,
-    inplace_concat: None,
-    inplace_repeat: None,
-};
diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index 287f0d6b69..d8b5bd67c8 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -164,16 +164,6 @@ pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResul
 pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>;
 pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> Cow<'static, PySequenceMethods>;
 
-macro_rules! then_some_closure {
-    ($cond:expr, $closure:expr) => {
-        if $cond {
-            Some($closure)
-        } else {
-            None
-        }
-    };
-}
-
 fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> {
     let ret = vm.call_special_method(obj.to_owned(), identifier!(vm, __len__), ())?;
     let len = ret.payload::<PyInt>().ok_or_else(|| {
@@ -282,45 +272,73 @@ fn as_mapping_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PyMappin
     static_as_mapping_generic(has_length, has_subscript, has_ass_subscript)
 }
 
-fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
+pub(crate) fn static_as_sequence_generic(
+    has_length: bool,
+    has_ass_item: bool,
+) -> &'static PySequenceMethods {
+    static METHODS: &[PySequenceMethods] = &[
+        new_generic(false, false),
+        new_generic(true, false),
+        new_generic(false, true),
+        new_generic(true, true),
+    ];
+
+    fn length(seq: &PySequence, vm: &VirtualMachine) -> PyResult<usize> {
+        length_wrapper(seq.obj, vm)
+    }
+    fn item(seq: &PySequence, i: isize, vm: &VirtualMachine) -> PyResult {
+        vm.call_special_method(seq.obj.to_owned(), identifier!(vm, __getitem__), (i,))
+    }
+    fn ass_item(
+        seq: &PySequence,
+        i: isize,
+        value: Option<PyObjectRef>,
+        vm: &VirtualMachine,
+    ) -> PyResult<()> {
+        match value {
+            Some(value) => vm
+                .call_special_method(
+                    seq.obj.to_owned(),
+                    identifier!(vm, __setitem__),
+                    (i.to_pyobject(vm), value),
+                )
+                .map(|_| Ok(()))?,
+            None => vm
+                .call_special_method(
+                    seq.obj.to_owned(),
+                    identifier!(vm, __delitem__),
+                    (i.to_pyobject(vm),),
+                )
+                .map(|_| Ok(()))?,
+        }
+    }
+
+    const fn new_generic(has_length: bool, has_ass_item: bool) -> PySequenceMethods {
+        PySequenceMethods {
+            length: if has_length { Some(length) } else { None },
+            item: Some(item),
+            ass_item: if has_ass_item { Some(ass_item) } else { None },
+            ..PySequenceMethods::NOT_IMPLEMENTED
+        }
+    }
+
+    let key = bool_int(has_length) | (bool_int(has_ass_item) << 1);
+
+    &METHODS[key]
+}
+
+fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
     if !zelf.class().has_attr(identifier!(vm, __getitem__)) {
-        return Cow::Borrowed(PySequenceMethods::not_implemented());
-    }
-
-    Cow::Owned(PySequenceMethods {
-        length: then_some_closure!(
-            zelf.class().has_attr(identifier!(vm, __len__)),
-            |seq, vm| { length_wrapper(seq.obj, vm) }
-        ),
-        item: Some(|seq, i, vm| {
-            vm.call_special_method(
-                seq.obj.to_owned(),
-                identifier!(vm, __getitem__),
-                (i.to_pyobject(vm),),
-            )
-        }),
-        ass_item: then_some_closure!(
-            zelf.class().has_attr(identifier!(vm, __setitem__))
-                | zelf.class().has_attr(identifier!(vm, __delitem__)),
-            |seq, i, value, vm| match value {
-                Some(value) => vm
-                    .call_special_method(
-                        seq.obj.to_owned(),
-                        identifier!(vm, __setitem__),
-                        (i.to_pyobject(vm), value),
-                    )
-                    .map(|_| Ok(()))?,
-                None => vm
-                    .call_special_method(
-                        seq.obj.to_owned(),
-                        identifier!(vm, __delitem__),
-                        (i.to_pyobject(vm),)
-                    )
-                    .map(|_| Ok(()))?,
-            }
-        ),
-        ..Default::default()
-    })
+        return Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED);
+    }
+
+    let (has_length, has_ass_item) = (
+        zelf.class().has_attr(identifier!(vm, __len__)),
+        zelf.class().has_attr(identifier!(vm, __setitem__))
+            | zelf.class().has_attr(identifier!(vm, __delitem__)),
+    );
+
+    Cow::Borrowed(static_as_sequence_generic(has_length, has_ass_item))
 }
 
 fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
@@ -436,7 +454,7 @@ impl PyType {
         match name.as_str() {
             "__len__" | "__getitem__" | "__setitem__" | "__delitem__" => {
                 update_slot!(as_mapping, as_mapping_generic);
-                update_slot!(as_sequence, as_sequence_wrapper);
+                update_slot!(as_sequence, as_sequence_generic);
             }
             "__hash__" => {
                 update_slot!(hash, hash_wrapper);

From 4546661105a353ee4f9908b9502bc66272908068 Mon Sep 17 00:00:00 2001
From: Jeong Yunwon <jeong@youknowone.org>
Date: Sun, 29 May 2022 03:40:08 +0900
Subject: [PATCH 9/9] &'static PySequenceMethods

---
 vm/src/builtins/iter.rs         |  10 +-
 vm/src/builtins/list.rs         |  31 ++++++-
 vm/src/builtins/mappingproxy.rs |   2 +-
 vm/src/protocol/mapping.rs      |   4 +-
 vm/src/protocol/object.rs       |  39 ++++----
 vm/src/protocol/sequence.rs     | 159 +++++++++++++-------------------
 vm/src/types/slot.rs            |  17 ++--
 7 files changed, 123 insertions(+), 139 deletions(-)

diff --git a/vm/src/builtins/iter.rs b/vm/src/builtins/iter.rs
index e1d588dfb3..98bdb4a414 100644
--- a/vm/src/builtins/iter.rs
+++ b/vm/src/builtins/iter.rs
@@ -14,7 +14,6 @@ use rustpython_common::{
     lock::{PyMutex, PyRwLock, PyRwLockUpgradableReadGuard},
     static_cell,
 };
-use std::borrow::Cow;
 
 /// Marks status of iterator.
 #[derive(Debug, Clone)]
@@ -163,7 +162,7 @@ pub fn builtins_reversed(vm: &VirtualMachine) -> &PyObject {
 #[derive(Debug)]
 pub struct PySequenceIterator {
     // cached sequence methods
-    seq_methods: Cow<'static, PySequenceMethods>,
+    seq_methods: &'static PySequenceMethods,
     internal: PyMutex<PositionIterInternal<PyObjectRef>>,
 }
 
@@ -177,9 +176,8 @@ impl PyPayload for PySequenceIterator {
 impl PySequenceIterator {
     pub fn new(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
         let seq = PySequence::try_protocol(obj.as_ref(), vm)?;
-        let seq_methods = seq.methods_cow(vm).clone();
         Ok(Self {
-            seq_methods,
+            seq_methods: seq.methods,
             internal: PyMutex::new(PositionIterInternal::new(obj, 0)),
         })
     }
@@ -188,7 +186,7 @@ impl PySequenceIterator {
     fn length_hint(&self, vm: &VirtualMachine) -> PyObjectRef {
         let internal = self.internal.lock();
         if let IterStatus::Active(obj) = &internal.status {
-            let seq = PySequence::with_methods(obj, self.seq_methods.clone());
+            let seq = PySequence::with_methods(obj, self.seq_methods);
             seq.length(vm)
                 .map(|x| PyInt::from(x).into_pyobject(vm))
                 .unwrap_or_else(|_| vm.ctx.not_implemented())
@@ -212,7 +210,7 @@ impl IterNextIterable for PySequenceIterator {}
 impl IterNext for PySequenceIterator {
     fn next(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
         zelf.internal.lock().next(|obj, pos| {
-            let seq = PySequence::with_methods(obj, zelf.seq_methods.clone());
+            let seq = PySequence::with_methods(obj, zelf.seq_methods);
             PyIterReturn::from_getitem_result(seq.get_item(pos as isize, vm), vm)
         })
     }
diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs
index 139b358fcf..27a22ea284 100644
--- a/vm/src/builtins/list.rs
+++ b/vm/src/builtins/list.rs
@@ -139,7 +139,7 @@ impl PyList {
     }
 
     fn inplace_concat(zelf: &Py<Self>, other: &PyObject, vm: &VirtualMachine) -> PyObjectRef {
-        if let Ok(mut seq) = PySequence::from(other).extract_cloned(Ok, vm) {
+        if let Ok(mut seq) = extract_cloned(other, Ok, vm) {
             zelf.borrow_vec_mut().append(&mut seq);
             zelf.to_owned().into()
         } else {
@@ -149,7 +149,7 @@ impl PyList {
 
     #[pymethod(magic)]
     fn iadd(zelf: PyRef<Self>, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
-        if let Ok(mut seq) = PySequence::from(other.as_ref()).extract_cloned(Ok, vm) {
+        if let Ok(mut seq) = extract_cloned(&*other, Ok, vm) {
             zelf.borrow_vec_mut().append(&mut seq);
             zelf.into()
         } else {
@@ -215,7 +215,7 @@ impl PyList {
         match SequenceIndex::try_from_borrowed_object(vm, needle)? {
             SequenceIndex::Int(index) => self.borrow_vec_mut().set_item_by_index(vm, index, value),
             SequenceIndex::Slice(slice) => {
-                let sec = PySequence::from(value.as_ref()).extract_cloned(Ok, vm)?;
+                let sec = extract_cloned(&*value, Ok, vm)?;
                 self.borrow_vec_mut().set_item_by_slice(vm, slice, &sec)
             }
         }
@@ -352,6 +352,31 @@ impl PyList {
     }
 }
 
+fn extract_cloned<F, R>(obj: &PyObject, mut f: F, vm: &VirtualMachine) -> PyResult<Vec<R>>
+where
+    F: FnMut(PyObjectRef) -> PyResult<R>,
+{
+    use crate::builtins::PyTuple;
+    if let Some(tuple) = obj.payload_if_exact::<PyTuple>(vm) {
+        tuple.iter().map(|x| f(x.clone())).collect()
+    } else if let Some(list) = obj.payload_if_exact::<PyList>(vm) {
+        list.borrow_vec().iter().map(|x| f(x.clone())).collect()
+    } else {
+        let iter = obj.to_owned().get_iter(vm)?;
+        let iter = iter.iter::<PyObjectRef>(vm)?;
+        let len = PySequence::new(obj, vm)
+            .and_then(|seq| seq.length_opt(vm))
+            .transpose()?
+            .unwrap_or(0);
+        let mut v = Vec::with_capacity(len);
+        for x in iter {
+            v.push(f(x?)?);
+        }
+        v.shrink_to_fit();
+        Ok(v)
+    }
+}
+
 impl<'a> MutObjectSequenceOp<'a> for PyList {
     type Guard = PyMappedRwLockReadGuard<'a, [PyObjectRef]>;
 
diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs
index 18495155ba..d603bca740 100644
--- a/vm/src/builtins/mappingproxy.rs
+++ b/vm/src/builtins/mappingproxy.rs
@@ -91,7 +91,7 @@ impl PyMappingProxy {
             MappingProxyInner::Class(class) => Ok(key
                 .as_interned_str(vm)
                 .map_or(false, |key| class.attributes.read().contains_key(key))),
-            MappingProxyInner::Mapping(obj) => PySequence::from(obj.as_ref()).contains(key, vm),
+            MappingProxyInner::Mapping(mapping) => PySequence::contains(&*mapping, key, vm),
         }
     }
 
diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs
index b19818cc12..48663e7a8f 100644
--- a/vm/src/protocol/mapping.rs
+++ b/vm/src/protocol/mapping.rs
@@ -69,9 +69,7 @@ impl PyMapping<'_> {
     // PyMapping::Check
     #[inline]
     pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool {
-        Self::find_methods(obj, vm)
-            .and_then(|m| m.subscript)
-            .is_some()
+        Self::find_methods(obj, vm).map_or(false, PyMappingMethods::check)
     }
 
     pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PyMappingMethods> {
diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs
index 31cf76b47b..b475b78f02 100644
--- a/vm/src/protocol/object.rs
+++ b/vm/src/protocol/object.rs
@@ -522,8 +522,8 @@ impl PyObject {
     // int PyObject_TypeCheck(PyObject *o, PyTypeObject *type)
 
     pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> {
-        PySequence::from(self)
-            .length_opt(vm)
+        PySequence::new(self, vm)
+            .and_then(|seq| seq.length_opt(vm))
             .or_else(|| PyMapping::new(self, vm).and_then(|mapping| mapping.length_opt(vm)))
     }
 
@@ -576,17 +576,17 @@ impl PyObject {
                 return f(&mapping, &needle, Some(value), vm);
             }
         }
-
-        let seq = PySequence::from(self);
-        if let Some(f) = seq.methods(vm).ass_item {
-            let i = needle.key_as_isize(vm)?;
-            f(&seq, i, Some(value), vm)
-        } else {
-            Err(vm.new_type_error(format!(
-                "'{}' does not support item assignment",
-                self.class()
-            )))
+        if let Some(seq) = PySequence::new(self, vm) {
+            if let Some(f) = seq.methods.ass_item {
+                let i = needle.key_as_isize(vm)?;
+                return f(&seq, i, Some(value), vm);
+            }
         }
+
+        Err(vm.new_type_error(format!(
+            "'{}' does not support item assignment",
+            self.class()
+        )))
     }
 
     pub fn del_item<K: DictKey + ?Sized>(&self, needle: &K, vm: &VirtualMachine) -> PyResult<()> {
@@ -600,14 +600,13 @@ impl PyObject {
                 return f(&mapping, &needle, None, vm);
             }
         }
-
-        let seq = PySequence::from(self);
-
-        if let Some(f) = seq.methods(vm).ass_item {
-            let i = needle.key_as_isize(vm)?;
-            f(&seq, i, None, vm)
-        } else {
-            Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class())))
+        if let Some(seq) = PySequence::new(self, vm) {
+            if let Some(f) = seq.methods.ass_item {
+                let i = needle.key_as_isize(vm)?;
+                return f(&seq, i, None, vm);
+            }
         }
+
+        Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class())))
     }
 }
diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs
index 331d788695..fd87c8a1ff 100644
--- a/vm/src/protocol/sequence.rs
+++ b/vm/src/protocol/sequence.rs
@@ -1,22 +1,17 @@
 use crate::{
     builtins::{PyList, PyListRef, PySlice, PyTuple, PyTupleRef},
-    common::lock::OnceCell,
     convert::ToPyObject,
     function::PyArithmeticValue,
     protocol::PyMapping,
     AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
 };
 use itertools::Itertools;
-use std::{
-    borrow::{Borrow, Cow},
-    fmt::Debug,
-};
+use std::fmt::Debug;
 
 // Sequence Protocol
 // https://docs.python.org/3/c-api/sequence.html
 
 #[allow(clippy::type_complexity)]
-#[derive(Default, Clone)]
 pub struct PySequenceMethods {
     pub length: Option<fn(&PySequence, &VirtualMachine) -> PyResult<usize>>,
     pub concat: Option<fn(&PySequence, &PyObject, &VirtualMachine) -> PyResult>,
@@ -40,6 +35,10 @@ impl PySequenceMethods {
         inplace_concat: None,
         inplace_repeat: None,
     };
+
+    pub fn check(&self) -> bool {
+        self.item.is_some()
+    }
 }
 
 impl Debug for PySequenceMethods {
@@ -59,61 +58,50 @@ impl Debug for PySequenceMethods {
 
 pub struct PySequence<'a> {
     pub obj: &'a PyObject,
-    // some function don't need it, so lazy initialize
-    methods: OnceCell<Cow<'static, PySequenceMethods>>,
+    pub methods: &'static PySequenceMethods,
 }
 
-impl<'a> From<&'a PyObject> for PySequence<'a> {
-    fn from(obj: &'a PyObject) -> Self {
-        Self {
-            obj,
-            methods: OnceCell::new(),
-        }
+impl<'a> PySequence<'a> {
+    #[inline]
+    pub fn new(obj: &'a PyObject, vm: &VirtualMachine) -> Option<Self> {
+        let methods = Self::find_methods(obj, vm)?;
+        Some(Self { obj, methods })
     }
-}
 
-impl<'a> PySequence<'a> {
-    pub fn with_methods(obj: &'a PyObject, methods: Cow<'static, PySequenceMethods>) -> Self {
-        Self {
-            obj,
-            methods: OnceCell::from(methods),
-        }
+    #[inline]
+    pub fn with_methods(obj: &'a PyObject, methods: &'static PySequenceMethods) -> Self {
+        Self { obj, methods }
     }
 
     pub fn try_protocol(obj: &'a PyObject, vm: &VirtualMachine) -> PyResult<Self> {
-        let zelf = Self::from(obj);
-        if zelf.check(vm) {
-            Ok(zelf)
-        } else {
-            Err(vm.new_type_error(format!("'{}' is not a sequence", obj.class())))
+        if let Some(methods) = Self::find_methods(obj, vm) {
+            if methods.check() {
+                return Ok(Self::with_methods(obj, methods));
+            }
         }
+
+        Err(vm.new_type_error(format!("'{}' is not a sequence", obj.class())))
     }
 }
 
 impl PySequence<'_> {
     // PySequence_Check
-    pub fn check(&self, vm: &VirtualMachine) -> bool {
-        self.methods(vm).item.is_some()
+    pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool {
+        Self::find_methods(obj, vm).map_or(false, PySequenceMethods::check)
     }
 
-    pub fn methods(&self, vm: &VirtualMachine) -> &PySequenceMethods {
-        self.methods_cow(vm).borrow()
-    }
-
-    pub fn methods_cow(&self, vm: &VirtualMachine) -> &Cow<'static, PySequenceMethods> {
-        self.methods.get_or_init(|| {
-            let cls = self.obj.class();
-            if !cls.is(vm.ctx.types.dict_type) {
-                if let Some(f) = cls.mro_find_map(|x| x.slots.as_sequence.load()) {
-                    return f(self.obj, vm);
-                }
+    pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PySequenceMethods> {
+        let cls = obj.class();
+        if !cls.is(vm.ctx.types.dict_type) {
+            if let Some(f) = cls.mro_find_map(|x| x.slots.as_sequence.load()) {
+                return Some(f(obj, vm));
             }
-            Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED)
-        })
+        }
+        None
     }
 
     pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> {
-        self.methods(vm).length.map(|f| f(self, vm))
+        self.methods.length.map(|f| f(self, vm))
     }
 
     pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> {
@@ -126,12 +114,12 @@ impl PySequence<'_> {
     }
 
     pub fn concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult {
-        if let Some(f) = self.methods(vm).concat {
+        if let Some(f) = self.methods.concat {
             return f(self, other, vm);
         }
 
         // if both arguments apear to be sequences, try fallback to __add__
-        if self.check(vm) && PySequence::from(other).check(vm) {
+        if PySequence::check(other, vm) {
             let ret = vm._add(self.obj, other)?;
             if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
                 return Ok(ret);
@@ -144,30 +132,29 @@ impl PySequence<'_> {
     }
 
     pub fn repeat(&self, n: usize, vm: &VirtualMachine) -> PyResult {
-        if let Some(f) = self.methods(vm).repeat {
+        if let Some(f) = self.methods.repeat {
             return f(self, n, vm);
         }
 
         // try fallback to __mul__
-        if self.check(vm) {
-            let ret = vm._mul(self.obj, &n.to_pyobject(vm))?;
-            if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
-                return Ok(ret);
-            }
+        let ret = vm._mul(self.obj, &n.to_pyobject(vm))?;
+        if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
+            return Ok(ret);
         }
+
         Err(vm.new_type_error(format!("'{}' object can't be repeated", self.obj.class())))
     }
 
     pub fn inplace_concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult {
-        if let Some(f) = self.methods(vm).inplace_concat {
+        if let Some(f) = self.methods.inplace_concat {
             return f(self, other, vm);
         }
-        if let Some(f) = self.methods(vm).concat {
+        if let Some(f) = self.methods.concat {
             return f(self, other, vm);
         }
 
         // if both arguments apear to be sequences, try fallback to __iadd__
-        if self.check(vm) && PySequence::from(other).check(vm) {
+        if PySequence::check(other, vm) {
             let ret = vm._iadd(self.obj, other)?;
             if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
                 return Ok(ret);
@@ -180,24 +167,23 @@ impl PySequence<'_> {
     }
 
     pub fn inplace_repeat(&self, n: usize, vm: &VirtualMachine) -> PyResult {
-        if let Some(f) = self.methods(vm).inplace_repeat {
+        if let Some(f) = self.methods.inplace_repeat {
             return f(self, n, vm);
         }
-        if let Some(f) = self.methods(vm).repeat {
+        if let Some(f) = self.methods.repeat {
             return f(self, n, vm);
         }
 
-        if self.check(vm) {
-            let ret = vm._imul(self.obj, &n.to_pyobject(vm))?;
-            if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
-                return Ok(ret);
-            }
+        let ret = vm._imul(self.obj, &n.to_pyobject(vm))?;
+        if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) {
+            return Ok(ret);
         }
+
         Err(vm.new_type_error(format!("'{}' object can't be repeated", self.obj.class())))
     }
 
     pub fn get_item(&self, i: isize, vm: &VirtualMachine) -> PyResult {
-        if let Some(f) = self.methods(vm).item {
+        if let Some(f) = self.methods.item {
             return f(self, i, vm);
         }
         Err(vm.new_type_error(format!(
@@ -207,7 +193,7 @@ impl PySequence<'_> {
     }
 
     fn _ass_item(&self, i: isize, value: Option<PyObjectRef>, vm: &VirtualMachine) -> PyResult<()> {
-        if let Some(f) = self.methods(vm).ass_item {
+        if let Some(f) = self.methods.ass_item {
             return f(self, i, value, vm);
         }
         Err(vm.new_type_error(format!(
@@ -301,23 +287,6 @@ impl PySequence<'_> {
         Ok(list)
     }
 
-    pub fn contains(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
-        if let Some(f) = self.methods(vm).contains {
-            return f(self, target, vm);
-        }
-
-        let iter = self.obj.to_owned().get_iter(vm)?;
-        let iter = iter.iter::<PyObjectRef>(vm)?;
-
-        for elem in iter {
-            let elem = elem?;
-            if vm.bool_eq(&elem, target)? {
-                return Ok(true);
-            }
-        }
-        Ok(false)
-    }
-
     pub fn count(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult<usize> {
         let mut n = 0;
 
@@ -379,24 +348,22 @@ impl PySequence<'_> {
         }
     }
 
-    pub fn extract_cloned<F, R>(&self, mut f: F, vm: &VirtualMachine) -> PyResult<Vec<R>>
-    where
-        F: FnMut(PyObjectRef) -> PyResult<R>,
-    {
-        if let Some(tuple) = self.obj.payload_if_exact::<PyTuple>(vm) {
-            tuple.iter().map(|x| f(x.clone())).collect()
-        } else if let Some(list) = self.obj.payload_if_exact::<PyList>(vm) {
-            list.borrow_vec().iter().map(|x| f(x.clone())).collect()
-        } else {
-            let iter = self.obj.to_owned().get_iter(vm)?;
-            let iter = iter.iter::<PyObjectRef>(vm)?;
-            let len = self.length(vm).unwrap_or(0);
-            let mut v = Vec::with_capacity(len);
-            for x in iter {
-                v.push(f(x?)?);
+    pub fn contains(zelf: &PyObject, target: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
+        if let Some(seq) = PySequence::new(zelf, vm) {
+            if let Some(contains) = seq.methods.contains {
+                return contains(&seq, target, vm);
+            }
+        }
+
+        let iter = zelf.to_owned().get_iter(vm)?;
+        let iter = iter.iter::<PyObjectRef>(vm)?;
+
+        for elem in iter {
+            let elem = elem?;
+            if vm.bool_eq(&elem, target)? {
+                return Ok(true);
             }
-            v.shrink_to_fit();
-            Ok(v)
         }
+        Ok(false)
     }
 }
diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs
index d8b5bd67c8..245e7a81c8 100644
--- a/vm/src/types/slot.rs
+++ b/vm/src/types/slot.rs
@@ -14,10 +14,7 @@ use crate::{
 };
 use crossbeam_utils::atomic::AtomicCell;
 use num_traits::{Signed, ToPrimitive};
-use std::{
-    borrow::{Borrow, Cow},
-    cmp::Ordering,
-};
+use std::{borrow::Borrow, cmp::Ordering};
 
 // The corresponding field in CPython is `tp_` prefixed.
 // e.g. name -> tp_name
@@ -162,7 +159,7 @@ pub(crate) type DescrSetFunc =
 pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult;
 pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult<()>;
 pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>;
-pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> Cow<'static, PySequenceMethods>;
+pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> &'static PySequenceMethods;
 
 fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> {
     let ret = vm.call_special_method(obj.to_owned(), identifier!(vm, __len__), ())?;
@@ -327,9 +324,9 @@ pub(crate) fn static_as_sequence_generic(
     &METHODS[key]
 }
 
-fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
+fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PySequenceMethods {
     if !zelf.class().has_attr(identifier!(vm, __getitem__)) {
-        return Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED);
+        return &PySequenceMethods::NOT_IMPLEMENTED;
     }
 
     let (has_length, has_ass_item) = (
@@ -338,7 +335,7 @@ fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PyS
             | zelf.class().has_attr(identifier!(vm, __delitem__)),
     );
 
-    Cow::Borrowed(static_as_sequence_generic(has_length, has_ass_item))
+    static_as_sequence_generic(has_length, has_ass_item)
 }
 
 fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
@@ -985,8 +982,8 @@ pub trait AsSequence: PyPayload {
 
     #[inline]
     #[pyslot]
-    fn as_sequence(_zelf: &PyObject, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> {
-        Cow::Borrowed(&Self::AS_SEQUENCE)
+    fn as_sequence(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PySequenceMethods {
+        &Self::AS_SEQUENCE
     }
 
     fn sequence_downcast<'a>(seq: &'a PySequence) -> &'a Py<Self> {