Merge pull request #2231 from youknowone/slot2

slot optimization 2
This commit is contained in:
Jeong YunWon authored and GitHub committed 2020-09-24 22:20:27 +09:00
commit 01e974eadc
12 files changed
+152 -112

No files matched your search

+1 -21
View File
@@ -341,29 +341,9 @@ where
quote! {
slots.#slot_ident = Some(#into_func);
}
} else if slot_name == "cmp" {
let into_func = quote_spanned! {ident.span() =>
::std::boxed::Box::new(Self::#ident)
};
quote! {
slots.#slot_ident = Some(#into_func);
}
} else {
let into_func = if slot_name == "call" {
quote_spanned! {ident.span() =>
|zelf: ::rustpython_vm::pyobject::PyObjectRef, args: ::rustpython_vm::function::PyFuncArgs, vm: &::rustpython_vm::VirtualMachine| -> ::rustpython_vm::pyobject::PyResult {
use ::rustpython_vm::pyobject::TryFromObject;
let zelf = PyRef::<Self>::try_from_object(vm, zelf)?;
Self::#ident(zelf, args, vm)
}
}
} else {
quote_spanned! {ident.span() =>
Self::#ident
}
};
quote! {
slots.#slot_ident.store(Some(#into_func as _))
slots.#slot_ident.store(Some(Self::#ident as _))
}
}
};
+5 -2
View File
@@ -47,11 +47,14 @@ macro_rules! py_class {
py_class
}
};
(@extract_slots($ctx:expr, $slots:expr, (slot $slot_name:ident), $value:expr)) => {
$slots.$slot_name = Some(
(@extract_slots($ctx:expr, $slots:expr, (slot new), $value:expr)) => {
$slots.new = Some(
$crate::function::IntoPyNativeFunc::into_func($value)
);
};
(@extract_slots($ctx:expr, $slots:expr, (slot $slot_name:ident), $value:expr)) => {
$slots.$slot_name.store(Some($value));
};
(@extract_slots($ctx:expr, $class:expr, $name:expr, $value:expr)) => {};
(@extract_attrs($ctx:expr, $slots:expr, (slot $slot_name:ident), $value:expr)) => {};
(@extract_attrs($ctx:expr, $class:expr, $name:expr, $value:expr)) => {
+7 -7
View File
@@ -7,7 +7,7 @@ use crate::obj::objtype::PyClassRef;
use crate::pyobject::{
PyClassImpl, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
};
use crate::slots::{SlotCall, SlotDescriptor};
use crate::slots::{Callable, SlotDescriptor};
use crate::vm::VirtualMachine;
pub struct PyFuncDef {
@@ -106,13 +106,13 @@ impl PyBuiltinFunction {
}
}
impl SlotCall for PyBuiltinFunction {
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl Callable for PyBuiltinFunction {
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
(zelf.value.func)(vm, args)
}
}
#[pyimpl(with(SlotCall), flags(HAS_DICT))]
#[pyimpl(with(Callable), flags(HAS_DICT))]
impl PyBuiltinFunction {
#[pyproperty(magic)]
fn module(&self, vm: &VirtualMachine) -> PyObjectRef {
@@ -186,13 +186,13 @@ impl SlotDescriptor for PyBuiltinMethod {
}
}
impl SlotCall for PyBuiltinMethod {
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl Callable for PyBuiltinMethod {
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
(zelf.value.func)(vm, args)
}
}
#[pyimpl(with(SlotDescriptor, SlotCall))]
#[pyimpl(with(SlotDescriptor, Callable))]
impl PyBuiltinMethod {
#[pyproperty(magic)]
fn name(&self) -> Option<PyStringRef> {
+23 -20
View File
@@ -19,7 +19,7 @@ use crate::pyobject::{
PyRef, PyResult, PyValue, TypeProtocol,
};
use crate::scope::Scope;
use crate::slots::{SlotCall, SlotDescriptor};
use crate::slots::{Callable, Comparable, PyComparisonOp, SlotDescriptor};
use crate::VirtualMachine;
use itertools::Itertools;
#[cfg(feature = "jit")]
@@ -269,7 +269,7 @@ impl PyValue for PyFunction {
}
}
#[pyimpl(with(SlotDescriptor, SlotCall), flags(HAS_DICT))]
#[pyimpl(with(SlotDescriptor, Callable), flags(HAS_DICT))]
impl PyFunction {
#[pyproperty(magic)]
fn code(&self) -> PyCodeRef {
@@ -320,8 +320,8 @@ impl SlotDescriptor for PyFunction {
}
}
impl SlotCall for PyFunction {
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl Callable for PyFunction {
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
zelf.invoke(args, vm)
}
}
@@ -334,14 +334,30 @@ pub struct PyBoundMethod {
pub function: PyObjectRef,
}
impl SlotCall for PyBoundMethod {
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl Callable for PyBoundMethod {
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
let args = args.insert(zelf.object.clone());
vm.invoke(&zelf.function, args)
}
}
#[pyimpl(with(SlotCall), flags(HAS_DICT))]
impl Comparable for PyBoundMethod {
fn cmp(
zelf: PyRef<Self>,
other: PyObjectRef,
op: PyComparisonOp,
_vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
op.eq_only(|| {
let other = class_or_notimplemented!(Self, other);
Ok(PyComparisonValue::Implemented(
zelf.function.is(&other.function) && zelf.object.is(&other.object),
))
})
}
}
#[pyimpl(with(Callable, Comparable), flags(HAS_DICT))]
impl PyBoundMethod {
pub fn new(object: PyObjectRef, function: PyObjectRef) -> Self {
PyBoundMethod { object, function }
@@ -377,19 +393,6 @@ impl PyBoundMethod {
fn module(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
vm.get_attribute(self.function.clone(), "__module__").ok()
}
#[pymethod(magic)]
fn eq(&self, other: PyObjectRef) -> PyResult<PyComparisonValue> {
let other = class_or_notimplemented!(Self, other);
Ok(PyComparisonValue::Implemented(
self.function.is(&other.function) && self.object.is(&other.object),
))
}
#[pymethod(magic)]
fn ne(&self, other: PyObjectRef) -> PyResult<PyComparisonValue> {
Ok(self.eq(other)?.map(|v| !v))
}
}
impl PyValue for PyBoundMethod {
+18 -8
View File
@@ -7,8 +7,8 @@ use crate::common::hash::PyHash;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objtype::PyClass;
use crate::pyobject::{
BorrowValue, IdProtocol, ItemProtocol, PyAttributes, PyClassImpl, PyComparisonValue, PyContext,
PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol,
BorrowValue, Either, IdProtocol, ItemProtocol, PyAttributes, PyClassImpl, PyComparisonValue,
PyContext, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol,
};
use crate::slots::PyComparisonOp;
use crate::vm::VirtualMachine;
@@ -44,6 +44,16 @@ impl PyBaseObject {
other: PyObjectRef,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
Self::cmp(zelf, other, op, vm).map(Either::B)
}
#[inline(always)]
fn cmp(
zelf: PyObjectRef,
other: PyObjectRef,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
let res = match op {
PyComparisonOp::Eq => {
@@ -76,7 +86,7 @@ impl PyBaseObject {
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::tp_cmp(zelf, other, PyComparisonOp::Eq, vm)
Self::cmp(zelf, other, PyComparisonOp::Eq, vm)
}
#[pymethod(magic)]
fn ne(
@@ -84,7 +94,7 @@ impl PyBaseObject {
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::tp_cmp(zelf, other, PyComparisonOp::Ne, vm)
Self::cmp(zelf, other, PyComparisonOp::Ne, vm)
}
#[pymethod(magic)]
fn lt(
@@ -92,7 +102,7 @@ impl PyBaseObject {
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::tp_cmp(zelf, other, PyComparisonOp::Lt, vm)
Self::cmp(zelf, other, PyComparisonOp::Lt, vm)
}
#[pymethod(magic)]
fn le(
@@ -100,7 +110,7 @@ impl PyBaseObject {
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::tp_cmp(zelf, other, PyComparisonOp::Le, vm)
Self::cmp(zelf, other, PyComparisonOp::Le, vm)
}
#[pymethod(magic)]
fn ge(
@@ -108,7 +118,7 @@ impl PyBaseObject {
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::tp_cmp(zelf, other, PyComparisonOp::Ge, vm)
Self::cmp(zelf, other, PyComparisonOp::Ge, vm)
}
#[pymethod(magic)]
fn gt(
@@ -116,7 +126,7 @@ impl PyBaseObject {
other: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
Self::tp_cmp(zelf, other, PyComparisonOp::Gt, vm)
Self::cmp(zelf, other, PyComparisonOp::Gt, vm)
}
#[pymethod(magic)]
+19 -5
View File
@@ -14,10 +14,10 @@ use super::objtuple::PyTuple;
use super::objweakref::PyWeak;
use crate::function::{KwArgs, PyFuncArgs};
use crate::pyobject::{
BorrowValue, IdProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable, PyLease,
BorrowValue, Either, IdProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable, PyLease,
PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
};
use crate::slots::{self, PyClassSlots, PyTpFlags, SlotCall};
use crate::slots::{self, Callable, PyClassSlots, PyTpFlags};
use crate::vm::VirtualMachine;
use itertools::Itertools;
use std::ops::Deref;
@@ -150,6 +150,20 @@ impl PyClassRef {
} as _;
self.slots.hash.store(Some(func));
}
"__del__" => {
let func: slots::DelFunc = |zelf, vm| {
let magic = get_class_magic(&zelf, "__del__");
vm.invoke(&magic, vec![zelf.clone()]).map(|_| ())
} as _;
self.slots.del.store(Some(func));
}
"__eq__" | "__ne__" | "__le__" | "__lt__" | "__ge__" | "__gt__" => {
let func: slots::CmpFunc = |zelf, other, op, vm| {
let magic = get_class_magic(&zelf, op.method_name());
vm.invoke(&magic, vec![zelf, other]).map(Either::A)
} as _;
self.slots.cmp.store(Some(func))
}
_ => (),
}
}
@@ -165,7 +179,7 @@ fn get_class_magic(zelf: &PyObjectRef, name: &str) -> PyObjectRef {
// attrs.get(name).unwrap().clone()
}
#[pyimpl(with(SlotCall), flags(BASETYPE))]
#[pyimpl(with(Callable), flags(BASETYPE))]
impl PyClass {
#[pyproperty(name = "__mro__")]
fn get_mro(zelf: PyRef<Self>) -> PyTuple {
@@ -495,8 +509,8 @@ impl PyClass {
}
}
impl SlotCall for PyClass {
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl Callable for PyClass {
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm_trace!("type_call: {:?}", zelf);
let obj = call_tp_new(zelf.clone(), zelf.clone(), args.clone(), vm)?;
+4 -4
View File
@@ -5,7 +5,7 @@ use crate::pyobject::{
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol,
};
use crate::pyobjectrc::{PyObjectRc, PyObjectWeak};
use crate::slots::{Comparable, Hashable, PyComparisonOp, SlotCall};
use crate::slots::{Callable, Comparable, Hashable, PyComparisonOp};
use crate::vm::VirtualMachine;
use crossbeam_utils::atomic::AtomicCell;
@@ -38,14 +38,14 @@ impl PyValue for PyWeak {
pub type PyWeakRef = PyRef<PyWeak>;
impl SlotCall for PyWeak {
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl Callable for PyWeak {
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
args.bind::<()>(vm)?;
Ok(vm.unwrap_or_none(zelf.upgrade()))
}
}
#[pyimpl(with(SlotCall, Hashable, Comparable), flags(BASETYPE))]
#[pyimpl(with(Callable, Hashable, Comparable), flags(BASETYPE))]
impl PyWeak {
// TODO callbacks
#[pyslot]
+3 -2
View File
@@ -146,11 +146,12 @@ where
// CPython-compatible drop implementation
let zelf = Self::into_ref(self.clone());
if let Some(del_method) = zelf.inner.get_class_attr("__del__") {
if let Some(del_slot) = zelf.class().first_in_mro(|cls| cls.slots.del.load()) {
crate::vm::thread::with_vm(&zelf, |vm| {
if let Err(e) = vm.invoke(&del_method, vec![zelf.clone()]) {
if let Err(e) = del_slot(&zelf, vm) {
// exception in del will be ignored but printed
print!("Exception ignored in: ",);
let del_method = zelf.get_class_attr("__del__").unwrap();
let repr = vm.to_repr(&del_method);
match repr {
Ok(v) => println!("{}", v.to_string()),
+45 -19
View File
@@ -4,7 +4,7 @@ use crate::common::cell::PyRwLock;
use crate::common::hash::PyHash;
use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
use crate::pyobject::{
IdProtocol, PyComparisonValue, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
Either, IdProtocol, PyComparisonValue, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
};
use crate::VirtualMachine;
use crossbeam_utils::atomic::AtomicCell;
@@ -40,33 +40,30 @@ impl Default for PyTpFlags {
}
}
pub(crate) type GenericMethod = fn(PyObjectRef, PyFuncArgs, &VirtualMachine) -> PyResult;
pub(crate) type GenericMethod = fn(&PyObjectRef, PyFuncArgs, &VirtualMachine) -> PyResult;
pub(crate) type DelFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<()>;
pub(crate) type DescrGetFunc =
fn(PyObjectRef, Option<PyObjectRef>, Option<PyObjectRef>, &VirtualMachine) -> PyResult;
pub(crate) type HashFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyHash>;
pub(crate) type CmpFunc = fn(
PyObjectRef,
PyObjectRef,
PyComparisonOp,
&VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>>;
#[derive(Default)]
pub struct PyClassSlots {
pub flags: PyTpFlags,
pub name: PyRwLock<Option<String>>, // tp_name, not class name
pub new: Option<PyNativeFunc>,
pub del: AtomicCell<Option<DelFunc>>,
pub call: AtomicCell<Option<GenericMethod>>,
pub descr_get: AtomicCell<Option<DescrGetFunc>>,
pub hash: AtomicCell<Option<HashFunc>>,
pub cmp: Option<CmpFunc>,
pub cmp: AtomicCell<Option<CmpFunc>>,
}
type CmpFunc = Box<
py_dyn_fn!(
dyn Fn(
PyObjectRef,
PyObjectRef,
PyComparisonOp,
&VirtualMachine,
) -> PyResult<PyComparisonValue>
),
>;
impl PyClassSlots {
pub fn from_flags(flags: PyTpFlags) -> Self {
Self {
@@ -83,10 +80,39 @@ impl std::fmt::Debug for PyClassSlots {
}
#[pyimpl]
pub trait SlotCall: PyValue {
#[pymethod(magic)]
pub trait SlotDesctuctor: PyValue {
#[pyslot]
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult;
fn tp_del(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
if let Some(zelf) = zelf.downcast_ref() {
Self::del(zelf, vm)
} else {
Err(vm.new_type_error("unexpected payload for __del__".to_owned()))
}
}
#[pymethod(magic)]
fn __del__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult<()> {
Self::del(&zelf, vm)
}
fn del(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<()>;
}
#[pyimpl]
pub trait Callable: PyValue {
#[pyslot]
fn tp_call(zelf: &PyObjectRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
if let Some(zelf) = zelf.downcast_ref() {
Self::call(zelf, args, vm)
} else {
Err(vm.new_type_error("unexpected payload for __call__".to_owned()))
}
}
#[pymethod]
fn __call__(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
Self::call(&zelf, args, vm)
}
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult;
}
#[pyimpl]
@@ -193,9 +219,9 @@ pub trait Comparable: PyValue {
other: PyObjectRef,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
let zelf = PyRef::try_from_object(vm, zelf)?;
Self::cmp(zelf, other, op, vm)
Self::cmp(zelf, other, op, vm).map(Either::B)
}
fn cmp(
+7 -2
View File
@@ -416,11 +416,15 @@ fn io_base_cm_enter(instance: PyObjectRef) -> PyObjectRef {
instance
}
fn io_base_cm_del(instance: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
vm.call_method(&instance, "close", vec![])?;
fn io_base_cm_del_slot(instance: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
vm.call_method(instance, "close", vec![])?;
Ok(())
}
fn io_base_cm_del(instance: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
io_base_cm_del_slot(&instance, vm)
}
fn io_base_cm_exit(instance: PyObjectRef, _args: PyFuncArgs, vm: &VirtualMachine) -> PyResult<()> {
vm.call_method(&instance, "close", vec![])?;
Ok(())
@@ -1271,6 +1275,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let io_base = py_class!(ctx, "_IOBase", &ctx.types.object_type, {
"__enter__" => ctx.new_method(io_base_cm_enter),
"__del__" => ctx.new_method(io_base_cm_del),
(slot del) => io_base_cm_del_slot,
"__exit__" => ctx.new_method(io_base_cm_exit),
"seekable" => ctx.new_method(io_base_seekable),
"readable" => ctx.new_method(io_base_readable),
+5 -5
View File
@@ -10,7 +10,7 @@ mod _json {
use crate::pyobject::{
BorrowValue, IdProtocol, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue,
};
use crate::slots::SlotCall;
use crate::slots::Callable;
use crate::VirtualMachine;
use num_bigint::BigInt;
@@ -35,7 +35,7 @@ mod _json {
}
}
#[pyimpl(with(SlotCall))]
#[pyimpl(with(Callable))]
impl JsonScanner {
#[pyslot]
fn tp_new(cls: PyClassRef, ctx: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
@@ -197,7 +197,7 @@ mod _json {
}
fn call(
zelf: PyRef<Self>,
zelf: &PyRef<Self>,
pystr: PyStringRef,
idx: isize,
vm: &VirtualMachine,
@@ -222,8 +222,8 @@ mod _json {
}
}
impl SlotCall for JsonScanner {
fn call(zelf: PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
impl Callable for JsonScanner {
fn call(zelf: &PyRef<Self>, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
let (pystr, idx) = args.bind::<(PyStringRef, isize)>(vm)?;
JsonScanner::call(zelf, pystr, idx, vm)
}
+15 -17
View File
@@ -886,7 +886,7 @@ impl VirtualMachine {
match slot_call {
Some(slot_call) => {
self.trace_event(TraceEvent::Call)?;
let result = slot_call(callable.clone(), args, self);
let result = slot_call(callable, args, self);
self.trace_event(TraceEvent::Return)?;
result
}
@@ -1399,15 +1399,14 @@ impl VirtualMachine {
// TODO: _Py_EnterRecursiveCall(tstate, " in comparison")
let call_cmp = |obj: PyObjectRef, other, op| {
let ret = if let Some(ref cmp) = obj.class().slots.cmp {
Some(cmp(obj, other, op, self)?.map(Either::B))
} else if let Some(method) = self.get_method(obj, op.method_name()).transpose()? {
let ret = self.invoke(&method, vec![other])?;
Some(PyArithmaticValue::from_object(self, ret).map(Either::A))
} else {
None
};
Ok(ret)
let cmp = obj
.class()
.first_in_mro(|cls| cls.slots.cmp.load())
.unwrap();
Ok(match cmp(obj, other, op, self)? {
Either::A(obj) => PyArithmaticValue::from_object(self, obj).map(Either::A),
Either::B(arithmatic) => arithmatic.map(Either::B),
})
};
let mut checked_reverse_op = false;
@@ -1417,19 +1416,18 @@ impl VirtualMachine {
!v_class.is(&w_class) && objtype::issubclass(&w_class, &v_class)
};
if is_strict_subclass {
if let Some(res) = call_cmp(w.clone(), v.clone(), swapped)? {
checked_reverse_op = true;
if let PyArithmaticValue::Implemented(x) = res {
return Ok(x);
}
let res = call_cmp(w.clone(), v.clone(), swapped)?;
checked_reverse_op = true;
if let PyArithmaticValue::Implemented(x) = res {
return Ok(x);
}
}
if let Some(PyArithmaticValue::Implemented(x)) = call_cmp(v.clone(), w.clone(), op)? {
if let PyArithmaticValue::Implemented(x) = call_cmp(v.clone(), w.clone(), op)? {
return Ok(x);
}
if !checked_reverse_op {
let res = call_cmp(w.clone(), v.clone(), swapped)?;
if let Some(PyArithmaticValue::Implemented(x)) = res {
if let PyArithmaticValue::Implemented(x) = res {
return Ok(x);
}
}