-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Implement Number Protocol for PyBool
#4639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,19 @@ | ||
use super::{PyInt, PyStrRef, PyType, PyTypeRef}; | ||
use crate::{ | ||
class::PyClassImpl, convert::ToPyObject, function::OptionalArg, identifier, types::Constructor, | ||
atomic_func, | ||
class::PyClassImpl, | ||
convert::{ToPyObject, ToPyResult}, | ||
function::OptionalArg, | ||
identifier, | ||
protocol::PyNumberMethods, | ||
types::{AsNumber, Constructor}, | ||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, | ||
VirtualMachine, | ||
}; | ||
use crossbeam_utils::atomic::AtomicCell; | ||
use num_bigint::Sign; | ||
use num_traits::Zero; | ||
use once_cell::sync::Lazy; | ||
use std::fmt::{Debug, Formatter}; | ||
|
||
impl ToPyObject for bool { | ||
|
@@ -102,7 +110,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 +174,48 @@ impl PyBool { | |
} | ||
} | ||
|
||
macro_rules! int_method { | ||
($method:ident) => { | ||
AtomicCell::new(PyInt::as_number().$method.load().to_owned()) | ||
}; | ||
} | ||
|
||
impl AsNumber for PyBool { | ||
fn as_number() -> &'static PyNumberMethods { | ||
static AS_NUMBER: Lazy<PyNumberMethods> = Lazy::new(|| PyNumberMethods { | ||
add: int_method!(add), | ||
subtract: int_method!(subtract), | ||
multiply: int_method!(multiply), | ||
remainder: int_method!(remainder), | ||
divmod: int_method!(divmod), | ||
power: int_method!(power), | ||
negative: int_method!(negative), | ||
positive: int_method!(positive), | ||
absolute: int_method!(absolute), | ||
boolean: int_method!(boolean), | ||
invert: int_method!(invert), | ||
lshift: int_method!(lshift), | ||
rshift: int_method!(rshift), | ||
and: atomic_func!(|number, other, vm| { | ||
PyBool::and(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) | ||
}), | ||
xor: atomic_func!(|number, other, vm| { | ||
PyBool::xor(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) | ||
}), | ||
or: atomic_func!(|number, other, vm| { | ||
PyBool::or(number.obj.to_owned(), other.to_owned(), vm).to_pyresult(vm) | ||
}), | ||
Comment on lines
+199
to
+207
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is more room to optimize because and, xor, or actually doesn't require to increase reference count. To fully optimize it, #[pymethod(name = "__rand__")]
#[pymethod(magic)]
fn and(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
Self::inner_and(&lhs, &rhs, vm)
}
fn inner_and(lhs: &PyObject, rhs: &PyObject, vm: &VirtualMachine) -> PyObjectRef {
if lhs.fast_isinstance(vm.ctx.types.bool_type)
&& rhs.fast_isinstance(vm.ctx.types.bool_type)
{
let lhs = get_value(&lhs);
let rhs = get_value(&rhs);
(lhs && rhs).to_pyobject(vm)
} else {
get_py_int(&lhs).and(rhs, vm).to_pyobject(vm)
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This approach seems not working directly, since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure but would There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, |
||
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); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure yet, but I think those fields in bool have to be
None
.In my opinion, If that's not working with None, any inherited object of numbers can make similar problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems
int
operations are needed forbool
in CPython.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since
PyBool
is a subclass ofPyInt
and inherits its methods, so(True + True) ** True
should somehow work without the above overriding inas_number
I think.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am sorry to make confusion. I agree bool need int operations.
But I'd like to say manually filling
PyNumberMethods
is not the way to do.We can do that job only for builtin types written in Rust.
Making them
None
and finding a way to make it work will be helpful to solve the problem in general.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did not test but I think PyBool is working with int number methods via mro search in PyNumber::find_methods().
But Maybe we can override it in PyBool to have some optimize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it wasn't. Because that is a static variable. We have no way to edit it later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@qingshi163 do you have any suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can inherit slots when initializing the subclass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/python/cpython/blob/71db5dbcd714b2e1297c43538188dd69715feb9a/Objects/typeobject.c#L8849-L8902
slots will be inherit if subclass does not override it.
when there is a change of slots on the type, cpython recusively update slots for all the subclasses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is true in cpython but not here yet. That's the reason we always call
mro_find_map()
to find slots.