From 13d715e2a508441ced461ce92680b4c53bbc7693 Mon Sep 17 00:00:00 2001 From: Karatus Date: Sun, 28 Aug 2022 16:12:02 +0900 Subject: [PATCH 01/17] Binary ops with number protocol (partially imported) from #4139 https://github.com/RustPython/RustPython/pull/4139 --- vm/src/protocol/mod.rs | 2 +- vm/src/protocol/number.rs | 110 +++++++- vm/src/stdlib/builtins.rs | 12 +- vm/src/vm/vm_ops.rs | 525 +++++++++++++++++++++++++++----------- 4 files changed, 479 insertions(+), 170 deletions(-) diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs index 4170c20df6..f355b61ea4 100644 --- a/vm/src/protocol/mod.rs +++ b/vm/src/protocol/mod.rs @@ -10,5 +10,5 @@ pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, V pub use callable::PyCallable; pub use iter::{PyIter, PyIterIter, PyIterReturn}; pub use mapping::{PyMapping, PyMappingMethods}; -pub use number::{PyNumber, PyNumberMethods}; +pub use number::{PyNumber, PyNumberMethods, PyNumberMethodsOffset}; pub use sequence::{PySequence, PySequenceMethods}; diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index bbf88de6ab..8abc54e1bc 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -8,6 +8,7 @@ use crate::{ VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; +use std::ptr; type UnaryFunc = AtomicCell PyResult>>; type BinaryFunc = @@ -109,6 +110,7 @@ impl PyObject { } #[derive(Default)] +// #[repr(C)] pub struct PyNumberMethods { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions @@ -199,6 +201,98 @@ impl PyNumberMethods { }; } +pub enum PyNumberMethodsOffset { + Add, + Subtract, + Multiply, + Remainder, + Divmod, + Power, + Negative, + Positive, + Absolute, + Boolean, + Invert, + Lshift, + Rshift, + And, + Xor, + Or, + Int, + Float, + InplaceAdd, + InplaceSubtract, + InplaceMultiply, + InplaceRemainder, + InplaceDivmod, + InplacePower, + InplaceLshift, + InplaceRshift, + InplaceAnd, + InplaceXor, + InplaceOr, + FloorDivide, + TrueDivide, + InplaceFloorDivide, + InplaceTrueDivide, + Index, + MatrixMultiply, + InplaceMatrixMultiply, +} + +impl PyNumberMethodsOffset { + pub fn method(&self, methods: &PyNumberMethods, vm: &VirtualMachine) -> PyResult<&BinaryFunc> { + use PyNumberMethodsOffset::*; + unsafe { + match self { + // BinaryFunc + Add => ptr::addr_of!(methods.add), + Subtract => ptr::addr_of!(methods.subtract), + Multiply => ptr::addr_of!(methods.multiply), + Remainder => ptr::addr_of!(methods.remainder), + Divmod => ptr::addr_of!(methods.divmod), + Power => ptr::addr_of!(methods.power), + Lshift => ptr::addr_of!(methods.lshift), + Rshift => ptr::addr_of!(methods.rshift), + And => ptr::addr_of!(methods.and), + Xor => ptr::addr_of!(methods.xor), + Or => ptr::addr_of!(methods.or), + InplaceAdd => ptr::addr_of!(methods.inplace_add), + InplaceSubtract => ptr::addr_of!(methods.inplace_subtract), + InplaceMultiply => ptr::addr_of!(methods.inplace_multiply), + InplaceRemainder => ptr::addr_of!(methods.inplace_remainder), + InplaceDivmod => ptr::addr_of!(methods.inplace_divmod), + InplacePower => ptr::addr_of!(methods.inplace_power), + InplaceLshift => ptr::addr_of!(methods.inplace_lshift), + InplaceRshift => ptr::addr_of!(methods.inplace_rshift), + InplaceAnd => ptr::addr_of!(methods.inplace_and), + InplaceXor => ptr::addr_of!(methods.inplace_xor), + InplaceOr => ptr::addr_of!(methods.inplace_or), + FloorDivide => ptr::addr_of!(methods.floor_divide), + TrueDivide => ptr::addr_of!(methods.true_divide), + InplaceFloorDivide => ptr::addr_of!(methods.inplace_floor_divide), + InplaceTrueDivide => ptr::addr_of!(methods.inplace_true_divide), + MatrixMultiply => ptr::addr_of!(methods.matrix_multiply), + InplaceMatrixMultiply => ptr::addr_of!(methods.inplace_matrix_multiply), + // UnaryFunc + Negative => ptr::null(), + Positive => ptr::null(), + Absolute => ptr::null(), + Boolean => ptr::null(), + Invert => ptr::null(), + Int => ptr::null(), + Float => ptr::null(), + Index => ptr::null(), + } + .as_ref() + .ok_or_else(|| { + vm.new_value_error("No unaryop supported for PyNumberMethodsOffset".to_owned()) + }) + } + } +} + +#[derive(Copy, Clone)] pub struct PyNumber<'a> { pub obj: &'a PyObject, methods: &'a PyNumberMethods, @@ -220,8 +314,12 @@ impl PyNumber<'_> { obj.class().mro_find_map(|x| x.slots.as_number.load()) } - pub fn methods(&self) -> &PyNumberMethods { - self.methods + pub fn methods<'a>( + &'a self, + op_slot: &'a PyNumberMethodsOffset, + vm: &VirtualMachine, + ) -> PyResult<&BinaryFunc> { + op_slot.method(self.methods, vm) } // PyNumber_Check @@ -238,12 +336,12 @@ impl PyNumber<'_> { // PyIndex_Check pub fn is_index(&self) -> bool { - self.methods().index.load().is_some() + self.methods.index.load().is_some() } #[inline] pub fn int(self, vm: &VirtualMachine) -> Option> { - self.methods().int.load().map(|f| { + self.methods.int.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -267,7 +365,7 @@ impl PyNumber<'_> { #[inline] pub fn index(self, vm: &VirtualMachine) -> Option> { - self.methods().index.load().map(|f| { + self.methods.index.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -291,7 +389,7 @@ impl PyNumber<'_> { #[inline] pub fn float(self, vm: &VirtualMachine) -> Option>> { - self.methods().float.load().map(|f| { + self.methods.float.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyFloat::class(vm)) { warnings::warn( diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index fa2d917682..4ebdbd4f02 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -21,7 +21,7 @@ mod builtins { ArgBytesLike, ArgCallable, ArgIntoBool, ArgIterable, ArgMapping, ArgStrOrBytesLike, Either, FuncArgs, KwArgs, OptionalArg, OptionalOption, PosArgs, PyArithmeticValue, }, - protocol::{PyIter, PyIterReturn}, + protocol::{PyIter, PyIterReturn, PyNumberMethodsOffset}, py_io, readline::{Readline, ReadlineResult}, stdlib::sys, @@ -605,13 +605,9 @@ mod builtins { modulus, } = args; match modulus { - None => vm.call_or_reflection( - &x, - &y, - identifier!(vm, __pow__), - identifier!(vm, __rpow__), - |vm, x, y| Err(vm.new_unsupported_binop_error(x, y, "pow")), - ), + None => vm.binary_op(&x, &y, PyNumberMethodsOffset::Power, "pow", |vm, _, _| { + Ok(vm.ctx.not_implemented()) + }), Some(z) => { let try_pow_value = |obj: &PyObject, args: (PyObjectRef, PyObjectRef, PyObjectRef)| diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index c0530a6916..c7a2818035 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -3,9 +3,10 @@ use crate::{ builtins::{PyInt, PyIntRef, PyStr, PyStrInterned, PyStrRef}, function::PyArithmeticValue, object::{AsObject, PyObject, PyObjectRef, PyResult}, - protocol::PyIterReturn, + protocol::{PyIterReturn, PyNumberMethodsOffset, PySequence}, types::PyComparisonOp, }; +use num_traits::ToPrimitive; /// Collection of operators impl VirtualMachine { @@ -103,6 +104,7 @@ impl VirtualMachine { } } + // TODO: Should be deleted after transplanting complete number protocol /// Calls a method on `obj` passing `arg`, if the method exists. /// /// Otherwise, or if the result is the special `NotImplemented` built-in constant, @@ -128,6 +130,7 @@ impl VirtualMachine { unsupported(self, obj, arg) } + // TODO: Should be deleted after transplanting complete number protocol /// Calls a method, falling back to its reflection with the operands /// reversed, and then to the value provided by `unsupported`. /// @@ -174,160 +177,348 @@ impl VirtualMachine { }) } - pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __sub__), - identifier!(self, __rsub__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-")), - ) + fn binary_op1( + &self, + a: &PyObject, + b: &PyObject, + op_slot: PyNumberMethodsOffset, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let num_a = a.to_number(); + let num_b = b.to_number(); + + let slot_a = num_a.methods(&op_slot, self)?.load(); + let slot_b = num_b.methods(&op_slot, self)?.load(); + + if let Some(slot_a) = slot_a { + if let Some(slot_b) = slot_b { + // Check if `a` is subclass of `b` + if b.fast_isinstance(a.class()) { + let ret = slot_b(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + } + + let ret = slot_a(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + + // No slot_a or Not implemented + if let Some(slot_b) = slot_b { + let ret = slot_b(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + + // Both slot_a & slot_b don't exist or are not implemented. + unsupported(self, a, b) } - pub fn _isub(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __isub__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __sub__), - identifier!(self, __rsub__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "-=")), - ) - }) + /// `binary_op()` can work only with [`PyNumberMethods::BinaryFunc`]. + pub fn binary_op( + &self, + a: &PyObject, + b: &PyObject, + op_slot: PyNumberMethodsOffset, + op: &str, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let result = self.binary_op1(a, b, op_slot, unsupported)?; + + if result.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Eq, + self, + )? { + Err(self.new_unsupported_binop_error(a, b, op)) + } else { + Ok(result) + } + } + + /// ### Binary in-place operators + /// + /// The in-place operators are defined to fall back to the 'normal', + /// non in-place operations, if the in-place methods are not in place. + /// + /// - If the left hand object has the appropriate struct members, and + /// they are filled, call the appropriate function and return the + /// result. No coercion is done on the arguments; the left-hand object + /// is the one the operation is performed on, and it's up to the + /// function to deal with the right-hand object. + /// + /// - Otherwise, in-place modification is not supported. Handle it exactly as + /// a non in-place operation of the same kind. + fn binary_iop1( + &self, + a: &PyObject, + b: &PyObject, + iop_slot: PyNumberMethodsOffset, + op_slot: PyNumberMethodsOffset, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let num_a = a.to_number(); + let slot_a = num_a.methods(&iop_slot, self)?.load(); + + if let Some(slot_a) = slot_a { + let ret = slot_a(num_a, b, self)?; + if ret.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + self, + )? { + return Ok(ret); + } + } + + self.binary_op1(a, b, op_slot, unsupported) + } + + /// `binary_iop()` can work only with [`PyNumberMethods::BinaryFunc`]. + fn binary_iop( + &self, + a: &PyObject, + b: &PyObject, + iop_slot: PyNumberMethodsOffset, + op_slot: PyNumberMethodsOffset, + op: &str, + unsupported: F, + ) -> PyResult + where + F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + { + let result = self.binary_iop1(a, b, iop_slot, op_slot, unsupported)?; + + if result.rich_compare_bool( + self.ctx.not_implemented.as_object(), + PyComparisonOp::Eq, + self, + )? { + Err(self.new_unsupported_binop_error(a, b, op)) + } else { + Ok(result) + } } pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + self.binary_op(a, b, PyNumberMethodsOffset::Add, "+", |vm, a, b| { + let seq_a = PySequence::try_protocol(a, vm); + + if let Ok(seq_a) = seq_a { + let ret = seq_a.concat(b, vm)?; + if ret.rich_compare_bool( + vm.ctx.not_implemented.as_object(), + PyComparisonOp::Ne, + vm, + )? { + return Ok(ret); + } + } + + Ok(vm.ctx.not_implemented()) + }) + } + + pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __add__), - identifier!(self, __radd__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+")), + PyNumberMethodsOffset::InplaceAdd, + PyNumberMethodsOffset::Add, + "+=", + |vm, a, b| { + let seq_a = PySequence::try_protocol(a, vm); + + if let Ok(seq_a) = seq_a { + return seq_a.inplace_concat(b, vm); + } + + Ok(vm.ctx.not_implemented()) + }, ) } - pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __iadd__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __add__), - identifier!(self, __radd__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+=")), - ) + pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Subtract, "-", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } - pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _isub(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __mul__), - identifier!(self, __rmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*")), + PyNumberMethodsOffset::InplaceSubtract, + PyNumberMethodsOffset::Subtract, + "-=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __imul__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __mul__), - identifier!(self, __rmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*=")), - ) + pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Multiply, "*", |vm, a, b| { + // TODO: check if PySequence::with_methods can replace try_protocol + let seq_a = PySequence::try_protocol(a, vm); + let seq_b = PySequence::try_protocol(b, vm); + + // TODO: I think converting to isize process should be handled in repeat function. + // TODO: This can be helpful to unify the sequence protocol's closure. + + if let Ok(seq_a) = seq_a { + let n = b.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + return seq_a.repeat(n, vm); + } else if let Ok(seq_b) = seq_b { + let n = a.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + return seq_b.repeat(n, vm); + } + + Ok(vm.ctx.not_implemented()) }) } - pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __matmul__), - identifier!(self, __rmatmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@")), + PyNumberMethodsOffset::InplaceMultiply, + PyNumberMethodsOffset::Multiply, + "*=", + |vm, a, b| { + // TODO: check if PySequence::with_methods can replace try_protocol + let seq_a = PySequence::try_protocol(a, vm); + let seq_b = PySequence::try_protocol(b, vm); + + if let Ok(seq_a) = seq_a { + let n = b.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + return seq_a.inplace_repeat(n, vm); + } else if let Ok(seq_b) = seq_b { + let n = a.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { + vm.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + + /* Note that the right hand operand should not be + * mutated in this case so sq_inplace_repeat is not + * used. */ + return seq_b.repeat(n, vm); + } + + Ok(vm.ctx.not_implemented()) + }, ) } - pub fn _imatmul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __imatmul__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __matmul__), - identifier!(self, __rmatmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@=")), - ) - }) + pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op( + a, + b, + PyNumberMethodsOffset::MatrixMultiply, + "@", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) } - pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _imatmul(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __truediv__), - identifier!(self, __rtruediv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/")), + PyNumberMethodsOffset::InplaceMatrixMultiply, + PyNumberMethodsOffset::MatrixMultiply, + "@=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __itruediv__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __truediv__), - identifier!(self, __rtruediv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/=")), - ) + pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::TrueDivide, "/", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } - pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __floordiv__), - identifier!(self, __rfloordiv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//")), + PyNumberMethodsOffset::InplaceTrueDivide, + PyNumberMethodsOffset::TrueDivide, + "/=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _ifloordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ifloordiv__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __floordiv__), - identifier!(self, __rfloordiv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//=")), - ) - }) + pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op( + a, + b, + PyNumberMethodsOffset::FloorDivide, + "//", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) } - pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _ifloordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __pow__), - identifier!(self, __rpow__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**")), + PyNumberMethodsOffset::InplaceFloorDivide, + PyNumberMethodsOffset::FloorDivide, + "//=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ipow__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __pow__), - identifier!(self, __rpow__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**=")), - ) + pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Power, "**", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } + pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( + a, + b, + PyNumberMethodsOffset::InplacePower, + PyNumberMethodsOffset::Power, + "**=", + |vm, _, _| Ok(vm.ctx.not_implemented()), + ) + } + + // TODO: `str` modular opertation(mod, imod) is not supported now. Should implement it. pub fn _mod(&self, a: &PyObject, b: &PyObject) -> PyResult { self.call_or_reflection( a, @@ -336,6 +527,10 @@ impl VirtualMachine { identifier!(self, __rmod__), |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%")), ) + + // self.binary_op(a, b, PyNumberMethodsOffset::Remainder, "%", |vm, _, _| { + // Ok(vm.ctx.not_implemented()) + // }) } pub fn _imod(&self, a: &PyObject, b: &PyObject) -> PyResult { @@ -348,84 +543,76 @@ impl VirtualMachine { |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%=")), ) }) + + // self.binary_iop( + // a, + // b, + // PyNumberMethodsOffset::InplaceRemainder, + // PyNumberMethodsOffset::Remainder, + // "%=", + // |vm, _, _| Ok(vm.ctx.not_implemented()), + // ) } pub fn _divmod(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __divmod__), - identifier!(self, __rdivmod__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "divmod")), - ) + self.binary_op(a, b, PyNumberMethodsOffset::Divmod, "divmod", |vm, _, _| { + Ok(vm.ctx.not_implemented()) + }) } pub fn _lshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + self.binary_op(a, b, PyNumberMethodsOffset::Lshift, "<<", |vm, _, _| { + Ok(vm.ctx.not_implemented()) + }) + } + + pub fn _ilshift(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __lshift__), - identifier!(self, __rlshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<")), + PyNumberMethodsOffset::InplaceLshift, + PyNumberMethodsOffset::Lshift, + "<<=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _ilshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ilshift__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __lshift__), - identifier!(self, __rlshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<=")), - ) + pub fn _rshift(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Rshift, ">>", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } - pub fn _rshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _irshift(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __rshift__), - identifier!(self, __rrshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>")), + PyNumberMethodsOffset::InplaceRshift, + PyNumberMethodsOffset::Rshift, + ">>=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _irshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __irshift__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __rshift__), - identifier!(self, __rrshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>=")), - ) + pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, PyNumberMethodsOffset::Xor, "^", |vm, _, _| { + Ok(vm.ctx.not_implemented()) }) } - pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( + pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( a, b, - identifier!(self, __xor__), - identifier!(self, __rxor__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^")), + PyNumberMethodsOffset::InplaceXor, + PyNumberMethodsOffset::Xor, + "^=", + |vm, _, _| Ok(vm.ctx.not_implemented()), ) } - pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ixor__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __xor__), - identifier!(self, __rxor__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^=")), - ) - }) - } - + // TODO: `or` method doesn't work because of structure of `type_::or_()`. + // TODO: It should be changed by adjusting with AsNumber. pub fn _or(&self, a: &PyObject, b: &PyObject) -> PyResult { self.call_or_reflection( a, @@ -434,6 +621,10 @@ impl VirtualMachine { identifier!(self, __ror__), |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|")), ) + + // self.binary_op(a, b, PyNumberMethodsOffset::Or, "|", |vm, _, _| { + // Ok(vm.ctx.not_implemented()) + // }) } pub fn _ior(&self, a: &PyObject, b: &PyObject) -> PyResult { @@ -446,8 +637,19 @@ impl VirtualMachine { |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|=")), ) }) + + // self.binary_iop( + // a, + // b, + // PyNumberMethodsOffset::InplaceOr, + // PyNumberMethodsOffset::Or, + // "|=", + // |vm, _, _| Ok(vm.ctx.not_implemented()), + // ) } + // TODO: `and` method doesn't work because of structure of `set`. + // TODO: It should be changed by adjusting with AsNumber. pub fn _and(&self, a: &PyObject, b: &PyObject) -> PyResult { self.call_or_reflection( a, @@ -456,6 +658,10 @@ impl VirtualMachine { identifier!(self, __rand__), |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&")), ) + + // self.binary_op(a, b, PyNumberMethodsOffset::And, "&", |vm, _, _| { + // Ok(vm.ctx.not_implemented()) + // }) } pub fn _iand(&self, a: &PyObject, b: &PyObject) -> PyResult { @@ -468,6 +674,15 @@ impl VirtualMachine { |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&=")), ) }) + + // self.binary_iop( + // a, + // b, + // PyNumberMethodsOffset::InplaceAnd, + // PyNumberMethodsOffset::And, + // "&=", + // |vm, _, _| Ok(vm.ctx.not_implemented()), + // ) } pub fn _abs(&self, a: &PyObject) -> PyResult { From f1dc0a6fcbb66bd2029d2f14670c87e3e8bd6e68 Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Mon, 6 Mar 2023 19:17:36 +0900 Subject: [PATCH 02/17] Improve: binary ops with Number Protocol --- vm/src/protocol/mod.rs | 2 +- vm/src/protocol/number.rs | 121 +++---- vm/src/stdlib/builtins.rs | 6 +- vm/src/vm/vm_ops.rs | 691 ++++++++++---------------------------- 4 files changed, 222 insertions(+), 598 deletions(-) diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs index f355b61ea4..7cde1a8757 100644 --- a/vm/src/protocol/mod.rs +++ b/vm/src/protocol/mod.rs @@ -10,5 +10,5 @@ pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, V pub use callable::PyCallable; pub use iter::{PyIter, PyIterIter, PyIterReturn}; pub use mapping::{PyMapping, PyMappingMethods}; -pub use number::{PyNumber, PyNumberMethods, PyNumberMethodsOffset}; +pub use number::{PyNumber, PyNumberBinaryOpSlot, PyNumberMethods}; pub use sequence::{PySequence, PySequenceMethods}; diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index 8abc54e1bc..a7e9efd56d 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -8,7 +8,6 @@ use crate::{ VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; -use std::ptr; type UnaryFunc = AtomicCell PyResult>>; type BinaryFunc = @@ -110,7 +109,6 @@ impl PyObject { } #[derive(Default)] -// #[repr(C)] pub struct PyNumberMethods { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions @@ -138,7 +136,6 @@ pub struct PyNumberMethods { pub inplace_subtract: BinaryFunc, pub inplace_multiply: BinaryFunc, pub inplace_remainder: BinaryFunc, - pub inplace_divmod: BinaryFunc, pub inplace_power: BinaryFunc, pub inplace_lshift: BinaryFunc, pub inplace_rshift: BinaryFunc, @@ -184,7 +181,6 @@ impl PyNumberMethods { inplace_subtract: AtomicCell::new(None), inplace_multiply: AtomicCell::new(None), inplace_remainder: AtomicCell::new(None), - inplace_divmod: AtomicCell::new(None), inplace_power: AtomicCell::new(None), inplace_lshift: AtomicCell::new(None), inplace_rshift: AtomicCell::new(None), @@ -199,32 +195,58 @@ impl PyNumberMethods { matrix_multiply: AtomicCell::new(None), inplace_matrix_multiply: AtomicCell::new(None), }; + + pub fn get_binary_op(&self, op_slot: &PyNumberBinaryOpSlot) -> PyResult<&BinaryFunc> { + use PyNumberBinaryOpSlot::*; + let binary_op = match op_slot { + Add => &self.add, + Subtract => &self.subtract, + Multiply => &self.multiply, + Remainder => &self.remainder, + Divmod => &self.divmod, + Power => &self.power, + Lshift => &self.lshift, + Rshift => &self.rshift, + And => &self.and, + Xor => &self.xor, + Or => &self.or, + InplaceAdd => &self.inplace_add, + InplaceSubtract => &self.inplace_subtract, + InplaceMultiply => &self.inplace_multiply, + InplaceRemainder => &self.inplace_remainder, + InplacePower => &self.inplace_power, + InplaceLshift => &self.inplace_lshift, + InplaceRshift => &self.inplace_rshift, + InplaceAnd => &self.inplace_and, + InplaceXor => &self.inplace_xor, + InplaceOr => &self.inplace_or, + FloorDivide => &self.floor_divide, + TrueDivide => &self.true_divide, + InplaceFloorDivide => &self.inplace_floor_divide, + InplaceTrueDivide => &self.inplace_true_divide, + MatrixMultiply => &self.matrix_multiply, + InplaceMatrixMultiply => &self.inplace_matrix_multiply, + }; + Ok(binary_op) + } } -pub enum PyNumberMethodsOffset { +pub enum PyNumberBinaryOpSlot { Add, Subtract, Multiply, Remainder, Divmod, Power, - Negative, - Positive, - Absolute, - Boolean, - Invert, Lshift, Rshift, And, Xor, Or, - Int, - Float, InplaceAdd, InplaceSubtract, InplaceMultiply, InplaceRemainder, - InplaceDivmod, InplacePower, InplaceLshift, InplaceRshift, @@ -235,63 +257,10 @@ pub enum PyNumberMethodsOffset { TrueDivide, InplaceFloorDivide, InplaceTrueDivide, - Index, MatrixMultiply, InplaceMatrixMultiply, } -impl PyNumberMethodsOffset { - pub fn method(&self, methods: &PyNumberMethods, vm: &VirtualMachine) -> PyResult<&BinaryFunc> { - use PyNumberMethodsOffset::*; - unsafe { - match self { - // BinaryFunc - Add => ptr::addr_of!(methods.add), - Subtract => ptr::addr_of!(methods.subtract), - Multiply => ptr::addr_of!(methods.multiply), - Remainder => ptr::addr_of!(methods.remainder), - Divmod => ptr::addr_of!(methods.divmod), - Power => ptr::addr_of!(methods.power), - Lshift => ptr::addr_of!(methods.lshift), - Rshift => ptr::addr_of!(methods.rshift), - And => ptr::addr_of!(methods.and), - Xor => ptr::addr_of!(methods.xor), - Or => ptr::addr_of!(methods.or), - InplaceAdd => ptr::addr_of!(methods.inplace_add), - InplaceSubtract => ptr::addr_of!(methods.inplace_subtract), - InplaceMultiply => ptr::addr_of!(methods.inplace_multiply), - InplaceRemainder => ptr::addr_of!(methods.inplace_remainder), - InplaceDivmod => ptr::addr_of!(methods.inplace_divmod), - InplacePower => ptr::addr_of!(methods.inplace_power), - InplaceLshift => ptr::addr_of!(methods.inplace_lshift), - InplaceRshift => ptr::addr_of!(methods.inplace_rshift), - InplaceAnd => ptr::addr_of!(methods.inplace_and), - InplaceXor => ptr::addr_of!(methods.inplace_xor), - InplaceOr => ptr::addr_of!(methods.inplace_or), - FloorDivide => ptr::addr_of!(methods.floor_divide), - TrueDivide => ptr::addr_of!(methods.true_divide), - InplaceFloorDivide => ptr::addr_of!(methods.inplace_floor_divide), - InplaceTrueDivide => ptr::addr_of!(methods.inplace_true_divide), - MatrixMultiply => ptr::addr_of!(methods.matrix_multiply), - InplaceMatrixMultiply => ptr::addr_of!(methods.inplace_matrix_multiply), - // UnaryFunc - Negative => ptr::null(), - Positive => ptr::null(), - Absolute => ptr::null(), - Boolean => ptr::null(), - Invert => ptr::null(), - Int => ptr::null(), - Float => ptr::null(), - Index => ptr::null(), - } - .as_ref() - .ok_or_else(|| { - vm.new_value_error("No unaryop supported for PyNumberMethodsOffset".to_owned()) - }) - } - } -} - #[derive(Copy, Clone)] pub struct PyNumber<'a> { pub obj: &'a PyObject, @@ -314,12 +283,12 @@ impl PyNumber<'_> { obj.class().mro_find_map(|x| x.slots.as_number.load()) } - pub fn methods<'a>( - &'a self, - op_slot: &'a PyNumberMethodsOffset, - vm: &VirtualMachine, - ) -> PyResult<&BinaryFunc> { - op_slot.method(self.methods, vm) + pub fn methods(&self) -> &PyNumberMethods { + self.methods + } + + pub fn get_binary_op(&self, op_slot: &PyNumberBinaryOpSlot) -> PyResult<&BinaryFunc> { + self.methods().get_binary_op(op_slot) } // PyNumber_Check @@ -336,12 +305,12 @@ impl PyNumber<'_> { // PyIndex_Check pub fn is_index(&self) -> bool { - self.methods.index.load().is_some() + self.methods().index.load().is_some() } #[inline] pub fn int(self, vm: &VirtualMachine) -> Option> { - self.methods.int.load().map(|f| { + self.methods().int.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -365,7 +334,7 @@ impl PyNumber<'_> { #[inline] pub fn index(self, vm: &VirtualMachine) -> Option> { - self.methods.index.load().map(|f| { + self.methods().index.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -389,7 +358,7 @@ impl PyNumber<'_> { #[inline] pub fn float(self, vm: &VirtualMachine) -> Option>> { - self.methods.float.load().map(|f| { + self.methods().float.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyFloat::class(vm)) { warnings::warn( diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index 4ebdbd4f02..e382546b6e 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -21,7 +21,7 @@ mod builtins { ArgBytesLike, ArgCallable, ArgIntoBool, ArgIterable, ArgMapping, ArgStrOrBytesLike, Either, FuncArgs, KwArgs, OptionalArg, OptionalOption, PosArgs, PyArithmeticValue, }, - protocol::{PyIter, PyIterReturn, PyNumberMethodsOffset}, + protocol::{PyIter, PyIterReturn, PyNumberBinaryOpSlot}, py_io, readline::{Readline, ReadlineResult}, stdlib::sys, @@ -605,9 +605,7 @@ mod builtins { modulus, } = args; match modulus { - None => vm.binary_op(&x, &y, PyNumberMethodsOffset::Power, "pow", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }), + None => vm.binary_op(&x, &y, &PyNumberBinaryOpSlot::Power, "pow"), Some(z) => { let try_pow_value = |obj: &PyObject, args: (PyObjectRef, PyObjectRef, PyObjectRef)| diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index c7a2818035..fb524bc16b 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -1,13 +1,34 @@ use super::{PyMethod, VirtualMachine}; use crate::{ - builtins::{PyInt, PyIntRef, PyStr, PyStrInterned, PyStrRef}, - function::PyArithmeticValue, + builtins::{PyInt, PyIntRef, PyStr, PyStrRef}, object::{AsObject, PyObject, PyObjectRef, PyResult}, - protocol::{PyIterReturn, PyNumberMethodsOffset, PySequence}, + protocol::{PyIterReturn, PyNumberBinaryOpSlot, PySequence}, types::PyComparisonOp, }; use num_traits::ToPrimitive; +macro_rules! binary_func { + ($fn:ident, $op_slot:ident, $op:expr) => { + pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_op(a, b, &PyNumberBinaryOpSlot::$op_slot, $op) + } + }; +} + +macro_rules! inplace_binary_func { + ($fn:ident, $iop_slot:ident, $op_slot:ident, $op:expr) => { + pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult { + self.binary_iop( + a, + b, + &PyNumberBinaryOpSlot::$iop_slot, + &PyNumberBinaryOpSlot::$op_slot, + $op, + ) + } + }; +} + /// Collection of operators impl VirtualMachine { #[inline] @@ -104,585 +125,221 @@ impl VirtualMachine { } } - // TODO: Should be deleted after transplanting complete number protocol - /// Calls a method on `obj` passing `arg`, if the method exists. + /// Calling scheme used for binary operations: /// - /// Otherwise, or if the result is the special `NotImplemented` built-in constant, - /// calls `unsupported` to determine fallback value. - pub fn call_or_unsupported( - &self, - obj: &PyObject, - arg: &PyObject, - method: &'static PyStrInterned, - unsupported: F, - ) -> PyResult - where - F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, - { - if let Some(method_or_err) = self.get_method(obj.to_owned(), method) { - let method = method_or_err?; - let result = method.call((arg.to_owned(),), self)?; - if let PyArithmeticValue::Implemented(x) = PyArithmeticValue::from_object(self, result) - { - return Ok(x); - } - } - unsupported(self, obj, arg) - } - - // TODO: Should be deleted after transplanting complete number protocol - /// Calls a method, falling back to its reflection with the operands - /// reversed, and then to the value provided by `unsupported`. + /// Order operations are tried until either a valid result or error: + /// b.op(a,b)[*], a.op(a,b), b.op(a,b) /// - /// For example: the following: - /// - /// `call_or_reflection(lhs, rhs, "__and__", "__rand__", unsupported)` - /// - /// 1. Calls `__and__` with `lhs` and `rhs`. - /// 2. If above is not implemented, calls `__rand__` with `rhs` and `lhs`. - /// 3. If above is not implemented, invokes `unsupported` for the result. - pub fn call_or_reflection( - &self, - lhs: &PyObject, - rhs: &PyObject, - default: &'static PyStrInterned, - reflection: &'static PyStrInterned, - unsupported: fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, - ) -> PyResult { - if rhs.fast_isinstance(lhs.class()) { - let lop = lhs.get_class_attr(reflection); - let rop = rhs.get_class_attr(reflection); - if let Some((lop, rop)) = lop.zip(rop) { - if !lop.is(&rop) { - if let Ok(r) = self.call_or_unsupported(rhs, lhs, reflection, |vm, _, _| { - Err(vm.new_exception_empty(vm.ctx.exceptions.exception_type.to_owned())) - }) { - return Ok(r); - } - } - } - } - // Try to call the default method - self.call_or_unsupported(lhs, rhs, default, move |vm, lhs, rhs| { - // Try to call the reflection method - // don't call reflection method if operands are of the same type - if !lhs.class().is(rhs.class()) { - vm.call_or_unsupported(rhs, lhs, reflection, |_, rhs, lhs| { - // switch them around again - unsupported(vm, lhs, rhs) - }) - } else { - unsupported(vm, lhs, rhs) - } - }) - } - - fn binary_op1( - &self, - a: &PyObject, - b: &PyObject, - op_slot: PyNumberMethodsOffset, - unsupported: F, - ) -> PyResult - where - F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, - { + /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a) + fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: &PyNumberBinaryOpSlot) -> PyResult { let num_a = a.to_number(); let num_b = b.to_number(); - let slot_a = num_a.methods(&op_slot, self)?.load(); - let slot_b = num_b.methods(&op_slot, self)?.load(); + let slot_a = num_a.get_binary_op(op_slot)?.load(); + let mut slot_b = if b.class().is(a.class()) { + None + } else { + match num_b.get_binary_op(op_slot)?.load() { + Some(slot_b) + if slot_b as usize == slot_a.map(|s| s as usize).unwrap_or_default() => + { + None + } + slot_b => slot_b, + } + }; if let Some(slot_a) = slot_a { - if let Some(slot_b) = slot_b { - // Check if `a` is subclass of `b` + if let Some(slot_bb) = slot_b { if b.fast_isinstance(a.class()) { - let ret = slot_b(num_a, b, self)?; - if ret.rich_compare_bool( - self.ctx.not_implemented.as_object(), - PyComparisonOp::Ne, - self, - )? { - return Ok(ret); + let x = slot_bb(num_a, b, self)?; + if !x.is(&self.ctx.not_implemented) { + return Ok(x); } + slot_b = None; } } - - let ret = slot_a(num_a, b, self)?; - if ret.rich_compare_bool( - self.ctx.not_implemented.as_object(), - PyComparisonOp::Ne, - self, - )? { - return Ok(ret); + let x = slot_a(num_a, b, self)?; + if !x.is(&self.ctx.not_implemented) { + return Ok(x); } } - // No slot_a or Not implemented if let Some(slot_b) = slot_b { - let ret = slot_b(num_a, b, self)?; - if ret.rich_compare_bool( - self.ctx.not_implemented.as_object(), - PyComparisonOp::Ne, - self, - )? { - return Ok(ret); + let x = slot_b(num_a, b, self)?; + if !x.is(&self.ctx.not_implemented) { + return Ok(x); } } - // Both slot_a & slot_b don't exist or are not implemented. - unsupported(self, a, b) + Ok(self.ctx.not_implemented()) } - /// `binary_op()` can work only with [`PyNumberMethods::BinaryFunc`]. - pub fn binary_op( + pub fn binary_op( &self, a: &PyObject, b: &PyObject, - op_slot: PyNumberMethodsOffset, + op_slot: &PyNumberBinaryOpSlot, op: &str, - unsupported: F, - ) -> PyResult - where - F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, - { - let result = self.binary_op1(a, b, op_slot, unsupported)?; - - if result.rich_compare_bool( - self.ctx.not_implemented.as_object(), - PyComparisonOp::Eq, - self, - )? { - Err(self.new_unsupported_binop_error(a, b, op)) - } else { - Ok(result) + ) -> PyResult { + let result = self.binary_op1(a, b, op_slot)?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); } + Err(self.new_unsupported_binop_error(a, b, op)) } - /// ### Binary in-place operators + /// Binary in-place operators /// /// The in-place operators are defined to fall back to the 'normal', /// non in-place operations, if the in-place methods are not in place. /// /// - If the left hand object has the appropriate struct members, and - /// they are filled, call the appropriate function and return the - /// result. No coercion is done on the arguments; the left-hand object - /// is the one the operation is performed on, and it's up to the - /// function to deal with the right-hand object. + /// they are filled, call the appropriate function and return the + /// result. No coercion is done on the arguments; the left-hand object + /// is the one the operation is performed on, and it's up to the + /// function to deal with the right-hand object. /// /// - Otherwise, in-place modification is not supported. Handle it exactly as - /// a non in-place operation of the same kind. - fn binary_iop1( + /// a non in-place operation of the same kind. + fn binary_iop1( &self, a: &PyObject, b: &PyObject, - iop_slot: PyNumberMethodsOffset, - op_slot: PyNumberMethodsOffset, - unsupported: F, - ) -> PyResult - where - F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, - { + iop_slot: &PyNumberBinaryOpSlot, + op_slot: &PyNumberBinaryOpSlot, + ) -> PyResult { let num_a = a.to_number(); - let slot_a = num_a.methods(&iop_slot, self)?.load(); - - if let Some(slot_a) = slot_a { - let ret = slot_a(num_a, b, self)?; - if ret.rich_compare_bool( - self.ctx.not_implemented.as_object(), - PyComparisonOp::Ne, - self, - )? { - return Ok(ret); + if let Some(slot) = num_a.get_binary_op(iop_slot)?.load() { + let x = slot(num_a, b, self)?; + if !x.is(&self.ctx.not_implemented) { + return Ok(x); } } - - self.binary_op1(a, b, op_slot, unsupported) + self.binary_op1(a, b, op_slot) } - /// `binary_iop()` can work only with [`PyNumberMethods::BinaryFunc`]. - fn binary_iop( + fn binary_iop( &self, a: &PyObject, b: &PyObject, - iop_slot: PyNumberMethodsOffset, - op_slot: PyNumberMethodsOffset, + iop_slot: &PyNumberBinaryOpSlot, + op_slot: &PyNumberBinaryOpSlot, op: &str, - unsupported: F, - ) -> PyResult - where - F: Fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, - { - let result = self.binary_iop1(a, b, iop_slot, op_slot, unsupported)?; - - if result.rich_compare_bool( - self.ctx.not_implemented.as_object(), - PyComparisonOp::Eq, - self, - )? { - Err(self.new_unsupported_binop_error(a, b, op)) - } else { - Ok(result) + ) -> PyResult { + let result = self.binary_iop1(a, b, iop_slot, op_slot)?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); } - } + Err(self.new_unsupported_binop_error(a, b, op)) + } + + binary_func!(_sub, Subtract, "-"); + binary_func!(_mod, Remainder, "%"); + binary_func!(_divmod, Divmod, "divmod"); + binary_func!(_pow, Power, "**"); + binary_func!(_lshift, Lshift, "<<"); + binary_func!(_rshift, Rshift, ">>"); + binary_func!(_and, And, "&"); + binary_func!(_xor, Xor, "^"); + binary_func!(_or, Or, "|"); + binary_func!(_floordiv, FloorDivide, "//"); + binary_func!(_truediv, TrueDivide, "/"); + binary_func!(_matmul, MatrixMultiply, "@"); + + inplace_binary_func!(_isub, InplaceSubtract, Subtract, "-="); + inplace_binary_func!(_imod, InplaceRemainder, Remainder, "%="); + inplace_binary_func!(_ipow, InplacePower, Power, "**="); + inplace_binary_func!(_ilshift, InplaceLshift, Lshift, "<<="); + inplace_binary_func!(_irshift, InplaceRshift, Rshift, ">>="); + inplace_binary_func!(_iand, InplaceAnd, And, "&="); + inplace_binary_func!(_ixor, InplaceXor, Xor, "^="); + inplace_binary_func!(_ior, InplaceOr, Or, "|="); + inplace_binary_func!(_ifloordiv, InplaceFloorDivide, FloorDivide, "//="); + inplace_binary_func!(_itruediv, InplaceTrueDivide, TrueDivide, "/="); + inplace_binary_func!(_imatmul, InplaceMatrixMultiply, MatrixMultiply, "@="); pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Add, "+", |vm, a, b| { - let seq_a = PySequence::try_protocol(a, vm); - - if let Ok(seq_a) = seq_a { - let ret = seq_a.concat(b, vm)?; - if ret.rich_compare_bool( - vm.ctx.not_implemented.as_object(), - PyComparisonOp::Ne, - vm, - )? { - return Ok(ret); - } + let result = self.binary_op1(a, b, &PyNumberBinaryOpSlot::Add)?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); + } + if let Ok(seq_a) = PySequence::try_protocol(a, self) { + let result = seq_a.concat(b, self)?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); } - - Ok(vm.ctx.not_implemented()) - }) + } + Err(self.new_unsupported_binop_error(a, b, "+")) } pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplaceAdd, - PyNumberMethodsOffset::Add, - "+=", - |vm, a, b| { - let seq_a = PySequence::try_protocol(a, vm); - - if let Ok(seq_a) = seq_a { - return seq_a.inplace_concat(b, vm); - } - - Ok(vm.ctx.not_implemented()) - }, - ) - } - - pub fn _sub(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Subtract, "-", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }) - } - - pub fn _isub(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( + let result = self.binary_iop1( a, b, - PyNumberMethodsOffset::InplaceSubtract, - PyNumberMethodsOffset::Subtract, - "-=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) + &PyNumberBinaryOpSlot::InplaceAdd, + &PyNumberBinaryOpSlot::Add, + )?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); + } + if let Ok(seq_a) = PySequence::try_protocol(a, self) { + let result = seq_a.inplace_concat(b, self)?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); + } + } + Err(self.new_unsupported_binop_error(a, b, "+=")) } pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Multiply, "*", |vm, a, b| { - // TODO: check if PySequence::with_methods can replace try_protocol - let seq_a = PySequence::try_protocol(a, vm); - let seq_b = PySequence::try_protocol(b, vm); - - // TODO: I think converting to isize process should be handled in repeat function. - // TODO: This can be helpful to unify the sequence protocol's closure. - - if let Ok(seq_a) = seq_a { - let n = b.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { - vm.new_overflow_error("repeated bytes are too long".to_owned()) + let result = self.binary_op1(a, b, &PyNumberBinaryOpSlot::Multiply)?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); + } + if let Ok(seq_a) = PySequence::try_protocol(a, self) { + let n = + b.try_index(self)?.as_bigint().to_isize().ok_or_else(|| { + self.new_overflow_error("repeated bytes are too long".to_owned()) })?; - - return seq_a.repeat(n, vm); - } else if let Ok(seq_b) = seq_b { - let n = a.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { - vm.new_overflow_error("repeated bytes are too long".to_owned()) + return seq_a.repeat(n, self); + } else if let Ok(seq_b) = PySequence::try_protocol(b, self) { + let n = + a.try_index(self)?.as_bigint().to_isize().ok_or_else(|| { + self.new_overflow_error("repeated bytes are too long".to_owned()) })?; - - return seq_b.repeat(n, vm); - } - - Ok(vm.ctx.not_implemented()) - }) + return seq_b.repeat(n, self); + } + Err(self.new_unsupported_binop_error(a, b, "*")) } pub fn _imul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplaceMultiply, - PyNumberMethodsOffset::Multiply, - "*=", - |vm, a, b| { - // TODO: check if PySequence::with_methods can replace try_protocol - let seq_a = PySequence::try_protocol(a, vm); - let seq_b = PySequence::try_protocol(b, vm); - - if let Ok(seq_a) = seq_a { - let n = b.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { - vm.new_overflow_error("repeated bytes are too long".to_owned()) - })?; - - return seq_a.inplace_repeat(n, vm); - } else if let Ok(seq_b) = seq_b { - let n = a.try_int(vm)?.as_bigint().to_isize().ok_or_else(|| { - vm.new_overflow_error("repeated bytes are too long".to_owned()) - })?; - - /* Note that the right hand operand should not be - * mutated in this case so sq_inplace_repeat is not - * used. */ - return seq_b.repeat(n, vm); - } - - Ok(vm.ctx.not_implemented()) - }, - ) - } - - pub fn _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op( - a, - b, - PyNumberMethodsOffset::MatrixMultiply, - "@", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - pub fn _imatmul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplaceMatrixMultiply, - PyNumberMethodsOffset::MatrixMultiply, - "@=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - pub fn _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::TrueDivide, "/", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }) - } - - pub fn _itruediv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( + let result = self.binary_iop1( a, b, - PyNumberMethodsOffset::InplaceTrueDivide, - PyNumberMethodsOffset::TrueDivide, - "/=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - pub fn _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op( - a, - b, - PyNumberMethodsOffset::FloorDivide, - "//", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - pub fn _ifloordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplaceFloorDivide, - PyNumberMethodsOffset::FloorDivide, - "//=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - pub fn _pow(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Power, "**", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }) - } - - pub fn _ipow(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplacePower, - PyNumberMethodsOffset::Power, - "**=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - // TODO: `str` modular opertation(mod, imod) is not supported now. Should implement it. - pub fn _mod(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __mod__), - identifier!(self, __rmod__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%")), - ) - - // self.binary_op(a, b, PyNumberMethodsOffset::Remainder, "%", |vm, _, _| { - // Ok(vm.ctx.not_implemented()) - // }) - } - - pub fn _imod(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __imod__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __mod__), - identifier!(self, __rmod__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "%=")), - ) - }) - - // self.binary_iop( - // a, - // b, - // PyNumberMethodsOffset::InplaceRemainder, - // PyNumberMethodsOffset::Remainder, - // "%=", - // |vm, _, _| Ok(vm.ctx.not_implemented()), - // ) - } - - pub fn _divmod(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Divmod, "divmod", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }) - } - - pub fn _lshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Lshift, "<<", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }) - } - - pub fn _ilshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplaceLshift, - PyNumberMethodsOffset::Lshift, - "<<=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - pub fn _rshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Rshift, ">>", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }) - } - - pub fn _irshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplaceRshift, - PyNumberMethodsOffset::Rshift, - ">>=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - pub fn _xor(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, PyNumberMethodsOffset::Xor, "^", |vm, _, _| { - Ok(vm.ctx.not_implemented()) - }) - } - - pub fn _ixor(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_iop( - a, - b, - PyNumberMethodsOffset::InplaceXor, - PyNumberMethodsOffset::Xor, - "^=", - |vm, _, _| Ok(vm.ctx.not_implemented()), - ) - } - - // TODO: `or` method doesn't work because of structure of `type_::or_()`. - // TODO: It should be changed by adjusting with AsNumber. - pub fn _or(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __or__), - identifier!(self, __ror__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|")), - ) - - // self.binary_op(a, b, PyNumberMethodsOffset::Or, "|", |vm, _, _| { - // Ok(vm.ctx.not_implemented()) - // }) - } - - pub fn _ior(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __ior__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __or__), - identifier!(self, __ror__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "|=")), - ) - }) - - // self.binary_iop( - // a, - // b, - // PyNumberMethodsOffset::InplaceOr, - // PyNumberMethodsOffset::Or, - // "|=", - // |vm, _, _| Ok(vm.ctx.not_implemented()), - // ) - } - - // TODO: `and` method doesn't work because of structure of `set`. - // TODO: It should be changed by adjusting with AsNumber. - pub fn _and(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __and__), - identifier!(self, __rand__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&")), - ) - - // self.binary_op(a, b, PyNumberMethodsOffset::And, "&", |vm, _, _| { - // Ok(vm.ctx.not_implemented()) - // }) - } - - pub fn _iand(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_unsupported(a, b, identifier!(self, __iand__), |vm, a, b| { - vm.call_or_reflection( - a, - b, - identifier!(self, __and__), - identifier!(self, __rand__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "&=")), - ) - }) - - // self.binary_iop( - // a, - // b, - // PyNumberMethodsOffset::InplaceAnd, - // PyNumberMethodsOffset::And, - // "&=", - // |vm, _, _| Ok(vm.ctx.not_implemented()), - // ) + &PyNumberBinaryOpSlot::InplaceMultiply, + &PyNumberBinaryOpSlot::Multiply, + )?; + if !result.is(&self.ctx.not_implemented) { + return Ok(result); + } + if let Ok(seq_a) = PySequence::try_protocol(a, self) { + let n = + b.try_index(self)?.as_bigint().to_isize().ok_or_else(|| { + self.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + return seq_a.inplace_repeat(n, self); + } else if let Ok(seq_b) = PySequence::try_protocol(b, self) { + let n = + a.try_index(self)?.as_bigint().to_isize().ok_or_else(|| { + self.new_overflow_error("repeated bytes are too long".to_owned()) + })?; + /* Note that the right hand operand should not be + * mutated in this case so inplace_repeat is not + * used. */ + return seq_b.repeat(n, self); + } + Err(self.new_unsupported_binop_error(a, b, "*=")) } pub fn _abs(&self, a: &PyObject) -> PyResult { From ba9c354e1bc995d4c0dbd3342af9f1008d2cffbd Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Mon, 6 Mar 2023 19:19:31 +0900 Subject: [PATCH 03/17] Add binary ops in Number Protocol to update_slot --- vm/src/types/slot.rs | 200 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index f0647d31e0..c7c23eec9b 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -214,6 +214,18 @@ fn float_wrapper(num: PyNumber, vm: &VirtualMachine) -> PyResult> }) } +macro_rules! number_binary_op_wrapper { + ($name:ident) => { + |num, other, vm| { + vm.call_special_method( + num.obj.to_owned(), + identifier!(vm, $name), + (other.to_owned(),), + ) + } + }; +} + fn getitem_wrapper(obj: &PyObject, needle: K, vm: &VirtualMachine) -> PyResult { vm.call_special_method(obj.to_owned(), identifier!(vm, __getitem__), (needle,)) } @@ -484,6 +496,194 @@ impl PyType { toggle_ext_func!(number_methods, float, float_wrapper); update_pointer_slot!(as_number, number_methods); } + _ if name == identifier!(ctx, __add__) => { + toggle_ext_func!(number_methods, add, number_binary_op_wrapper!(__add__)); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __iadd__) => { + toggle_ext_func!( + number_methods, + inplace_add, + number_binary_op_wrapper!(__iadd__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __sub__) => { + toggle_ext_func!(number_methods, subtract, number_binary_op_wrapper!(__sub__)); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __isub__) => { + toggle_ext_func!( + number_methods, + inplace_subtract, + number_binary_op_wrapper!(__isub__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __mul__) => { + toggle_ext_func!(number_methods, multiply, number_binary_op_wrapper!(__mul__)); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __imul__) => { + toggle_ext_func!( + number_methods, + inplace_multiply, + number_binary_op_wrapper!(__imul__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __mod__) => { + toggle_ext_func!( + number_methods, + remainder, + number_binary_op_wrapper!(__mod__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __imod__) => { + toggle_ext_func!( + number_methods, + inplace_remainder, + number_binary_op_wrapper!(__imod__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __divmod__) => { + toggle_ext_func!( + number_methods, + divmod, + number_binary_op_wrapper!(__divmod__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __pow__) => { + toggle_ext_func!(number_methods, power, number_binary_op_wrapper!(__pow__)); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __ipow__) => { + toggle_ext_func!( + number_methods, + inplace_power, + number_binary_op_wrapper!(__ipow__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __lshift__) => { + toggle_ext_func!( + number_methods, + lshift, + number_binary_op_wrapper!(__lshift__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __ilshift__) => { + toggle_ext_func!( + number_methods, + inplace_lshift, + number_binary_op_wrapper!(__ilshift__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __rshift__) => { + toggle_ext_func!( + number_methods, + rshift, + number_binary_op_wrapper!(__rshift__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __irshift__) => { + toggle_ext_func!( + number_methods, + inplace_rshift, + number_binary_op_wrapper!(__irshift__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __and__) => { + toggle_ext_func!(number_methods, and, number_binary_op_wrapper!(__and__)); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __iand__) => { + toggle_ext_func!( + number_methods, + inplace_and, + number_binary_op_wrapper!(__iand__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __xor__) => { + toggle_ext_func!(number_methods, xor, number_binary_op_wrapper!(__xor__)); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __ixor__) => { + toggle_ext_func!( + number_methods, + inplace_xor, + number_binary_op_wrapper!(__ixor__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __or__) => { + toggle_ext_func!(number_methods, or, number_binary_op_wrapper!(__or__)); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __ior__) => { + toggle_ext_func!( + number_methods, + inplace_or, + number_binary_op_wrapper!(__ior__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __floordiv__) => { + toggle_ext_func!( + number_methods, + floor_divide, + number_binary_op_wrapper!(__floordiv__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __ifloordiv__) => { + toggle_ext_func!( + number_methods, + inplace_floor_divide, + number_binary_op_wrapper!(__ifloordiv__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __truediv__) => { + toggle_ext_func!( + number_methods, + true_divide, + number_binary_op_wrapper!(__truediv__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __itruediv__) => { + toggle_ext_func!( + number_methods, + inplace_true_divide, + number_binary_op_wrapper!(__itruediv__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __matmul__) => { + toggle_ext_func!( + number_methods, + matrix_multiply, + number_binary_op_wrapper!(__matmul__) + ); + update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __imatmul__) => { + toggle_ext_func!( + number_methods, + inplace_matrix_multiply, + number_binary_op_wrapper!(__imatmul__) + ); + update_pointer_slot!(as_number, number_methods); + } _ => {} } } From 984db6798c0e43beb5cc5b0b0294ba83b8bd5d11 Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Thu, 9 Mar 2023 23:41:48 +0900 Subject: [PATCH 04/17] Add PyNumberSlots to support right binary ops --- vm/src/builtins/bytearray.rs | 15 +- vm/src/builtins/bytes.rs | 14 +- vm/src/builtins/complex.rs | 32 +-- vm/src/builtins/dict.rs | 25 +- vm/src/builtins/float.rs | 55 ++--- vm/src/builtins/int.rs | 69 ++---- vm/src/builtins/mappingproxy.rs | 25 +- vm/src/builtins/set.rs | 160 +++++++----- vm/src/builtins/singletons.rs | 9 +- vm/src/builtins/str.rs | 14 +- vm/src/builtins/type.rs | 8 +- vm/src/builtins/union.rs | 9 +- vm/src/protocol/mod.rs | 4 +- vm/src/protocol/number.rs | 169 ++++++------- vm/src/types/slot.rs | 417 +++++++++++++++++++++++--------- vm/src/vm/vm_ops.rs | 20 +- 16 files changed, 611 insertions(+), 434 deletions(-) diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 48639b818e..ae39e234cb 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -37,7 +37,6 @@ use crate::{ VirtualMachine, }; use bstr::ByteSlice; -use once_cell::sync::Lazy; use std::mem::size_of; #[pyclass(module = false, name = "bytearray", unhashable = true)] @@ -859,14 +858,16 @@ impl AsSequence for PyByteArray { impl AsNumber for PyByteArray { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - remainder: atomic_func!(|number, other, vm| { - PyByteArray::number_downcast(number) - .mod_(other.to_owned(), vm) - .to_pyresult(vm) + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + remainder: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.mod_(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index df18ac8457..a76ae966c9 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -629,14 +629,16 @@ impl AsSequence for PyBytes { impl AsNumber for PyBytes { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - remainder: atomic_func!(|number, other, vm| { - PyBytes::number_downcast(number) - .mod_(other.to_owned(), vm) - .to_pyresult(vm) + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + remainder: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.mod_(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/complex.rs b/vm/src/builtins/complex.rs index c288c8e135..4146b29b60 100644 --- a/vm/src/builtins/complex.rs +++ b/vm/src/builtins/complex.rs @@ -1,6 +1,5 @@ use super::{float, PyStr, PyType, PyTypeRef}; use crate::{ - atomic_func, class::PyClassImpl, convert::{ToPyObject, ToPyResult}, function::{ @@ -15,7 +14,6 @@ use crate::{ }; use num_complex::Complex64; use num_traits::Zero; -use once_cell::sync::Lazy; use rustpython_common::{float_ops, hash}; use std::num::Wrapping; @@ -454,38 +452,34 @@ impl Hashable for PyComplex { impl AsNumber for PyComplex { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - add: atomic_func!(|number, other, vm| { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + add: Some(|number, other, vm| { PyComplex::number_op(number, other, |a, b, _vm| a + b, vm) }), - subtract: atomic_func!(|number, other, vm| { + subtract: Some(|number, other, vm| { PyComplex::number_op(number, other, |a, b, _vm| a - b, vm) }), - multiply: atomic_func!(|number, other, vm| { + multiply: Some(|number, other, vm| { PyComplex::number_op(number, other, |a, b, _vm| a * b, vm) }), - power: atomic_func!(|number, other, vm| PyComplex::number_op( - number, other, inner_pow, vm - )), - negative: atomic_func!(|number, vm| { + power: Some(|number, other, vm| PyComplex::number_op(number, other, inner_pow, vm)), + negative: Some(|number, vm| { let value = PyComplex::number_downcast(number).value; (-value).to_pyresult(vm) }), - positive: atomic_func!( - |number, vm| PyComplex::number_downcast_exact(number, vm).to_pyresult(vm) - ), - absolute: atomic_func!(|number, vm| { + positive: Some(|number, vm| { + PyComplex::number_downcast_exact(number, vm).to_pyresult(vm) + }), + absolute: Some(|number, vm| { let value = PyComplex::number_downcast(number).value; value.norm().to_pyresult(vm) }), - boolean: atomic_func!(|number, _vm| Ok(PyComplex::number_downcast(number) - .value - .is_zero())), - true_divide: atomic_func!(|number, other, vm| { + boolean: Some(|number, _vm| Ok(PyComplex::number_downcast(number).value.is_zero())), + true_divide: Some(|number, other, vm| { PyComplex::number_op(number, other, inner_div, vm) }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 5590f856be..1803fdf171 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -479,20 +479,23 @@ impl AsSequence for PyDict { impl AsNumber for PyDict { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - or: atomic_func!(|num, args, vm| { - PyDict::number_downcast(num).or(args.to_pyobject(vm), vm) + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + or: Some(|num, args, vm| { + if let Some(num) = num.obj.downcast_ref::() { + PyDict::or(num, args.to_pyobject(vm), vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - inplace_or: atomic_func!(|num, args, vm| { - PyDict::ior( - PyDict::number_downcast(num).to_owned(), - args.to_pyobject(vm), - vm, - ) - .map(|d| d.into()) + inplace_or: Some(|num, args, vm| { + if let Some(num) = num.obj.downcast_ref::() { + PyDict::ior(num.to_owned(), args.to_pyobject(vm), vm).map(|d| d.into()) + } else { + Ok(vm.ctx.not_implemented()) + } }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index 38e1521393..d97cb40245 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -2,7 +2,6 @@ use super::{ try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef, }; use crate::{ - atomic_func, class::PyClassImpl, common::format::FormatSpec, common::{float_ops, hash}, @@ -21,7 +20,6 @@ use num_bigint::{BigInt, ToBigInt}; use num_complex::Complex64; use num_rational::Ratio; use num_traits::{Signed, ToPrimitive, Zero}; -use once_cell::sync::Lazy; #[pyclass(module = false, name = "float")] #[derive(Debug, Copy, Clone, PartialEq)] @@ -545,53 +543,32 @@ impl Hashable for PyFloat { impl AsNumber for PyFloat { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - add: atomic_func!(|num, other, vm| PyFloat::number_op( - num, - other, - |a, b, _vm| a + b, - vm - )), - subtract: atomic_func!(|num, other, vm| PyFloat::number_op( - num, - other, - |a, b, _vm| a - b, - vm - )), - multiply: atomic_func!(|num, other, vm| PyFloat::number_op( - num, - other, - |a, b, _vm| a * b, - vm - )), - remainder: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, inner_mod, vm)), - divmod: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, inner_divmod, vm)), - power: atomic_func!(|num, other, vm| PyFloat::number_op(num, other, float_pow, vm)), - negative: atomic_func!(|num, vm| { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + add: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a + b, vm)), + subtract: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a - b, vm)), + multiply: Some(|num, other, vm| PyFloat::number_op(num, other, |a, b, _vm| a * b, vm)), + remainder: Some(|num, other, vm| PyFloat::number_op(num, other, inner_mod, vm)), + divmod: Some(|num, other, vm| PyFloat::number_op(num, other, inner_divmod, vm)), + power: Some(|num, other, vm| PyFloat::number_op(num, other, float_pow, vm)), + negative: Some(|num, vm| { let value = PyFloat::number_downcast(num).value; (-value).to_pyresult(vm) }), - positive: atomic_func!( - |num, vm| PyFloat::number_downcast_exact(num, vm).to_pyresult(vm) - ), - absolute: atomic_func!(|num, vm| { + positive: Some(|num, vm| PyFloat::number_downcast_exact(num, vm).to_pyresult(vm)), + absolute: Some(|num, vm| { let value = PyFloat::number_downcast(num).value; value.abs().to_pyresult(vm) }), - boolean: atomic_func!(|num, _vm| Ok(PyFloat::number_downcast(num).value.is_zero())), - int: atomic_func!(|num, vm| { + boolean: Some(|num, _vm| Ok(PyFloat::number_downcast(num).value.is_zero())), + int: Some(|num, vm| { let value = PyFloat::number_downcast(num).value; try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x)) }), - float: atomic_func!(|num, vm| Ok(PyFloat::number_downcast_exact(num, vm))), - floor_divide: atomic_func!(|num, other, vm| { - PyFloat::number_op(num, other, inner_floordiv, vm) - }), - true_divide: atomic_func!(|num, other, vm| { - PyFloat::number_op(num, other, inner_div, vm) - }), + float: Some(|num, vm| Ok(PyFloat::number_downcast_exact(num, vm))), + floor_divide: Some(|num, other, vm| PyFloat::number_op(num, other, inner_floordiv, vm)), + true_divide: Some(|num, other, vm| PyFloat::number_op(num, other, inner_div, vm)), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 7e9501f693..de8a062bb2 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -1,6 +1,5 @@ use super::{float, PyByteArray, PyBytes, PyStr, PyType, PyTypeRef}; use crate::{ - atomic_func, builtins::PyStrRef, bytesinner::PyBytesInner, class::PyClassImpl, @@ -20,7 +19,6 @@ use num_bigint::{BigInt, BigUint, Sign}; use num_integer::Integer; use num_rational::Ratio; use num_traits::{One, Pow, PrimInt, Signed, ToPrimitive, Zero}; -use once_cell::sync::Lazy; use std::ops::{Div, Neg}; use std::{fmt, ops::Not}; @@ -729,54 +727,33 @@ impl Hashable for PyInt { impl AsNumber for PyInt { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - add: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)), - subtract: atomic_func!(|num, other, vm| PyInt::number_op( - num, - other, - |a, b, _vm| a - b, - vm - )), - multiply: atomic_func!(|num, other, vm| PyInt::number_op( - num, - other, - |a, b, _vm| a * b, - vm - )), - remainder: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)), - divmod: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)), - power: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)), - negative: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value) - .neg() - .to_pyresult(vm)), - positive: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())), - absolute: atomic_func!(|num, vm| PyInt::number_downcast(num) - .value - .abs() - .to_pyresult(vm)), - boolean: atomic_func!(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())), - invert: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value) - .not() - .to_pyresult(vm)), - lshift: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)), - rshift: atomic_func!(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)), - and: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)), - xor: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)), - or: atomic_func!(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)), - int: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), - float: atomic_func!(|num, vm| { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + add: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)), + subtract: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a - b, vm)), + multiply: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a * b, vm)), + remainder: Some(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)), + divmod: Some(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)), + power: Some(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)), + negative: Some(|num, vm| (&PyInt::number_downcast(num).value).neg().to_pyresult(vm)), + positive: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())), + absolute: Some(|num, vm| PyInt::number_downcast(num).value.abs().to_pyresult(vm)), + boolean: Some(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())), + invert: Some(|num, vm| (&PyInt::number_downcast(num).value).not().to_pyresult(vm)), + lshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)), + rshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)), + and: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)), + xor: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)), + or: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)), + int: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), + float: Some(|num, vm| { let zelf = PyInt::number_downcast(num); try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x)) }), - floor_divide: atomic_func!(|num, other, vm| { - PyInt::number_op(num, other, inner_floordiv, vm) - }), - true_divide: atomic_func!(|num, other, vm| { - PyInt::number_op(num, other, inner_truediv, vm) - }), - index: atomic_func!(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), + floor_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_floordiv, vm)), + true_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_truediv, vm)), + index: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index 023abdd246..25ddcb03be 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -1,5 +1,3 @@ -use once_cell::sync::Lazy; - use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef}; use crate::{ atomic_func, @@ -10,6 +8,7 @@ use crate::{ types::{AsMapping, AsNumber, AsSequence, Comparable, Constructor, Iterable, PyComparisonOp}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; +use once_cell::sync::Lazy; #[pyclass(module = false, name = "mappingproxy")] #[derive(Debug)] @@ -70,7 +69,7 @@ impl Constructor for PyMappingProxy { } } -#[pyclass(with(AsMapping, Iterable, Constructor, AsSequence, Comparable))] +#[pyclass(with(AsMapping, Iterable, Constructor, AsSequence, Comparable, AsNumber))] impl PyMappingProxy { fn get_inner(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult> { let opt = match &self.mapping { @@ -232,15 +231,23 @@ impl AsSequence for PyMappingProxy { impl AsNumber for PyMappingProxy { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - or: atomic_func!(|num, args, vm| { - PyMappingProxy::number_downcast(num).or(args.to_pyobject(vm), vm) + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + or: Some(|num, args, vm| { + if let Some(num) = num.obj.downcast_ref::() { + num.or(args.to_pyobject(vm), vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - inplace_or: atomic_func!(|num, args, vm| { - PyMappingProxy::number_downcast(num).ior(args.to_pyobject(vm), vm) + inplace_or: Some(|num, args, vm| { + if let Some(num) = num.obj.downcast_ref::() { + num.ior(args.to_pyobject(vm), vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index 0814e17644..7a53093c79 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -805,61 +805,85 @@ impl Iterable for PySet { impl AsNumber for PySet { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - subtract: atomic_func!(|number, other, vm| { - PySet::number_downcast(number) - .sub(other.to_owned(), vm) - .to_pyresult(vm) + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + subtract: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.sub(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - and: atomic_func!(|number, other, vm| { - PySet::number_downcast(number) - .and(other.to_owned(), vm) - .to_pyresult(vm) + and: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.and(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - xor: atomic_func!(|number, other, vm| { - PySet::number_downcast(number) - .xor(other.to_owned(), vm) - .to_pyresult(vm) + xor: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.xor(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - or: atomic_func!(|number, other, vm| { - PySet::number_downcast(number) - .or(other.to_owned(), vm) - .to_pyresult(vm) + or: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.or(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - inplace_subtract: atomic_func!(|number, other, vm| { - PySet::isub( - PySet::number_downcast(number).to_owned(), - AnySet::try_from_object(vm, other.to_owned())?, - vm, - ) - .to_pyresult(vm) + inplace_subtract: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + PySet::isub( + number.to_owned(), + AnySet::try_from_object(vm, other.to_owned())?, + vm, + ) + .to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - inplace_and: atomic_func!(|number, other, vm| { - PySet::iand( - PySet::number_downcast(number).to_owned(), - AnySet::try_from_object(vm, other.to_owned())?, - vm, - ) - .to_pyresult(vm) + inplace_and: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + PySet::iand( + number.to_owned(), + AnySet::try_from_object(vm, other.to_owned())?, + vm, + ) + .to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - inplace_xor: atomic_func!(|number, other, vm| { - PySet::ixor( - PySet::number_downcast(number).to_owned(), - AnySet::try_from_object(vm, other.to_owned())?, - vm, - ) - .to_pyresult(vm) + inplace_xor: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + PySet::ixor( + number.to_owned(), + AnySet::try_from_object(vm, other.to_owned())?, + vm, + ) + .to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - inplace_or: atomic_func!(|number, other, vm| { - PySet::ior( - PySet::number_downcast(number).to_owned(), - AnySet::try_from_object(vm, other.to_owned())?, - vm, - ) - .to_pyresult(vm) + inplace_or: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + PySet::ior( + number.to_owned(), + AnySet::try_from_object(vm, other.to_owned())?, + vm, + ) + .to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } @@ -1105,29 +1129,37 @@ impl Iterable for PyFrozenSet { impl AsNumber for PyFrozenSet { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - subtract: atomic_func!(|number, other, vm| { - PyFrozenSet::number_downcast(number) - .sub(other.to_owned(), vm) - .to_pyresult(vm) + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + subtract: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.sub(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - and: atomic_func!(|number, other, vm| { - PyFrozenSet::number_downcast(number) - .and(other.to_owned(), vm) - .to_pyresult(vm) + and: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.and(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - xor: atomic_func!(|number, other, vm| { - PyFrozenSet::number_downcast(number) - .xor(other.to_owned(), vm) - .to_pyresult(vm) + xor: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.xor(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), - or: atomic_func!(|number, other, vm| { - PyFrozenSet::number_downcast(number) - .or(other.to_owned(), vm) - .to_pyresult(vm) + or: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.or(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/singletons.rs b/vm/src/builtins/singletons.rs index 65081a0d46..28a142e103 100644 --- a/vm/src/builtins/singletons.rs +++ b/vm/src/builtins/singletons.rs @@ -1,8 +1,5 @@ -use once_cell::sync::Lazy; - use super::{PyType, PyTypeRef}; use crate::{ - atomic_func, class::PyClassImpl, convert::ToPyObject, protocol::PyNumberMethods, @@ -60,10 +57,10 @@ impl PyNone { impl AsNumber for PyNone { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - boolean: atomic_func!(|_number, _vm| Ok(false)), + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + boolean: Some(|_number, _vm| Ok(false)), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 514db2bfc5..a34cd322c0 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -1301,14 +1301,16 @@ impl AsMapping for PyStr { impl AsNumber for PyStr { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - remainder: atomic_func!(|number, other, vm| { - PyStr::number_downcast(number) - .modulo(other.to_owned(), vm) - .to_pyresult(vm) + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + remainder: Some(|number, other, vm| { + if let Some(number) = number.obj.downcast_ref::() { + number.modulo(other.to_owned(), vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index 8aef931687..d876460767 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -3,7 +3,6 @@ use super::{ PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak, }; use crate::{ - atomic_func, builtins::{ descriptor::{ DescrObject, MemberDef, MemberDescrObject, MemberGetter, MemberKind, MemberSetter, @@ -28,7 +27,6 @@ use crate::{ }; use indexmap::{map::Entry, IndexMap}; use itertools::Itertools; -use once_cell::sync::Lazy; use std::{borrow::Borrow, collections::HashSet, fmt, ops::Deref, pin::Pin, ptr::NonNull}; #[pyclass(module = false, name = "type")] @@ -1061,12 +1059,12 @@ impl Callable for PyType { impl AsNumber for PyType { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - or: atomic_func!(|num, other, vm| { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + or: Some(|num, other, vm| { or_(num.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index ad1899a04c..a5c11d3d2e 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -1,5 +1,3 @@ -use once_cell::sync::Lazy; - use super::{genericalias, type_}; use crate::{ atomic_func, @@ -13,6 +11,7 @@ use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; +use once_cell::sync::Lazy; use std::fmt; const CLS_ATTRS: &[&str] = &["__module__"]; @@ -257,12 +256,12 @@ impl AsMapping for PyUnion { impl AsNumber for PyUnion { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - or: atomic_func!(|num, other, vm| { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + or: Some(|num, other, vm| { PyUnion::or(num.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) }), ..PyNumberMethods::NOT_IMPLEMENTED - }); + }; &AS_NUMBER } } diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs index 7cde1a8757..9c7aa71073 100644 --- a/vm/src/protocol/mod.rs +++ b/vm/src/protocol/mod.rs @@ -10,5 +10,7 @@ pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, V pub use callable::PyCallable; pub use iter::{PyIter, PyIterIter, PyIterReturn}; pub use mapping::{PyMapping, PyMappingMethods}; -pub use number::{PyNumber, PyNumberBinaryOpSlot, PyNumberMethods}; +pub use number::{ + PyNumber, PyNumberBinaryFunc, PyNumberBinaryOpSlot, PyNumberMethods, PyNumberUnaryFunc, +}; pub use sequence::{PySequence, PySequenceMethods}; diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index a7e9efd56d..6841c81918 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -7,11 +7,9 @@ use crate::{ AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine, }; -use crossbeam_utils::atomic::AtomicCell; -type UnaryFunc = AtomicCell PyResult>>; -type BinaryFunc = - AtomicCell PyResult>>; +pub type PyNumberUnaryFunc = fn(PyNumber, &VirtualMachine) -> PyResult; +pub type PyNumberBinaryFunc = fn(PyNumber, &PyObject, &VirtualMachine) -> PyResult; impl PyObject { #[inline] @@ -113,45 +111,45 @@ pub struct PyNumberMethods { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions in the slot functions themselves. */ - pub add: BinaryFunc, - pub subtract: BinaryFunc, - pub multiply: BinaryFunc, - pub remainder: BinaryFunc, - pub divmod: BinaryFunc, - pub power: BinaryFunc, - pub negative: UnaryFunc, - pub positive: UnaryFunc, - pub absolute: UnaryFunc, - pub boolean: UnaryFunc, - pub invert: UnaryFunc, - pub lshift: BinaryFunc, - pub rshift: BinaryFunc, - pub and: BinaryFunc, - pub xor: BinaryFunc, - pub or: BinaryFunc, - pub int: UnaryFunc>, - pub float: UnaryFunc>, + pub add: Option, + pub subtract: Option, + pub multiply: Option, + pub remainder: Option, + pub divmod: Option, + pub power: Option, + pub negative: Option, + pub positive: Option, + pub absolute: Option, + pub boolean: Option>, + pub invert: Option, + pub lshift: Option, + pub rshift: Option, + pub and: Option, + pub xor: Option, + pub or: Option, + pub int: Option>>, + pub float: Option>>, - pub inplace_add: BinaryFunc, - pub inplace_subtract: BinaryFunc, - pub inplace_multiply: BinaryFunc, - pub inplace_remainder: BinaryFunc, - pub inplace_power: BinaryFunc, - pub inplace_lshift: BinaryFunc, - pub inplace_rshift: BinaryFunc, - pub inplace_and: BinaryFunc, - pub inplace_xor: BinaryFunc, - pub inplace_or: BinaryFunc, + pub inplace_add: Option, + pub inplace_subtract: Option, + pub inplace_multiply: Option, + pub inplace_remainder: Option, + pub inplace_power: Option, + pub inplace_lshift: Option, + pub inplace_rshift: Option, + pub inplace_and: Option, + pub inplace_xor: Option, + pub inplace_or: Option, - pub floor_divide: BinaryFunc, - pub true_divide: BinaryFunc, - pub inplace_floor_divide: BinaryFunc, - pub inplace_true_divide: BinaryFunc, + pub floor_divide: Option, + pub true_divide: Option, + pub inplace_floor_divide: Option, + pub inplace_true_divide: Option, - pub index: UnaryFunc>, + pub index: Option>>, - pub matrix_multiply: BinaryFunc, - pub inplace_matrix_multiply: BinaryFunc, + pub matrix_multiply: Option, + pub inplace_matrix_multiply: Option, } impl PyNumberMethods { @@ -159,44 +157,47 @@ impl PyNumberMethods { // TODO: weak order read for performance #[allow(clippy::declare_interior_mutable_const)] pub const NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods { - add: AtomicCell::new(None), - subtract: AtomicCell::new(None), - multiply: AtomicCell::new(None), - remainder: AtomicCell::new(None), - divmod: AtomicCell::new(None), - power: AtomicCell::new(None), - negative: AtomicCell::new(None), - positive: AtomicCell::new(None), - absolute: AtomicCell::new(None), - boolean: AtomicCell::new(None), - invert: AtomicCell::new(None), - lshift: AtomicCell::new(None), - rshift: AtomicCell::new(None), - and: AtomicCell::new(None), - xor: AtomicCell::new(None), - or: AtomicCell::new(None), - int: AtomicCell::new(None), - float: AtomicCell::new(None), - inplace_add: AtomicCell::new(None), - inplace_subtract: AtomicCell::new(None), - inplace_multiply: AtomicCell::new(None), - inplace_remainder: AtomicCell::new(None), - inplace_power: AtomicCell::new(None), - inplace_lshift: AtomicCell::new(None), - inplace_rshift: AtomicCell::new(None), - inplace_and: AtomicCell::new(None), - inplace_xor: AtomicCell::new(None), - inplace_or: AtomicCell::new(None), - floor_divide: AtomicCell::new(None), - true_divide: AtomicCell::new(None), - inplace_floor_divide: AtomicCell::new(None), - inplace_true_divide: AtomicCell::new(None), - index: AtomicCell::new(None), - matrix_multiply: AtomicCell::new(None), - inplace_matrix_multiply: AtomicCell::new(None), + add: None, + subtract: None, + multiply: None, + remainder: None, + divmod: None, + power: None, + negative: None, + positive: None, + absolute: None, + boolean: None, + invert: None, + lshift: None, + rshift: None, + and: None, + xor: None, + or: None, + int: None, + float: None, + inplace_add: None, + inplace_subtract: None, + inplace_multiply: None, + inplace_remainder: None, + inplace_power: None, + inplace_lshift: None, + inplace_rshift: None, + inplace_and: None, + inplace_xor: None, + inplace_or: None, + floor_divide: None, + true_divide: None, + inplace_floor_divide: None, + inplace_true_divide: None, + index: None, + matrix_multiply: None, + inplace_matrix_multiply: None, }; - pub fn get_binary_op(&self, op_slot: &PyNumberBinaryOpSlot) -> PyResult<&BinaryFunc> { + pub fn get_binary_op( + &self, + op_slot: &PyNumberBinaryOpSlot, + ) -> PyResult<&Option> { use PyNumberBinaryOpSlot::*; let binary_op = match op_slot { Add => &self.add, @@ -287,16 +288,16 @@ impl PyNumber<'_> { self.methods } - pub fn get_binary_op(&self, op_slot: &PyNumberBinaryOpSlot) -> PyResult<&BinaryFunc> { + pub fn get_binary_op( + &self, + op_slot: &PyNumberBinaryOpSlot, + ) -> PyResult<&Option> { self.methods().get_binary_op(op_slot) } // PyNumber_Check pub fn check(obj: &PyObject) -> bool { - let Some(methods) = Self::find_methods(obj) else { - return false; - }; - let methods = methods.as_ref(); + let methods = &obj.class().slots.number; methods.int.load().is_some() || methods.index.load().is_some() || methods.float.load().is_some() @@ -305,12 +306,12 @@ impl PyNumber<'_> { // PyIndex_Check pub fn is_index(&self) -> bool { - self.methods().index.load().is_some() + self.obj.class().slots.number.index.load().is_some() } #[inline] pub fn int(self, vm: &VirtualMachine) -> Option> { - self.methods().int.load().map(|f| { + self.obj.class().slots.number.int.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -334,7 +335,7 @@ impl PyNumber<'_> { #[inline] pub fn index(self, vm: &VirtualMachine) -> Option> { - self.methods().index.load().map(|f| { + self.obj.class().slots.number.index.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyInt::class(vm)) { warnings::warn( @@ -358,7 +359,7 @@ impl PyNumber<'_> { #[inline] pub fn float(self, vm: &VirtualMachine) -> Option>> { - self.methods().float.load().map(|f| { + self.obj.class().slots.number.float.load().map(|f| { let ret = f(self, vm)?; let value = if !ret.class().is(PyFloat::class(vm)) { warnings::warn( diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index c7c23eec9b..01900daeb3 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -1,14 +1,13 @@ -use crate::common::{hash::PyHash, lock::PyRwLock}; -use crate::convert::ToPyObject; use crate::{ builtins::{type_::PointerSlot, PyFloat, PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef}, bytecode::ComparisonOperator, - convert::ToPyResult, + common::{hash::PyHash, lock::PyRwLock}, + convert::{ToPyObject, ToPyResult}, function::{Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue}, identifier, protocol::{ - PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberMethods, PySequence, - PySequenceMethods, + PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberBinaryFunc, + PyNumberBinaryOpSlot, PyNumberMethods, PyNumberUnaryFunc, PySequence, PySequenceMethods, }, vm::Context, AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, @@ -85,6 +84,7 @@ pub struct PyTypeSlots { // The count of tp_members. pub member_count: usize, + pub number: PyNumberSlots, } impl PyTypeSlots { @@ -102,6 +102,128 @@ impl std::fmt::Debug for PyTypeSlots { } } +#[derive(Default)] +pub struct PyNumberSlots { + pub add: AtomicCell>, + pub subtract: AtomicCell>, + pub multiply: AtomicCell>, + pub remainder: AtomicCell>, + pub divmod: AtomicCell>, + pub power: AtomicCell>, + pub negative: AtomicCell>, + pub positive: AtomicCell>, + pub absolute: AtomicCell>, + pub boolean: AtomicCell>>, + pub invert: AtomicCell>, + pub lshift: AtomicCell>, + pub rshift: AtomicCell>, + pub and: AtomicCell>, + pub xor: AtomicCell>, + pub or: AtomicCell>, + pub int: AtomicCell>>>, + pub float: AtomicCell>>>, + + pub right_add: AtomicCell>, + pub right_subtract: AtomicCell>, + pub right_multiply: AtomicCell>, + pub right_remainder: AtomicCell>, + pub right_divmod: AtomicCell>, + pub right_power: AtomicCell>, + pub right_lshift: AtomicCell>, + pub right_rshift: AtomicCell>, + pub right_and: AtomicCell>, + pub right_xor: AtomicCell>, + pub right_or: AtomicCell>, + + pub inplace_add: AtomicCell>, + pub inplace_subtract: AtomicCell>, + pub inplace_multiply: AtomicCell>, + pub inplace_remainder: AtomicCell>, + pub inplace_power: AtomicCell>, + pub inplace_lshift: AtomicCell>, + pub inplace_rshift: AtomicCell>, + pub inplace_and: AtomicCell>, + pub inplace_xor: AtomicCell>, + pub inplace_or: AtomicCell>, + + pub floor_divide: AtomicCell>, + pub true_divide: AtomicCell>, + pub right_floor_divide: AtomicCell>, + pub right_true_divide: AtomicCell>, + pub inplace_floor_divide: AtomicCell>, + pub inplace_true_divide: AtomicCell>, + + pub index: AtomicCell>>>, + + pub matrix_multiply: AtomicCell>, + pub right_matrix_multiply: AtomicCell>, + pub inplace_matrix_multiply: AtomicCell>, +} + +impl PyNumberSlots { + pub fn get_left_binary_op( + &self, + op_slot: &PyNumberBinaryOpSlot, + ) -> PyResult> { + use PyNumberBinaryOpSlot::*; + let binary_op = match op_slot { + Add => self.add.load(), + Subtract => self.subtract.load(), + Multiply => self.multiply.load(), + Remainder => self.remainder.load(), + Divmod => self.divmod.load(), + Power => self.power.load(), + Lshift => self.lshift.load(), + Rshift => self.rshift.load(), + And => self.and.load(), + Xor => self.xor.load(), + Or => self.or.load(), + InplaceAdd => self.inplace_add.load(), + InplaceSubtract => self.inplace_subtract.load(), + InplaceMultiply => self.inplace_multiply.load(), + InplaceRemainder => self.inplace_remainder.load(), + InplacePower => self.inplace_power.load(), + InplaceLshift => self.inplace_lshift.load(), + InplaceRshift => self.inplace_rshift.load(), + InplaceAnd => self.inplace_and.load(), + InplaceXor => self.inplace_xor.load(), + InplaceOr => self.inplace_or.load(), + FloorDivide => self.floor_divide.load(), + TrueDivide => self.true_divide.load(), + InplaceFloorDivide => self.inplace_floor_divide.load(), + InplaceTrueDivide => self.inplace_true_divide.load(), + MatrixMultiply => self.matrix_multiply.load(), + InplaceMatrixMultiply => self.inplace_matrix_multiply.load(), + }; + Ok(binary_op) + } + + pub fn get_right_binary_op( + &self, + op_slot: &PyNumberBinaryOpSlot, + ) -> PyResult> { + use PyNumberBinaryOpSlot::*; + let binary_op = match op_slot { + Add => self.right_add.load(), + Subtract => self.right_subtract.load(), + Multiply => self.right_multiply.load(), + Remainder => self.right_remainder.load(), + Divmod => self.right_divmod.load(), + Power => self.right_power.load(), + Lshift => self.right_lshift.load(), + Rshift => self.right_rshift.load(), + And => self.right_and.load(), + Xor => self.right_xor.load(), + Or => self.right_or.load(), + FloorDivide => self.right_floor_divide.load(), + TrueDivide => self.right_true_divide.load(), + MatrixMultiply => self.right_matrix_multiply.load(), + _ => None, + }; + Ok(binary_op) + } +} + bitflags! { #[non_exhaustive] pub struct PyTypeFlags: u64 { @@ -373,6 +495,15 @@ impl PyType { }}; } + macro_rules! toggle_subslot { + ($group:ident, $name:ident, $func:expr) => { + self.slots + .$group + .$name + .store(if ADD { Some($func) } else { None }); + }; + } + macro_rules! update_slot { ($name:ident, $func:expr) => {{ self.slots.$name.store(Some($func)); @@ -485,204 +616,188 @@ impl PyType { toggle_slot!(del, del_wrapper); } _ if name == identifier!(ctx, __int__) => { - toggle_ext_func!(number_methods, int, int_wrapper); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, int, int_wrapper); } _ if name == identifier!(ctx, __index__) => { - toggle_ext_func!(number_methods, index, index_wrapper); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, index, index_wrapper); } _ if name == identifier!(ctx, __float__) => { - toggle_ext_func!(number_methods, float, float_wrapper); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, float, float_wrapper); } _ if name == identifier!(ctx, __add__) => { - toggle_ext_func!(number_methods, add, number_binary_op_wrapper!(__add__)); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, add, number_binary_op_wrapper!(__add__)); + } + _ if name == identifier!(ctx, __radd__) => { + toggle_subslot!(number, right_add, number_binary_op_wrapper!(__radd__)); } _ if name == identifier!(ctx, __iadd__) => { - toggle_ext_func!( - number_methods, - inplace_add, - number_binary_op_wrapper!(__iadd__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, inplace_add, number_binary_op_wrapper!(__iadd__)); } _ if name == identifier!(ctx, __sub__) => { - toggle_ext_func!(number_methods, subtract, number_binary_op_wrapper!(__sub__)); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, subtract, number_binary_op_wrapper!(__sub__)); + } + _ if name == identifier!(ctx, __rsub__) => { + toggle_subslot!(number, right_subtract, number_binary_op_wrapper!(__rsub__)); } _ if name == identifier!(ctx, __isub__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_subtract, number_binary_op_wrapper!(__isub__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __mul__) => { - toggle_ext_func!(number_methods, multiply, number_binary_op_wrapper!(__mul__)); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, multiply, number_binary_op_wrapper!(__mul__)); + } + _ if name == identifier!(ctx, __rmul__) => { + toggle_subslot!(number, right_multiply, number_binary_op_wrapper!(__rmul__)); } _ if name == identifier!(ctx, __imul__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_multiply, number_binary_op_wrapper!(__imul__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __mod__) => { - toggle_ext_func!( - number_methods, - remainder, - number_binary_op_wrapper!(__mod__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, remainder, number_binary_op_wrapper!(__mod__)); + } + _ if name == identifier!(ctx, __rmod__) => { + toggle_subslot!(number, right_remainder, number_binary_op_wrapper!(__rmod__)); } _ if name == identifier!(ctx, __imod__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_remainder, number_binary_op_wrapper!(__imod__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __divmod__) => { - toggle_ext_func!( - number_methods, - divmod, - number_binary_op_wrapper!(__divmod__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, divmod, number_binary_op_wrapper!(__divmod__)); + } + _ if name == identifier!(ctx, __rdivmod__) => { + toggle_subslot!(number, right_divmod, number_binary_op_wrapper!(__rdivmod__)); } _ if name == identifier!(ctx, __pow__) => { - toggle_ext_func!(number_methods, power, number_binary_op_wrapper!(__pow__)); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, power, number_binary_op_wrapper!(__pow__)); + } + _ if name == identifier!(ctx, __rpow__) => { + toggle_subslot!(number, right_power, number_binary_op_wrapper!(__rpow__)); } _ if name == identifier!(ctx, __ipow__) => { - toggle_ext_func!( - number_methods, - inplace_power, - number_binary_op_wrapper!(__ipow__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, inplace_power, number_binary_op_wrapper!(__ipow__)); } _ if name == identifier!(ctx, __lshift__) => { - toggle_ext_func!( - number_methods, - lshift, - number_binary_op_wrapper!(__lshift__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, lshift, number_binary_op_wrapper!(__lshift__)); + } + _ if name == identifier!(ctx, __rlshift__) => { + toggle_subslot!(number, right_lshift, number_binary_op_wrapper!(__rlshift__)); } _ if name == identifier!(ctx, __ilshift__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_lshift, number_binary_op_wrapper!(__ilshift__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __rshift__) => { - toggle_ext_func!( - number_methods, - rshift, - number_binary_op_wrapper!(__rshift__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, rshift, number_binary_op_wrapper!(__rshift__)); + } + _ if name == identifier!(ctx, __rrshift__) => { + toggle_subslot!(number, right_rshift, number_binary_op_wrapper!(__rrshift__)); } _ if name == identifier!(ctx, __irshift__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_rshift, number_binary_op_wrapper!(__irshift__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __and__) => { - toggle_ext_func!(number_methods, and, number_binary_op_wrapper!(__and__)); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, and, number_binary_op_wrapper!(__and__)); + } + _ if name == identifier!(ctx, __rand__) => { + toggle_subslot!(number, right_and, number_binary_op_wrapper!(__rand__)); } _ if name == identifier!(ctx, __iand__) => { - toggle_ext_func!( - number_methods, - inplace_and, - number_binary_op_wrapper!(__iand__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, inplace_and, number_binary_op_wrapper!(__iand__)); } _ if name == identifier!(ctx, __xor__) => { - toggle_ext_func!(number_methods, xor, number_binary_op_wrapper!(__xor__)); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, xor, number_binary_op_wrapper!(__xor__)); + } + _ if name == identifier!(ctx, __rxor__) => { + toggle_subslot!(number, right_xor, number_binary_op_wrapper!(__rxor__)); } _ if name == identifier!(ctx, __ixor__) => { - toggle_ext_func!( - number_methods, - inplace_xor, - number_binary_op_wrapper!(__ixor__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, inplace_xor, number_binary_op_wrapper!(__ixor__)); } _ if name == identifier!(ctx, __or__) => { - toggle_ext_func!(number_methods, or, number_binary_op_wrapper!(__or__)); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, or, number_binary_op_wrapper!(__or__)); + } + _ if name == identifier!(ctx, __ror__) => { + toggle_subslot!(number, right_or, number_binary_op_wrapper!(__ror__)); } _ if name == identifier!(ctx, __ior__) => { - toggle_ext_func!( - number_methods, - inplace_or, - number_binary_op_wrapper!(__ior__) - ); - update_pointer_slot!(as_number, number_methods); + toggle_subslot!(number, inplace_or, number_binary_op_wrapper!(__ior__)); } _ if name == identifier!(ctx, __floordiv__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, floor_divide, number_binary_op_wrapper!(__floordiv__) ); - update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __rfloordiv__) => { + toggle_subslot!( + number, + right_floor_divide, + number_binary_op_wrapper!(__rfloordiv__) + ); } _ if name == identifier!(ctx, __ifloordiv__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_floor_divide, number_binary_op_wrapper!(__ifloordiv__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __truediv__) => { - toggle_ext_func!( - number_methods, - true_divide, - number_binary_op_wrapper!(__truediv__) + toggle_subslot!(number, true_divide, number_binary_op_wrapper!(__truediv__)); + } + _ if name == identifier!(ctx, __rtruediv__) => { + toggle_subslot!( + number, + right_true_divide, + number_binary_op_wrapper!(__rtruediv__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __itruediv__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_true_divide, number_binary_op_wrapper!(__itruediv__) ); - update_pointer_slot!(as_number, number_methods); } _ if name == identifier!(ctx, __matmul__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, matrix_multiply, number_binary_op_wrapper!(__matmul__) ); - update_pointer_slot!(as_number, number_methods); + } + _ if name == identifier!(ctx, __rmatmul__) => { + toggle_subslot!( + number, + right_matrix_multiply, + number_binary_op_wrapper!(__rmatmul__) + ); } _ if name == identifier!(ctx, __imatmul__) => { - toggle_ext_func!( - number_methods, + toggle_subslot!( + number, inplace_matrix_multiply, number_binary_op_wrapper!(__imatmul__) ); - update_pointer_slot!(as_number, number_methods); } _ => {} } @@ -1161,6 +1276,26 @@ pub trait AsSequence: PyPayload { } } +macro_rules! extend_number_slot { + ($slots:ident, $methods:ident, $method:ident, $right_method:ident, $op_slot:ident) => { + if $methods.$method.is_some() { + $slots.number.$method.store($methods.$method); + $slots.number.$right_method.store(Some(|num, other, vm| { + num.get_binary_op(&PyNumberBinaryOpSlot::$op_slot)?.unwrap()( + other.to_number(), + num.obj, + vm, + ) + })); + } + }; + ($slots:ident, $methods:ident, $method:ident) => { + if $methods.$method.is_some() { + $slots.number.$method.store($methods.$method); + } + }; +} + #[pyclass] pub trait AsNumber: PyPayload { #[pyslot] @@ -1184,6 +1319,60 @@ pub trait AsNumber: PyPayload { Self::clone_exact(Self::number_downcast(number), vm) } } + + fn extend_slots(slots: &mut PyTypeSlots) { + let methods = Self::as_number(); + + extend_number_slot!(slots, methods, add, right_add, Add); + extend_number_slot!(slots, methods, subtract, right_subtract, Subtract); + extend_number_slot!(slots, methods, multiply, right_multiply, Multiply); + extend_number_slot!(slots, methods, remainder, right_remainder, Remainder); + extend_number_slot!(slots, methods, divmod, right_divmod, Divmod); + extend_number_slot!(slots, methods, power, right_power, Power); + extend_number_slot!(slots, methods, lshift, right_lshift, Lshift); + extend_number_slot!(slots, methods, rshift, right_rshift, Rshift); + extend_number_slot!(slots, methods, and, right_and, And); + extend_number_slot!(slots, methods, xor, right_xor, Xor); + extend_number_slot!(slots, methods, or, right_or, Or); + extend_number_slot!( + slots, + methods, + floor_divide, + right_floor_divide, + FloorDivide + ); + extend_number_slot!(slots, methods, true_divide, right_true_divide, TrueDivide); + extend_number_slot!( + slots, + methods, + matrix_multiply, + right_matrix_multiply, + MatrixMultiply + ); + + extend_number_slot!(slots, methods, negative); + extend_number_slot!(slots, methods, positive); + extend_number_slot!(slots, methods, absolute); + extend_number_slot!(slots, methods, boolean); + extend_number_slot!(slots, methods, invert); + extend_number_slot!(slots, methods, int); + extend_number_slot!(slots, methods, float); + extend_number_slot!(slots, methods, index); + + extend_number_slot!(slots, methods, inplace_add); + extend_number_slot!(slots, methods, inplace_subtract); + extend_number_slot!(slots, methods, inplace_multiply); + extend_number_slot!(slots, methods, inplace_remainder); + extend_number_slot!(slots, methods, inplace_power); + extend_number_slot!(slots, methods, inplace_lshift); + extend_number_slot!(slots, methods, inplace_rshift); + extend_number_slot!(slots, methods, inplace_and); + extend_number_slot!(slots, methods, inplace_xor); + extend_number_slot!(slots, methods, inplace_or); + extend_number_slot!(slots, methods, inplace_floor_divide); + extend_number_slot!(slots, methods, inplace_true_divide); + extend_number_slot!(slots, methods, inplace_matrix_multiply); + } } #[pyclass] diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index fb524bc16b..6f892b828d 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -128,18 +128,15 @@ impl VirtualMachine { /// Calling scheme used for binary operations: /// /// Order operations are tried until either a valid result or error: - /// b.op(a,b)[*], a.op(a,b), b.op(a,b) + /// b.rop(b,a)[*], a.op(a,b), b.rop(b,a) /// /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a) fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: &PyNumberBinaryOpSlot) -> PyResult { - let num_a = a.to_number(); - let num_b = b.to_number(); - - let slot_a = num_a.get_binary_op(op_slot)?.load(); + let slot_a = a.class().slots.number.get_left_binary_op(op_slot)?; let mut slot_b = if b.class().is(a.class()) { None } else { - match num_b.get_binary_op(op_slot)?.load() { + match b.class().slots.number.get_right_binary_op(op_slot)? { Some(slot_b) if slot_b as usize == slot_a.map(|s| s as usize).unwrap_or_default() => { @@ -152,21 +149,21 @@ impl VirtualMachine { if let Some(slot_a) = slot_a { if let Some(slot_bb) = slot_b { if b.fast_isinstance(a.class()) { - let x = slot_bb(num_a, b, self)?; + let x = slot_bb(b.to_number(), a, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); } slot_b = None; } } - let x = slot_a(num_a, b, self)?; + let x = slot_a(a.to_number(), b, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); } } if let Some(slot_b) = slot_b { - let x = slot_b(num_a, b, self)?; + let x = slot_b(b.to_number(), a, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); } @@ -209,9 +206,8 @@ impl VirtualMachine { iop_slot: &PyNumberBinaryOpSlot, op_slot: &PyNumberBinaryOpSlot, ) -> PyResult { - let num_a = a.to_number(); - if let Some(slot) = num_a.get_binary_op(iop_slot)?.load() { - let x = slot(num_a, b, self)?; + if let Some(slot) = a.class().slots.number.get_left_binary_op(iop_slot)? { + let x = slot(a.to_number(), b, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); } From 8a42a68a4f83e878edb6d034b94d1a1b01a03931 Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Wed, 8 Mar 2023 03:59:11 +0900 Subject: [PATCH 05/17] Temp: Implement Number Protocol for PyBool --- vm/src/builtins/bool.rs | 52 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs index ed21290f65..010212e67e 100644 --- a/vm/src/builtins/bool.rs +++ b/vm/src/builtins/bool.rs @@ -1,11 +1,17 @@ use super::{PyInt, PyStrRef, PyType, PyTypeRef}; use crate::{ - class::PyClassImpl, convert::ToPyObject, function::OptionalArg, identifier, types::Constructor, + class::PyClassImpl, + convert::{ToPyObject, ToPyResult}, + function::OptionalArg, + identifier, + protocol::PyNumberMethods, + types::{AsNumber, Constructor}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine, }; use num_bigint::Sign; use num_traits::Zero; +use once_cell::sync::Lazy; use std::fmt::{Debug, Formatter}; impl ToPyObject for bool { @@ -102,7 +108,7 @@ impl Constructor for PyBool { } } -#[pyclass(with(Constructor))] +#[pyclass(with(Constructor, AsNumber))] impl PyBool { #[pymethod(magic)] fn repr(zelf: bool, vm: &VirtualMachine) -> PyStrRef { @@ -166,6 +172,48 @@ impl PyBool { } } +macro_rules! int_method { + ($method:ident) => { + PyInt::as_number().$method + }; +} + +impl AsNumber for PyBool { + fn as_number() -> &'static PyNumberMethods { + static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { + add: int_method!(add), + subtract: int_method!(subtract), + multiply: int_method!(multiply), + remainder: int_method!(remainder), + divmod: int_method!(divmod), + power: int_method!(power), + negative: int_method!(negative), + positive: int_method!(positive), + absolute: int_method!(absolute), + boolean: int_method!(boolean), + invert: int_method!(invert), + lshift: int_method!(lshift), + rshift: int_method!(rshift), + and: Some(|number, other, vm| { + PyBool::and(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) + }), + xor: Some(|number, other, vm| { + PyBool::xor(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) + }), + or: Some(|number, other, vm| { + PyBool::or(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) + }), + int: int_method!(int), + float: int_method!(float), + floor_divide: int_method!(floor_divide), + true_divide: int_method!(true_divide), + index: int_method!(index), + ..PyNumberMethods::NOT_IMPLEMENTED + }); + &AS_NUMBER + } +} + pub(crate) fn init(context: &Context) { PyBool::extend_class(context, context.types.bool_type); } From 05317f166435c09df37d30001fd0bce4a9786733 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 9 Mar 2023 19:42:19 +0900 Subject: [PATCH 06/17] temporary fill up missing number protocols --- derive-impl/src/pyclass.rs | 29 +++++++--- stdlib/src/array.rs | 1 + vm/src/builtins/type.rs | 108 +++++++++++++++++++++++++++++-------- vm/src/stdlib/builtins.rs | 5 ++ vm/src/vm/context.rs | 9 +++- 5 files changed, 121 insertions(+), 31 deletions(-) diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs index 07df8b31f6..45c366746c 100644 --- a/derive-impl/src/pyclass.rs +++ b/derive-impl/src/pyclass.rs @@ -548,14 +548,27 @@ where other ), }; - quote_spanned! { ident.span() => - class.set_str_attr( - #py_name, - ctx.make_funcdef(#py_name, Self::#ident) - #doc - #build_func, - ctx, - ); + if py_name.starts_with("__") && py_name.ends_with("__") { + let name_ident = Ident::new(&py_name, ident.span()); + quote_spanned! { ident.span() => + class.set_attr( + ctx.names.#name_ident, + ctx.make_funcdef(#py_name, Self::#ident) + #doc + #build_func + .into(), + ); + } + } else { + quote_spanned! { ident.span() => + class.set_str_attr( + #py_name, + ctx.make_funcdef(#py_name, Self::#ident) + #doc + #build_func, + ctx, + ); + } } }; diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index 69a317c828..8dbc180d83 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -6,6 +6,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef { let array = module .get_attr("array", vm) .expect("Expect array has array type."); + array.init_builtin_number_slots(&vm.ctx); let collections_abc = vm .import("collections.abc", None, 0) diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index d876460767..f26dcfef1b 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -185,25 +185,6 @@ impl PyType { *slots.name.get_mut() = Some(String::from(name)); - #[allow(clippy::mutable_key_type)] - let mut slot_name_set = HashSet::new(); - - for cls in mro.iter() { - for &name in cls.attributes.read().keys() { - if name != identifier!(ctx, __new__) - && name.as_str().starts_with("__") - && name.as_str().ends_with("__") - { - slot_name_set.insert(name); - } - } - } - for &name in attrs.keys() { - if name.as_str().starts_with("__") && name.as_str().ends_with("__") { - slot_name_set.insert(name); - } - } - let new_type = PyRef::new_ref( PyType { base: Some(base), @@ -218,9 +199,7 @@ impl PyType { None, ); - for attr_name in slot_name_set { - new_type.update_slot::(attr_name, ctx); - } + new_type.init_slots(ctx); let weakref_type = super::PyWeak::static_type(); for base in &new_type.bases { @@ -278,6 +257,30 @@ impl PyType { Ok(new_type) } + pub(crate) fn init_slots(&self, ctx: &Context) { + #[allow(clippy::mutable_key_type)] + let mut slot_name_set = std::collections::HashSet::new(); + + for cls in self.mro.iter() { + for &name in cls.attributes.read().keys() { + if name == identifier!(ctx, __new__) { + continue; + } + if name.as_str().starts_with("__") && name.as_str().ends_with("__") { + slot_name_set.insert(name); + } + } + } + for &name in self.attributes.read().keys() { + if name.as_str().starts_with("__") && name.as_str().ends_with("__") { + slot_name_set.insert(name); + } + } + for attr_name in slot_name_set { + self.update_slot::(attr_name, ctx); + } + } + pub fn slot_name(&self) -> String { self.slots.name.read().as_ref().unwrap().to_string() } @@ -1326,3 +1329,64 @@ mod tests { ); } } + +impl crate::PyObject { + // temporary tool to fill missing number protocols for builtin types + pub fn init_builtin_number_slots(&self, ctx: &Context) { + let typ = self + .downcast_ref::() + .expect("not called from a type"); + macro_rules! call_update_slot { + ($name:ident) => { + let id = identifier!(ctx, $name); + if typ.has_attr(id) { + typ.update_slot::(identifier!(ctx, $name), ctx); + } + }; + } + call_update_slot!(__add__); + call_update_slot!(__radd__); + call_update_slot!(__iadd__); + call_update_slot!(__sub__); + call_update_slot!(__rsub__); + call_update_slot!(__isub__); + call_update_slot!(__mul__); + call_update_slot!(__rmul__); + call_update_slot!(__imul__); + call_update_slot!(__mod__); + call_update_slot!(__rmod__); + call_update_slot!(__imod__); + call_update_slot!(__div__); + call_update_slot!(__rdiv__); + call_update_slot!(__idiv__); + call_update_slot!(__divmod__); + call_update_slot!(__rdivmod__); + call_update_slot!(__pow__); + call_update_slot!(__rpow__); + call_update_slot!(__ipow__); + call_update_slot!(__lshift__); + call_update_slot!(__rlshift__); + call_update_slot!(__ilshift__); + call_update_slot!(__rshift__); + call_update_slot!(__rrshift__); + call_update_slot!(__irshift__); + call_update_slot!(__and__); + call_update_slot!(__rand__); + call_update_slot!(__iand__); + call_update_slot!(__xor__); + call_update_slot!(__rxor__); + call_update_slot!(__ixor__); + call_update_slot!(__or__); + call_update_slot!(__ror__); + call_update_slot!(__ior__); + call_update_slot!(__floordiv__); + call_update_slot!(__rfloordiv__); + call_update_slot!(__ifloordiv__); + call_update_slot!(__truediv__); + call_update_slot!(__rtruediv__); + call_update_slot!(__itruediv__); + call_update_slot!(__matmul__); + call_update_slot!(__rmatmul__); + call_update_slot!(__imatmul__); + } +} diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index e382546b6e..d25bb404ee 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -948,6 +948,11 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) { crate::protocol::VecBuffer::make_class(&vm.ctx); builtins::extend_module(vm, &module); + use crate::AsObject; + ctx.types + .generic_alias_type + .as_object() + .init_builtin_number_slots(&vm.ctx); let debug_mode: bool = vm.state.settings.optimize == 0; extend_module!(vm, module, { diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs index 9db628f76d..95b8da260d 100644 --- a/vm/src/vm/context.rs +++ b/vm/src/vm/context.rs @@ -78,6 +78,7 @@ declare_const_name! { __aenter__, __aexit__, __aiter__, + __alloc__, __all__, __and__, __anext__, @@ -121,7 +122,9 @@ declare_const_name! { __get__, __getattr__, __getattribute__, + __getformat__, __getitem__, + __getnewargs__, __gt__, __hash__, __iadd__, @@ -146,6 +149,7 @@ declare_const_name! { __iter__, __itruediv__, __ixor__, + __jit__, // RustPython dialect __le__, __len__, __length_hint__, @@ -195,13 +199,16 @@ declare_const_name! { __rtruediv__, __rxor__, __set__, - __set_name__, __setattr__, __setitem__, + __setstate__, + __set_name__, __slots__, __str__, __sub__, __subclasscheck__, + __subclasshook__, + __subclasses__, __sizeof__, __truediv__, __trunc__, From 81fbcbc1b2d5c5693abee250a539bff5c9251e29 Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Thu, 9 Mar 2023 23:48:46 +0900 Subject: [PATCH 07/17] Temp: Implement Number Protocol for PyDictKeys and PyDictItems --- vm/src/builtins/dict.rs | 128 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 1803fdf171..514dd54f58 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -1092,7 +1092,15 @@ trait ViewSetOps: DictView { } impl ViewSetOps for PyDictKeys {} -#[pyclass(with(DictView, Constructor, Comparable, Iterable, ViewSetOps, AsSequence))] +#[pyclass(with( + DictView, + Constructor, + Comparable, + Iterable, + ViewSetOps, + AsSequence, + AsNumber +))] impl PyDictKeys { #[pymethod(magic)] fn contains(zelf: PyRef, key: PyObjectRef, vm: &VirtualMachine) -> PyResult { @@ -1133,8 +1141,70 @@ impl AsSequence for PyDictKeys { } } +impl AsNumber for PyDictKeys { + fn as_number() -> &'static PyNumberMethods { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + subtract: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num + .difference(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?, + } + .into_pyobject(vm)) + }), + and: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num + .intersection(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?, + } + .into_pyobject(vm)) + }), + xor: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num.symmetric_difference( + ArgIterable::try_from_object(vm, args.to_owned())?, + vm, + )?, + } + .into_pyobject(vm)) + }), + or: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num.union(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?, + } + .into_pyobject(vm)) + }), + ..PyNumberMethods::NOT_IMPLEMENTED + }; + &AS_NUMBER + } +} + impl ViewSetOps for PyDictItems {} -#[pyclass(with(DictView, Constructor, Comparable, Iterable, ViewSetOps, AsSequence))] +#[pyclass(with( + DictView, + Constructor, + Comparable, + Iterable, + ViewSetOps, + AsSequence, + AsNumber +))] impl PyDictItems { #[pymethod(magic)] fn contains(zelf: PyRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { @@ -1189,6 +1259,60 @@ impl AsSequence for PyDictItems { } } +impl AsNumber for PyDictItems { + fn as_number() -> &'static PyNumberMethods { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + subtract: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num + .difference(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?, + } + .into_pyobject(vm)) + }), + and: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num + .intersection(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?, + } + .into_pyobject(vm)) + }), + xor: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num.symmetric_difference( + ArgIterable::try_from_object(vm, args.to_owned())?, + vm, + )?, + } + .into_pyobject(vm)) + }), + or: Some(|num, args, vm| { + let num = PySetInner::from_iter( + ArgIterable::try_from_object(vm, num.obj.to_owned())?.iter(vm)?, + vm, + )?; + Ok(PySet { + inner: num.union(ArgIterable::try_from_object(vm, args.to_owned())?, vm)?, + } + .into_pyobject(vm)) + }), + ..PyNumberMethods::NOT_IMPLEMENTED + }; + &AS_NUMBER + } +} + #[pyclass(with(DictView, Constructor, Iterable, AsSequence))] impl PyDictValues { #[pygetset] From a949c35228969edd520a3757d724ae6412daa952 Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Thu, 9 Mar 2023 18:37:36 +0900 Subject: [PATCH 08/17] Temp: Skip test_collections.TestChainMap.test_union_operators --- Lib/test/test_collections.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 68dee5ce67..c8f6880d09 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -259,6 +259,8 @@ def __contains__(self, key): d = c.new_child(b=20, c=30) self.assertEqual(d.maps, [{'b': 20, 'c': 30}, {'a': 1, 'b': 2}]) + # TODO: RUSTPYTHON + @unittest.expectedFailure def test_union_operators(self): cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4)) cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4)) From 53b89b704c473d0815730554f9bd405f498273a2 Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Fri, 10 Mar 2023 00:10:02 +0900 Subject: [PATCH 09/17] Temp: Skip test_mmap.MmapTests.test_concat_repeat_exception --- Lib/test/test_mmap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index fa371a291d..9dbd1ebcef 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -721,6 +721,7 @@ def test_resize_past_pos(self): self.assertRaises(ValueError, m.write_byte, 42) self.assertRaises(ValueError, m.write, b'abc') + @unittest.skip('TODO: RUSTPYTHON, Number Protocol is not properly implemented yet') def test_concat_repeat_exception(self): m = mmap.mmap(-1, 16) with self.assertRaises(TypeError): From 1fea8297607f36967cd11257f93103b26e2db541 Mon Sep 17 00:00:00 2001 From: Zhiyan Xiao Date: Thu, 9 Mar 2023 23:53:13 +0900 Subject: [PATCH 10/17] Do not skip test_xml_dom_minicompat.EmptyNodeListTestCase.test_emptynodelist___radd__ --- Lib/test/test_xml_dom_minicompat.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_xml_dom_minicompat.py b/Lib/test/test_xml_dom_minicompat.py index e2931256ab..c90a01d2e4 100644 --- a/Lib/test/test_xml_dom_minicompat.py +++ b/Lib/test/test_xml_dom_minicompat.py @@ -35,8 +35,6 @@ def test_emptynodelist___add__(self): node_list = EmptyNodeList() + NodeList() self.assertEqual(node_list, NodeList()) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_emptynodelist___radd__(self): node_list = [1,2] + EmptyNodeList() self.assertEqual(node_list, [1,2]) From 788ccffb2b5717b9fffaf8bc8647ee7d294ad6ef Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 9 Mar 2023 22:58:44 +0900 Subject: [PATCH 11/17] Make PyNumberMethods static --- vm/src/builtins/bool.rs | 31 +++-------------------- vm/src/builtins/int.rs | 56 +++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 55 deletions(-) diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs index 010212e67e..417cc0e8d3 100644 --- a/vm/src/builtins/bool.rs +++ b/vm/src/builtins/bool.rs @@ -11,7 +11,6 @@ use crate::{ }; use num_bigint::Sign; use num_traits::Zero; -use once_cell::sync::Lazy; use std::fmt::{Debug, Formatter}; impl ToPyObject for bool { @@ -172,28 +171,9 @@ impl PyBool { } } -macro_rules! int_method { - ($method:ident) => { - PyInt::as_number().$method - }; -} - impl AsNumber for PyBool { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: Lazy = Lazy::new(|| PyNumberMethods { - add: int_method!(add), - subtract: int_method!(subtract), - multiply: int_method!(multiply), - remainder: int_method!(remainder), - divmod: int_method!(divmod), - power: int_method!(power), - negative: int_method!(negative), - positive: int_method!(positive), - absolute: int_method!(absolute), - boolean: int_method!(boolean), - invert: int_method!(invert), - lshift: int_method!(lshift), - rshift: int_method!(rshift), + static AS_NUMBER: PyNumberMethods = PyNumberMethods { and: Some(|number, other, vm| { PyBool::and(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) }), @@ -203,13 +183,8 @@ impl AsNumber for PyBool { or: Some(|number, other, vm| { PyBool::or(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) }), - int: int_method!(int), - float: int_method!(float), - floor_divide: int_method!(floor_divide), - true_divide: int_method!(true_divide), - index: int_method!(index), - ..PyNumberMethods::NOT_IMPLEMENTED - }); + ..PyInt::AS_NUMBER + }; &AS_NUMBER } } diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index de8a062bb2..90f611974c 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -727,33 +727,7 @@ impl Hashable for PyInt { impl AsNumber for PyInt { fn as_number() -> &'static PyNumberMethods { - static AS_NUMBER: PyNumberMethods = PyNumberMethods { - add: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)), - subtract: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a - b, vm)), - multiply: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a * b, vm)), - remainder: Some(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)), - divmod: Some(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)), - power: Some(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)), - negative: Some(|num, vm| (&PyInt::number_downcast(num).value).neg().to_pyresult(vm)), - positive: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())), - absolute: Some(|num, vm| PyInt::number_downcast(num).value.abs().to_pyresult(vm)), - boolean: Some(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())), - invert: Some(|num, vm| (&PyInt::number_downcast(num).value).not().to_pyresult(vm)), - lshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)), - rshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)), - and: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)), - xor: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)), - or: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)), - int: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), - float: Some(|num, vm| { - let zelf = PyInt::number_downcast(num); - try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x)) - }), - floor_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_floordiv, vm)), - true_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_truediv, vm)), - index: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), - ..PyNumberMethods::NOT_IMPLEMENTED - }; + static AS_NUMBER: PyNumberMethods = PyInt::AS_NUMBER; &AS_NUMBER } @@ -764,6 +738,34 @@ impl AsNumber for PyInt { } impl PyInt { + pub(super) const AS_NUMBER: PyNumberMethods = PyNumberMethods { + add: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a + b, vm)), + subtract: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a - b, vm)), + multiply: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a * b, vm)), + remainder: Some(|num, other, vm| PyInt::number_op(num, other, inner_mod, vm)), + divmod: Some(|num, other, vm| PyInt::number_op(num, other, inner_divmod, vm)), + power: Some(|num, other, vm| PyInt::number_op(num, other, inner_pow, vm)), + negative: Some(|num, vm| (&PyInt::number_downcast(num).value).neg().to_pyresult(vm)), + positive: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm).into())), + absolute: Some(|num, vm| PyInt::number_downcast(num).value.abs().to_pyresult(vm)), + boolean: Some(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())), + invert: Some(|num, vm| (&PyInt::number_downcast(num).value).not().to_pyresult(vm)), + lshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_lshift, vm)), + rshift: Some(|num, other, vm| PyInt::number_op(num, other, inner_rshift, vm)), + and: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a & b, vm)), + xor: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a ^ b, vm)), + or: Some(|num, other, vm| PyInt::number_op(num, other, |a, b, _vm| a | b, vm)), + int: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), + float: Some(|num, vm| { + let zelf = PyInt::number_downcast(num); + try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x)) + }), + floor_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_floordiv, vm)), + true_divide: Some(|num, other, vm| PyInt::number_op(num, other, inner_truediv, vm)), + index: Some(|num, vm| Ok(PyInt::number_downcast_exact(num, vm))), + ..PyNumberMethods::NOT_IMPLEMENTED + }; + fn number_op(number: PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult where F: FnOnce(&BigInt, &BigInt, &VirtualMachine) -> R, From d9fc95c2a1f2f6beb593b58ffdd0c19b74166e28 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 9 Mar 2023 22:43:15 +0900 Subject: [PATCH 12/17] Fix sequence protocol concat --- Lib/test/test_mmap.py | 1 - vm/src/protocol/number.rs | 1 + vm/src/protocol/sequence.rs | 10 +++++++--- vm/src/vm/vm_ops.rs | 7 ++++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 9dbd1ebcef..fa371a291d 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -721,7 +721,6 @@ def test_resize_past_pos(self): self.assertRaises(ValueError, m.write_byte, 42) self.assertRaises(ValueError, m.write, b'abc') - @unittest.skip('TODO: RUSTPYTHON, Number Protocol is not properly implemented yet') def test_concat_repeat_exception(self): m = mmap.mmap(-1, 16) with self.assertRaises(TypeError): diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index 6841c81918..a3fae13e3e 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -232,6 +232,7 @@ impl PyNumberMethods { } } +#[derive(Copy, Clone)] pub enum PyNumberBinaryOpSlot { Add, Subtract, diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index 529ef2d1cb..32c65083e5 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -2,7 +2,7 @@ use crate::{ builtins::{type_::PointerSlot, PyList, PyListRef, PySlice, PyTuple, PyTupleRef}, convert::ToPyObject, function::PyArithmeticValue, - protocol::PyMapping, + protocol::{PyMapping, PyNumberBinaryOpSlot}, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; @@ -118,7 +118,7 @@ impl PySequence<'_> { // if both arguments apear to be sequences, try fallback to __add__ if self.check() && other.to_sequence(vm).check() { - let ret = vm._add(self.obj, other)?; + let ret = vm.binary_op1(self.obj, other, &PyNumberBinaryOpSlot::Add)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); } @@ -137,7 +137,11 @@ impl PySequence<'_> { // fallback to __mul__ if self.check() { - let ret = vm._mul(self.obj, &n.to_pyobject(vm))?; + let ret = vm.binary_op1( + self.obj, + &n.to_pyobject(vm), + &PyNumberBinaryOpSlot::Multiply, + )?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); } diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index 6f892b828d..9832982f90 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -131,7 +131,12 @@ impl VirtualMachine { /// b.rop(b,a)[*], a.op(a,b), b.rop(b,a) /// /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a) - fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: &PyNumberBinaryOpSlot) -> PyResult { + pub fn binary_op1( + &self, + a: &PyObject, + b: &PyObject, + op_slot: &PyNumberBinaryOpSlot, + ) -> PyResult { let slot_a = a.class().slots.number.get_left_binary_op(op_slot)?; let mut slot_b = if b.class().is(a.class()) { None From 9822749c92de2d1795df1f57c04778ed9146cb59 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Thu, 9 Mar 2023 23:51:45 +0900 Subject: [PATCH 13/17] PyNumberBinaryOpSlot -> PyNumberBinaryOp it is not a slot --- vm/src/protocol/mod.rs | 2 +- vm/src/protocol/number.rs | 8 ++++---- vm/src/protocol/sequence.rs | 10 +++------- vm/src/stdlib/builtins.rs | 4 ++-- vm/src/types/slot.rs | 12 +++++------ vm/src/vm/vm_ops.rs | 40 ++++++++++++++----------------------- 6 files changed, 31 insertions(+), 45 deletions(-) diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs index 9c7aa71073..a41d843e9b 100644 --- a/vm/src/protocol/mod.rs +++ b/vm/src/protocol/mod.rs @@ -11,6 +11,6 @@ pub use callable::PyCallable; pub use iter::{PyIter, PyIterIter, PyIterReturn}; pub use mapping::{PyMapping, PyMappingMethods}; pub use number::{ - PyNumber, PyNumberBinaryFunc, PyNumberBinaryOpSlot, PyNumberMethods, PyNumberUnaryFunc, + PyNumber, PyNumberBinaryFunc, PyNumberBinaryOp, PyNumberMethods, PyNumberUnaryFunc, }; pub use sequence::{PySequence, PySequenceMethods}; diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index a3fae13e3e..a4abec0256 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -196,9 +196,9 @@ impl PyNumberMethods { pub fn get_binary_op( &self, - op_slot: &PyNumberBinaryOpSlot, + op_slot: PyNumberBinaryOp, ) -> PyResult<&Option> { - use PyNumberBinaryOpSlot::*; + use PyNumberBinaryOp::*; let binary_op = match op_slot { Add => &self.add, Subtract => &self.subtract, @@ -233,7 +233,7 @@ impl PyNumberMethods { } #[derive(Copy, Clone)] -pub enum PyNumberBinaryOpSlot { +pub enum PyNumberBinaryOp { Add, Subtract, Multiply, @@ -291,7 +291,7 @@ impl PyNumber<'_> { pub fn get_binary_op( &self, - op_slot: &PyNumberBinaryOpSlot, + op_slot: PyNumberBinaryOp, ) -> PyResult<&Option> { self.methods().get_binary_op(op_slot) } diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index 32c65083e5..36c66ad8bb 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -2,7 +2,7 @@ use crate::{ builtins::{type_::PointerSlot, PyList, PyListRef, PySlice, PyTuple, PyTupleRef}, convert::ToPyObject, function::PyArithmeticValue, - protocol::{PyMapping, PyNumberBinaryOpSlot}, + protocol::{PyMapping, PyNumberBinaryOp}, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; @@ -118,7 +118,7 @@ impl PySequence<'_> { // if both arguments apear to be sequences, try fallback to __add__ if self.check() && other.to_sequence(vm).check() { - let ret = vm.binary_op1(self.obj, other, &PyNumberBinaryOpSlot::Add)?; + let ret = vm.binary_op1(self.obj, other, PyNumberBinaryOp::Add)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); } @@ -137,11 +137,7 @@ impl PySequence<'_> { // fallback to __mul__ if self.check() { - let ret = vm.binary_op1( - self.obj, - &n.to_pyobject(vm), - &PyNumberBinaryOpSlot::Multiply, - )?; + let ret = vm.binary_op1(self.obj, &n.to_pyobject(vm), PyNumberBinaryOp::Multiply)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); } diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index d25bb404ee..95929debcf 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -21,7 +21,7 @@ mod builtins { ArgBytesLike, ArgCallable, ArgIntoBool, ArgIterable, ArgMapping, ArgStrOrBytesLike, Either, FuncArgs, KwArgs, OptionalArg, OptionalOption, PosArgs, PyArithmeticValue, }, - protocol::{PyIter, PyIterReturn, PyNumberBinaryOpSlot}, + protocol::{PyIter, PyIterReturn, PyNumberBinaryOp}, py_io, readline::{Readline, ReadlineResult}, stdlib::sys, @@ -605,7 +605,7 @@ mod builtins { modulus, } = args; match modulus { - None => vm.binary_op(&x, &y, &PyNumberBinaryOpSlot::Power, "pow"), + None => vm.binary_op(&x, &y, PyNumberBinaryOp::Power, "pow"), Some(z) => { let try_pow_value = |obj: &PyObject, args: (PyObjectRef, PyObjectRef, PyObjectRef)| diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 01900daeb3..9d3483353e 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -7,7 +7,7 @@ use crate::{ identifier, protocol::{ PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberBinaryFunc, - PyNumberBinaryOpSlot, PyNumberMethods, PyNumberUnaryFunc, PySequence, PySequenceMethods, + PyNumberBinaryOp, PyNumberMethods, PyNumberUnaryFunc, PySequence, PySequenceMethods, }, vm::Context, AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, @@ -163,9 +163,9 @@ pub struct PyNumberSlots { impl PyNumberSlots { pub fn get_left_binary_op( &self, - op_slot: &PyNumberBinaryOpSlot, + op_slot: PyNumberBinaryOp, ) -> PyResult> { - use PyNumberBinaryOpSlot::*; + use PyNumberBinaryOp::*; let binary_op = match op_slot { Add => self.add.load(), Subtract => self.subtract.load(), @@ -200,9 +200,9 @@ impl PyNumberSlots { pub fn get_right_binary_op( &self, - op_slot: &PyNumberBinaryOpSlot, + op_slot: PyNumberBinaryOp, ) -> PyResult> { - use PyNumberBinaryOpSlot::*; + use PyNumberBinaryOp::*; let binary_op = match op_slot { Add => self.right_add.load(), Subtract => self.right_subtract.load(), @@ -1281,7 +1281,7 @@ macro_rules! extend_number_slot { if $methods.$method.is_some() { $slots.number.$method.store($methods.$method); $slots.number.$right_method.store(Some(|num, other, vm| { - num.get_binary_op(&PyNumberBinaryOpSlot::$op_slot)?.unwrap()( + num.get_binary_op(PyNumberBinaryOp::$op_slot)?.unwrap()( other.to_number(), num.obj, vm, diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index 9832982f90..af178ca347 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -2,7 +2,7 @@ use super::{PyMethod, VirtualMachine}; use crate::{ builtins::{PyInt, PyIntRef, PyStr, PyStrRef}, object::{AsObject, PyObject, PyObjectRef, PyResult}, - protocol::{PyIterReturn, PyNumberBinaryOpSlot, PySequence}, + protocol::{PyIterReturn, PyNumberBinaryOp, PySequence}, types::PyComparisonOp, }; use num_traits::ToPrimitive; @@ -10,7 +10,7 @@ use num_traits::ToPrimitive; macro_rules! binary_func { ($fn:ident, $op_slot:ident, $op:expr) => { pub fn $fn(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.binary_op(a, b, &PyNumberBinaryOpSlot::$op_slot, $op) + self.binary_op(a, b, PyNumberBinaryOp::$op_slot, $op) } }; } @@ -21,8 +21,8 @@ macro_rules! inplace_binary_func { self.binary_iop( a, b, - &PyNumberBinaryOpSlot::$iop_slot, - &PyNumberBinaryOpSlot::$op_slot, + PyNumberBinaryOp::$iop_slot, + PyNumberBinaryOp::$op_slot, $op, ) } @@ -131,12 +131,7 @@ impl VirtualMachine { /// b.rop(b,a)[*], a.op(a,b), b.rop(b,a) /// /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a) - pub fn binary_op1( - &self, - a: &PyObject, - b: &PyObject, - op_slot: &PyNumberBinaryOpSlot, - ) -> PyResult { + pub fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: PyNumberBinaryOp) -> PyResult { let slot_a = a.class().slots.number.get_left_binary_op(op_slot)?; let mut slot_b = if b.class().is(a.class()) { None @@ -181,7 +176,7 @@ impl VirtualMachine { &self, a: &PyObject, b: &PyObject, - op_slot: &PyNumberBinaryOpSlot, + op_slot: PyNumberBinaryOp, op: &str, ) -> PyResult { let result = self.binary_op1(a, b, op_slot)?; @@ -208,8 +203,8 @@ impl VirtualMachine { &self, a: &PyObject, b: &PyObject, - iop_slot: &PyNumberBinaryOpSlot, - op_slot: &PyNumberBinaryOpSlot, + iop_slot: PyNumberBinaryOp, + op_slot: PyNumberBinaryOp, ) -> PyResult { if let Some(slot) = a.class().slots.number.get_left_binary_op(iop_slot)? { let x = slot(a.to_number(), b, self)?; @@ -224,8 +219,8 @@ impl VirtualMachine { &self, a: &PyObject, b: &PyObject, - iop_slot: &PyNumberBinaryOpSlot, - op_slot: &PyNumberBinaryOpSlot, + iop_slot: PyNumberBinaryOp, + op_slot: PyNumberBinaryOp, op: &str, ) -> PyResult { let result = self.binary_iop1(a, b, iop_slot, op_slot)?; @@ -261,7 +256,7 @@ impl VirtualMachine { inplace_binary_func!(_imatmul, InplaceMatrixMultiply, MatrixMultiply, "@="); pub fn _add(&self, a: &PyObject, b: &PyObject) -> PyResult { - let result = self.binary_op1(a, b, &PyNumberBinaryOpSlot::Add)?; + let result = self.binary_op1(a, b, PyNumberBinaryOp::Add)?; if !result.is(&self.ctx.not_implemented) { return Ok(result); } @@ -275,12 +270,7 @@ impl VirtualMachine { } pub fn _iadd(&self, a: &PyObject, b: &PyObject) -> PyResult { - let result = self.binary_iop1( - a, - b, - &PyNumberBinaryOpSlot::InplaceAdd, - &PyNumberBinaryOpSlot::Add, - )?; + let result = self.binary_iop1(a, b, PyNumberBinaryOp::InplaceAdd, PyNumberBinaryOp::Add)?; if !result.is(&self.ctx.not_implemented) { return Ok(result); } @@ -294,7 +284,7 @@ impl VirtualMachine { } pub fn _mul(&self, a: &PyObject, b: &PyObject) -> PyResult { - let result = self.binary_op1(a, b, &PyNumberBinaryOpSlot::Multiply)?; + let result = self.binary_op1(a, b, PyNumberBinaryOp::Multiply)?; if !result.is(&self.ctx.not_implemented) { return Ok(result); } @@ -318,8 +308,8 @@ impl VirtualMachine { let result = self.binary_iop1( a, b, - &PyNumberBinaryOpSlot::InplaceMultiply, - &PyNumberBinaryOpSlot::Multiply, + PyNumberBinaryOp::InplaceMultiply, + PyNumberBinaryOp::Multiply, )?; if !result.is(&self.ctx.not_implemented) { return Ok(result); From 3c4ac0e5a2740f6f9898cfe73f7384f6cd39b7c6 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 10 Mar 2023 00:20:26 +0900 Subject: [PATCH 14/17] clean up PyNumber interfaces --- vm/src/protocol/number.rs | 77 ++++++++++++++++----------------------- vm/src/types/slot.rs | 6 +-- vm/src/vm/vm_ops.rs | 6 +-- 3 files changed, 37 insertions(+), 52 deletions(-) diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index a4abec0256..f9a0ffb02a 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -194,41 +194,37 @@ impl PyNumberMethods { inplace_matrix_multiply: None, }; - pub fn get_binary_op( - &self, - op_slot: PyNumberBinaryOp, - ) -> PyResult<&Option> { + pub fn binary_op(&self, op_slot: PyNumberBinaryOp) -> Option { use PyNumberBinaryOp::*; - let binary_op = match op_slot { - Add => &self.add, - Subtract => &self.subtract, - Multiply => &self.multiply, - Remainder => &self.remainder, - Divmod => &self.divmod, - Power => &self.power, - Lshift => &self.lshift, - Rshift => &self.rshift, - And => &self.and, - Xor => &self.xor, - Or => &self.or, - InplaceAdd => &self.inplace_add, - InplaceSubtract => &self.inplace_subtract, - InplaceMultiply => &self.inplace_multiply, - InplaceRemainder => &self.inplace_remainder, - InplacePower => &self.inplace_power, - InplaceLshift => &self.inplace_lshift, - InplaceRshift => &self.inplace_rshift, - InplaceAnd => &self.inplace_and, - InplaceXor => &self.inplace_xor, - InplaceOr => &self.inplace_or, - FloorDivide => &self.floor_divide, - TrueDivide => &self.true_divide, - InplaceFloorDivide => &self.inplace_floor_divide, - InplaceTrueDivide => &self.inplace_true_divide, - MatrixMultiply => &self.matrix_multiply, - InplaceMatrixMultiply => &self.inplace_matrix_multiply, - }; - Ok(binary_op) + match op_slot { + Add => self.add, + Subtract => self.subtract, + Multiply => self.multiply, + Remainder => self.remainder, + Divmod => self.divmod, + Power => self.power, + Lshift => self.lshift, + Rshift => self.rshift, + And => self.and, + Xor => self.xor, + Or => self.or, + InplaceAdd => self.inplace_add, + InplaceSubtract => self.inplace_subtract, + InplaceMultiply => self.inplace_multiply, + InplaceRemainder => self.inplace_remainder, + InplacePower => self.inplace_power, + InplaceLshift => self.inplace_lshift, + InplaceRshift => self.inplace_rshift, + InplaceAnd => self.inplace_and, + InplaceXor => self.inplace_xor, + InplaceOr => self.inplace_or, + FloorDivide => self.floor_divide, + TrueDivide => self.true_divide, + InplaceFloorDivide => self.inplace_floor_divide, + InplaceTrueDivide => self.inplace_true_divide, + MatrixMultiply => self.matrix_multiply, + InplaceMatrixMultiply => self.inplace_matrix_multiply, + } } } @@ -266,7 +262,7 @@ pub enum PyNumberBinaryOp { #[derive(Copy, Clone)] pub struct PyNumber<'a> { pub obj: &'a PyObject, - methods: &'a PyNumberMethods, + pub(crate) methods: &'a PyNumberMethods, } impl<'a> From<&'a PyObject> for PyNumber<'a> { @@ -285,17 +281,6 @@ impl PyNumber<'_> { obj.class().mro_find_map(|x| x.slots.as_number.load()) } - pub fn methods(&self) -> &PyNumberMethods { - self.methods - } - - pub fn get_binary_op( - &self, - op_slot: PyNumberBinaryOp, - ) -> PyResult<&Option> { - self.methods().get_binary_op(op_slot) - } - // PyNumber_Check pub fn check(obj: &PyObject) -> bool { let methods = &obj.class().slots.number; diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 9d3483353e..32975c91a4 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -161,7 +161,7 @@ pub struct PyNumberSlots { } impl PyNumberSlots { - pub fn get_left_binary_op( + pub fn left_binary_op( &self, op_slot: PyNumberBinaryOp, ) -> PyResult> { @@ -198,7 +198,7 @@ impl PyNumberSlots { Ok(binary_op) } - pub fn get_right_binary_op( + pub fn right_binary_op( &self, op_slot: PyNumberBinaryOp, ) -> PyResult> { @@ -1281,7 +1281,7 @@ macro_rules! extend_number_slot { if $methods.$method.is_some() { $slots.number.$method.store($methods.$method); $slots.number.$right_method.store(Some(|num, other, vm| { - num.get_binary_op(PyNumberBinaryOp::$op_slot)?.unwrap()( + num.methods.binary_op(PyNumberBinaryOp::$op_slot).unwrap()( other.to_number(), num.obj, vm, diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index af178ca347..ccc77b4409 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -132,11 +132,11 @@ impl VirtualMachine { /// /// [*] only when Py_TYPE(a) != Py_TYPE(b) && Py_TYPE(b) is a subclass of Py_TYPE(a) pub fn binary_op1(&self, a: &PyObject, b: &PyObject, op_slot: PyNumberBinaryOp) -> PyResult { - let slot_a = a.class().slots.number.get_left_binary_op(op_slot)?; + let slot_a = a.class().slots.number.left_binary_op(op_slot)?; let mut slot_b = if b.class().is(a.class()) { None } else { - match b.class().slots.number.get_right_binary_op(op_slot)? { + match b.class().slots.number.right_binary_op(op_slot)? { Some(slot_b) if slot_b as usize == slot_a.map(|s| s as usize).unwrap_or_default() => { @@ -206,7 +206,7 @@ impl VirtualMachine { iop_slot: PyNumberBinaryOp, op_slot: PyNumberBinaryOp, ) -> PyResult { - if let Some(slot) = a.class().slots.number.get_left_binary_op(iop_slot)? { + if let Some(slot) = a.class().slots.number.left_binary_op(iop_slot)? { let x = slot(a.to_number(), b, self)?; if !x.is(&self.ctx.not_implemented) { return Ok(x); From 97d69bd437eaa88e096d77a60ef9a2b3ab107651 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 10 Mar 2023 01:48:51 +0900 Subject: [PATCH 15/17] pin rust-toolchain --- rust-toolchain | 1 - rust-toolchain.toml | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 rust-toolchain create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 2bf5ad0447..0000000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -stable diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..588ffd5788 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.67.1" From 91b57a3f28776aae34cbfb8c0729856a6d2ca548 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 10 Mar 2023 02:01:18 +0900 Subject: [PATCH 16/17] Fix clippy warnings --- common/src/str.rs | 4 ++-- rust-toolchain.toml | 2 +- vm/src/stdlib/io.rs | 19 ++++--------------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/common/src/str.rs b/common/src/str.rs index febab87f4e..7e2024d0f3 100644 --- a/common/src/str.rs +++ b/common/src/str.rs @@ -245,10 +245,10 @@ pub mod levenshtein { if a == b { return 0; } - if (b'A'..=b'Z').contains(&a) { + if a.is_ascii_uppercase() { a += b'a' - b'A'; } - if (b'A'..=b'Z').contains(&b) { + if b.is_ascii_uppercase() { b += b'a' - b'A'; } if a == b { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 588ffd5788..292fe499e3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.67.1" +channel = "stable" diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 087d01008b..86dfc1e19a 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -1879,8 +1879,9 @@ mod _io { write_through: bool, } - #[derive(Debug, Copy, Clone)] + #[derive(Debug, Copy, Clone, Default)] enum Newlines { + #[default] Universal, Passthrough, Lf, @@ -1888,13 +1889,6 @@ mod _io { Crlf, } - impl Default for Newlines { - #[inline] - fn default() -> Self { - Newlines::Universal - } - } - impl Newlines { /// returns position where the new line starts if found, otherwise position at which to /// continue the search after more is read into the buffer @@ -2054,8 +2048,9 @@ mod _io { data: PendingWritesData, } - #[derive(Debug)] + #[derive(Debug, Default)] enum PendingWritesData { + #[default] None, One(PendingWrite), Many(Vec), @@ -2076,12 +2071,6 @@ mod _io { } } - impl Default for PendingWritesData { - fn default() -> Self { - PendingWritesData::None - } - } - impl PendingWrites { fn push(&mut self, write: PendingWrite) { self.num_bytes += write.as_bytes().len(); From 22877202019b471e52afdc7a3236a84981b1ddd2 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 10 Mar 2023 02:46:14 +0900 Subject: [PATCH 17/17] Mark flaky test_enum::TestFlag::test_unique_composite skip --- Lib/test/test_enum.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 19273e9965..0c640c2536 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -2466,9 +2466,7 @@ class Color(StrMixin, AllMixin, Flag): self.assertEqual(Color.ALL.value, 7) self.assertEqual(str(Color.BLUE), 'blue') - # TODO: RUSTPYTHON - @unittest.expectedFailure - @unittest.skipIf(sys.platform == "win32", "TODO: RUSTPYTHON, inconsistent test result on Windows due to threading") + @unittest.skip("TODO: RUSTPYTHON, inconsistent test result on Windows due to threading") @threading_helper.reap_threads def test_unique_composite(self): # override __eq__ to be identity only