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)) 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 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]) 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/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/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..292fe499e3 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable" 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/bool.rs b/vm/src/builtins/bool.rs index ed21290f65..417cc0e8d3 100644 --- a/vm/src/builtins/bool.rs +++ b/vm/src/builtins/bool.rs @@ -1,6 +1,11 @@ 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, }; @@ -102,7 +107,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 +171,24 @@ impl PyBool { } } +impl AsNumber for PyBool { + fn as_number() -> &'static PyNumberMethods { + static AS_NUMBER: PyNumberMethods = PyNumberMethods { + 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) + }), + ..PyInt::AS_NUMBER + }; + &AS_NUMBER + } +} + pub(crate) fn init(context: &Context) { PyBool::extend_class(context, context.types.bool_type); } 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..514dd54f58 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 } } @@ -1089,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 { @@ -1130,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 { @@ -1186,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] 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..90f611974c 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,7 @@ 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| { - 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))), - ..PyNumberMethods::NOT_IMPLEMENTED - }); + static AS_NUMBER: PyNumberMethods = PyInt::AS_NUMBER; &AS_NUMBER } @@ -787,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, 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..f26dcfef1b 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")] @@ -187,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), @@ -220,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 { @@ -280,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() } @@ -1061,12 +1062,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 } } @@ -1328,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/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 4170c20df6..a41d843e9b 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, PyNumberMethods}; +pub use number::{ + 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 bbf88de6ab..f9a0ffb02a 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,46 +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_divmod: 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 { @@ -160,48 +157,112 @@ 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_divmod: 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 binary_op(&self, op_slot: PyNumberBinaryOp) -> Option { + use PyNumberBinaryOp::*; + 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, + } + } } +#[derive(Copy, Clone)] +pub enum PyNumberBinaryOp { + Add, + Subtract, + Multiply, + Remainder, + Divmod, + Power, + Lshift, + Rshift, + And, + Xor, + Or, + InplaceAdd, + InplaceSubtract, + InplaceMultiply, + InplaceRemainder, + InplacePower, + InplaceLshift, + InplaceRshift, + InplaceAnd, + InplaceXor, + InplaceOr, + FloorDivide, + TrueDivide, + InplaceFloorDivide, + InplaceTrueDivide, + MatrixMultiply, + InplaceMatrixMultiply, +} + +#[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> { @@ -220,16 +281,9 @@ impl PyNumber<'_> { obj.class().mro_find_map(|x| x.slots.as_number.load()) } - pub fn methods(&self) -> &PyNumberMethods { - self.methods - } - // 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() @@ -238,12 +292,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( @@ -267,7 +321,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( @@ -291,7 +345,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/protocol/sequence.rs b/vm/src/protocol/sequence.rs index 529ef2d1cb..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, + 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._add(self.obj, other)?; + let ret = vm.binary_op1(self.obj, other, PyNumberBinaryOp::Add)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); } @@ -137,7 +137,7 @@ 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), 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 fa2d917682..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}, + protocol::{PyIter, PyIterReturn, PyNumberBinaryOp}, py_io, readline::{Readline, ReadlineResult}, stdlib::sys, @@ -605,13 +605,7 @@ 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, PyNumberBinaryOp::Power, "pow"), Some(z) => { let try_pow_value = |obj: &PyObject, args: (PyObjectRef, PyObjectRef, PyObjectRef)| @@ -954,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/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(); diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index f0647d31e0..32975c91a4 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, + PyNumberBinaryOp, 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 left_binary_op( + &self, + op_slot: PyNumberBinaryOp, + ) -> PyResult> { + use PyNumberBinaryOp::*; + 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 right_binary_op( + &self, + op_slot: PyNumberBinaryOp, + ) -> PyResult> { + use PyNumberBinaryOp::*; + 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 { @@ -214,6 +336,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,)) } @@ -361,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)); @@ -473,16 +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_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_subslot!(number, inplace_add, number_binary_op_wrapper!(__iadd__)); + } + _ if name == identifier!(ctx, __sub__) => { + 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_subslot!( + number, + inplace_subtract, + number_binary_op_wrapper!(__isub__) + ); + } + _ if name == identifier!(ctx, __mul__) => { + 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_subslot!( + number, + inplace_multiply, + number_binary_op_wrapper!(__imul__) + ); + } + _ if name == identifier!(ctx, __mod__) => { + 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_subslot!( + number, + inplace_remainder, + number_binary_op_wrapper!(__imod__) + ); + } + _ if name == identifier!(ctx, __divmod__) => { + 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_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_subslot!(number, inplace_power, number_binary_op_wrapper!(__ipow__)); + } + _ if name == identifier!(ctx, __lshift__) => { + 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_subslot!( + number, + inplace_lshift, + number_binary_op_wrapper!(__ilshift__) + ); + } + _ if name == identifier!(ctx, __rshift__) => { + 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_subslot!( + number, + inplace_rshift, + number_binary_op_wrapper!(__irshift__) + ); + } + _ if name == identifier!(ctx, __and__) => { + 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_subslot!(number, inplace_and, number_binary_op_wrapper!(__iand__)); + } + _ if name == identifier!(ctx, __xor__) => { + 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_subslot!(number, inplace_xor, number_binary_op_wrapper!(__ixor__)); + } + _ if name == identifier!(ctx, __or__) => { + 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_subslot!(number, inplace_or, number_binary_op_wrapper!(__ior__)); + } + _ if name == identifier!(ctx, __floordiv__) => { + toggle_subslot!( + number, + floor_divide, + number_binary_op_wrapper!(__floordiv__) + ); + } + _ if name == identifier!(ctx, __rfloordiv__) => { + toggle_subslot!( + number, + right_floor_divide, + number_binary_op_wrapper!(__rfloordiv__) + ); + } + _ if name == identifier!(ctx, __ifloordiv__) => { + toggle_subslot!( + number, + inplace_floor_divide, + number_binary_op_wrapper!(__ifloordiv__) + ); + } + _ if name == identifier!(ctx, __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__) + ); + } + _ if name == identifier!(ctx, __itruediv__) => { + toggle_subslot!( + number, + inplace_true_divide, + number_binary_op_wrapper!(__itruediv__) + ); + } + _ if name == identifier!(ctx, __matmul__) => { + toggle_subslot!( + number, + matrix_multiply, + number_binary_op_wrapper!(__matmul__) + ); + } + _ if name == identifier!(ctx, __rmatmul__) => { + toggle_subslot!( + number, + right_matrix_multiply, + number_binary_op_wrapper!(__rmatmul__) + ); + } + _ if name == identifier!(ctx, __imatmul__) => { + toggle_subslot!( + number, + inplace_matrix_multiply, + number_binary_op_wrapper!(__imatmul__) + ); } _ => {} } @@ -961,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.methods.binary_op(PyNumberBinaryOp::$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] @@ -984,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/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__, diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index c0530a6916..ccc77b4409 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -1,11 +1,33 @@ 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, + protocol::{PyIterReturn, PyNumberBinaryOp, 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, PyNumberBinaryOp::$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, + PyNumberBinaryOp::$iop_slot, + PyNumberBinaryOp::$op_slot, + $op, + ) + } + }; +} /// Collection of operators impl VirtualMachine { @@ -103,371 +125,212 @@ impl VirtualMachine { } } - /// 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) - { + /// Order operations are tried until either a valid result or error: + /// 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: PyNumberBinaryOp) -> PyResult { + 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.right_binary_op(op_slot)? { + 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_bb) = slot_b { + if b.fast_isinstance(a.class()) { + 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(a.to_number(), b, self)?; + if !x.is(&self.ctx.not_implemented) { return Ok(x); } } - unsupported(self, obj, arg) + + if let Some(slot_b) = slot_b { + let x = slot_b(b.to_number(), a, self)?; + if !x.is(&self.ctx.not_implemented) { + return Ok(x); + } + } + + Ok(self.ctx.not_implemented()) } - /// Calls a method, falling back to its reflection with the operands - /// reversed, and then to the value provided by `unsupported`. + pub fn binary_op( + &self, + a: &PyObject, + b: &PyObject, + op_slot: PyNumberBinaryOp, + op: &str, + ) -> 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 /// - /// For example: the following: + /// 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. /// - /// `call_or_reflection(lhs, rhs, "__and__", "__rand__", unsupported)` + /// - 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. /// - /// 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( + /// - Otherwise, in-place modification is not supported. Handle it exactly as + /// a non in-place operation of the same kind. + fn binary_iop1( &self, - lhs: &PyObject, - rhs: &PyObject, - default: &'static PyStrInterned, - reflection: &'static PyStrInterned, - unsupported: fn(&VirtualMachine, &PyObject, &PyObject) -> PyResult, + a: &PyObject, + b: &PyObject, + iop_slot: PyNumberBinaryOp, + op_slot: PyNumberBinaryOp, ) -> 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); - } - } + 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); } } - // 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) - } - }) - } - - 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, "-")), - ) + self.binary_op1(a, b, op_slot) } - 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, "-=")), - ) - }) - } + fn binary_iop( + &self, + a: &PyObject, + b: &PyObject, + iop_slot: PyNumberBinaryOp, + op_slot: PyNumberBinaryOp, + op: &str, + ) -> 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.call_or_reflection( - a, - b, - identifier!(self, __add__), - identifier!(self, __radd__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "+")), - ) + let result = self.binary_op1(a, b, PyNumberBinaryOp::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); + } + } + Err(self.new_unsupported_binop_error(a, b, "+")) } 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, "+=")), - ) - }) + let result = self.binary_iop1(a, b, PyNumberBinaryOp::InplaceAdd, PyNumberBinaryOp::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.call_or_reflection( - a, - b, - identifier!(self, __mul__), - identifier!(self, __rmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "*")), - ) + let result = self.binary_op1(a, b, PyNumberBinaryOp::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, 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, self); + } + Err(self.new_unsupported_binop_error(a, b, "*")) } 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 _matmul(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __matmul__), - identifier!(self, __rmatmul__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "@")), - ) - } - - 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 _truediv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __truediv__), - identifier!(self, __rtruediv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "/")), - ) - } - - 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 _floordiv(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __floordiv__), - identifier!(self, __rfloordiv__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "//")), - ) - } - - 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 _pow(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __pow__), - identifier!(self, __rpow__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "**")), - ) - } - - 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 _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, "%")), - ) - } - - 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, "%=")), - ) - }) - } - - 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")), - ) - } - - pub fn _lshift(&self, a: &PyObject, b: &PyObject) -> PyResult { - self.call_or_reflection( - a, - b, - identifier!(self, __lshift__), - identifier!(self, __rlshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "<<")), - ) - } - - 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.call_or_reflection( - a, - b, - identifier!(self, __rshift__), - identifier!(self, __rrshift__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, ">>")), - ) - } - - 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.call_or_reflection( + let result = self.binary_iop1( a, b, - identifier!(self, __xor__), - identifier!(self, __rxor__), - |vm, a, b| Err(vm.new_unsupported_binop_error(a, b, "^")), - ) - } - - 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, "^=")), - ) - }) - } - - 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, "|")), - ) - } - - 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, "|=")), - ) - }) - } - - 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, "&")), - ) - } - - 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, "&=")), - ) - }) + PyNumberBinaryOp::InplaceMultiply, + PyNumberBinaryOp::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 {