Skip to content

Implement Number Protocol for PyBool#4639

Closed
xiaozhiyan wants to merge 1 commit into
RustPython:mainfrom
xiaozhiyan:implement-number-protocol-for-pybool
Closed

Implement Number Protocol for PyBool#4639
xiaozhiyan wants to merge 1 commit into
RustPython:mainfrom
xiaozhiyan:implement-number-protocol-for-pybool

Conversation

@xiaozhiyan

@xiaozhiyan xiaozhiyan commented Mar 5, 2023

Copy link
Copy Markdown
Contributor

Closes #4638

In CPython, bool is a subclass of int, and its as_number method overrides and, xor and or.

In RustPython, as PyBool is not a subclass of PyInt, the as_number "overriding" is done by including all other methods of PyInt.

@fanninpm fanninpm requested a review from qingshi163 March 5, 2023 14:30
@qingshi163

Copy link
Copy Markdown
Contributor

In RustPython PyBool is a subclass of PyInt

>>>>> isinstance(True, int)
True

Comment thread vm/src/builtins/bool.rs
Comment on lines +186 to +198
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),

Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Contributor Author

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 for bool in CPython.

>>> (True + True) ** True
2

Copy link
Copy Markdown
Contributor Author

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 of PyInt and inherits its methods, so (True + True) ** True should somehow work without the above overriding in as_number I think.

Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Contributor

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

Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Member

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?

Copy link
Copy Markdown
Contributor

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

Copy link
Copy Markdown
Contributor

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slots will be inherit if subclass does not override it.

It is true in cpython but not here yet. That's the reason we always call mro_find_map() to find slots.

Comment thread vm/src/builtins/bool.rs
Comment on lines +199 to +207
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)
}),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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.
But it looks working fine.

To fully optimize it, PyBool::and can be changed like this and AsNumber can call inner_and

    #[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)
        }
    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach seems not working directly, since get_py_int(&lhs).and(rhs, vm) requires rhs to be an PyObjectRef as defined in int.rs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe rhs.to_owned() fornow. we can optimize int.and also in same way later

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure but would rhs.to_owned() be an extra burden? If it's ok (for now), I will adopt the above practice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, rhs.to_owned() is extra cost, and still same as previous one. Adding TODO: or FIXME: can be helpful.
It is on the way to go to the right design.

@youknowone

Copy link
Copy Markdown
Member

The inheritance is set like this:

#[pyclass(name = "bool", module = false, base = "PyInt")]  // this `PyInt` part
struct PyBool;

@xiaozhiyan

Copy link
Copy Markdown
Contributor Author

In RustPython PyBool is a subclass of PyInt

>>>>> isinstance(True, int)
True

I'm not sure, but does that mean PyBool will inherit existing methods of PyInt please?

@youknowone

Copy link
Copy Markdown
Member

Yes, int.to_bytes is a method of int.

>>>>> int.to_bytes == bool.to_bytes
True

@youknowone youknowone added the z-ls-2023 Tag to track Line OSS Sprint 2023 label Mar 7, 2023
@xiaozhiyan xiaozhiyan deleted the implement-number-protocol-for-pybool branch March 9, 2023 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

z-ls-2023 Tag to track Line OSS Sprint 2023

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Number Protocol for PyBool

3 participants