rustpython_vm/builtins/
bool.rs1use super::{PyInt, PyStrRef, PyType, PyTypeRef, PyUtf8StrRef};
2use crate::common::format::FormatSpec;
3use crate::{
4 AsObject, Context, Py, PyObject, PyObjectRef, PyResult, TryFromBorrowedObject, VirtualMachine,
5 class::PyClassImpl,
6 convert::{IntoPyException, ToPyObject, ToPyResult},
7 function::{FuncArgs, OptionalArg},
8 protocol::PyNumberMethods,
9 types::{AsNumber, Constructor, Representable},
10};
11use core::fmt::{Debug, Formatter};
12use num_traits::Zero;
13
14impl ToPyObject for bool {
15 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
16 vm.ctx.new_bool(self).into()
17 }
18}
19
20impl<'a> TryFromBorrowedObject<'a> for bool {
21 fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
22 match obj.downcast_ref::<PyInt>() {
24 Some(int_obj) => {
25 let int_val = int_obj.as_bigint();
26 Ok(!int_val.is_zero())
27 }
28 None => {
29 Err(vm.new_type_error(format!("Expected type bool, not {}", obj.class().name())))
30 }
31 }
32 }
33}
34
35impl PyObjectRef {
36 pub fn try_to_bool(self, vm: &VirtualMachine) -> PyResult<bool> {
38 if self.is(&vm.ctx.true_value) {
39 return Ok(true);
40 }
41 if self.is(&vm.ctx.false_value) {
42 return Ok(false);
43 }
44
45 let slots = &self.class().slots;
46
47 if let Some(nb_bool) = slots.as_number.boolean.load() {
49 return nb_bool(self.as_object().number(), vm);
50 }
51
52 if let Some(mp_length) = slots.as_mapping.length.load() {
54 let len = mp_length(self.as_object().mapping_unchecked(), vm)?;
55 return Ok(len != 0);
56 }
57
58 if let Some(sq_length) = slots.as_sequence.length.load() {
60 let len = sq_length(self.as_object().sequence_unchecked(), vm)?;
61 return Ok(len != 0);
62 }
63
64 Ok(true)
66 }
67}
68
69#[pyclass(name = "bool", module = false, base = PyInt, ctx = "bool_type")]
70#[repr(transparent)]
71pub struct PyBool(pub PyInt);
72
73impl Debug for PyBool {
74 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
75 let value = !self.0.as_bigint().is_zero();
76 write!(f, "PyBool({})", value)
77 }
78}
79
80impl Constructor for PyBool {
81 type Args = OptionalArg<PyObjectRef>;
82
83 fn slot_new(zelf: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
84 let x: Self::Args = args.bind(vm)?;
85 if !zelf.fast_isinstance(vm.ctx.types.type_type) {
86 let actual_class = zelf.class();
87 let actual_type = &actual_class.name();
88 return Err(vm.new_type_error(format!(
89 "requires a 'type' object but received a '{actual_type}'"
90 )));
91 }
92 let val = x.map_or(Ok(false), |val| val.try_to_bool(vm))?;
93 Ok(vm.ctx.new_bool(val).into())
94 }
95
96 fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
97 unimplemented!("use slot_new")
98 }
99}
100
101#[pyclass(with(Constructor, AsNumber, Representable), flags(_MATCH_SELF))]
102impl PyBool {
103 #[pymethod]
104 fn __format__(obj: PyObjectRef, spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<String> {
105 let new_bool = obj.try_to_bool(vm)?;
106 FormatSpec::parse(spec.as_str())
107 .and_then(|format_spec| format_spec.format_bool(new_bool))
108 .map_err(|err| err.into_pyexception(vm))
109 }
110}
111
112impl PyBool {
113 pub(crate) fn __or__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
114 if lhs.fast_isinstance(vm.ctx.types.bool_type)
115 && rhs.fast_isinstance(vm.ctx.types.bool_type)
116 {
117 let lhs = get_value(&lhs);
118 let rhs = get_value(&rhs);
119 (lhs || rhs).to_pyobject(vm)
120 } else if let Some(lhs) = lhs.downcast_ref::<PyInt>() {
121 lhs.__or__(rhs).to_pyobject(vm)
122 } else {
123 vm.ctx.not_implemented()
124 }
125 }
126
127 pub(crate) fn __and__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
128 if lhs.fast_isinstance(vm.ctx.types.bool_type)
129 && rhs.fast_isinstance(vm.ctx.types.bool_type)
130 {
131 let lhs = get_value(&lhs);
132 let rhs = get_value(&rhs);
133 (lhs && rhs).to_pyobject(vm)
134 } else if let Some(lhs) = lhs.downcast_ref::<PyInt>() {
135 lhs.__and__(rhs).to_pyobject(vm)
136 } else {
137 vm.ctx.not_implemented()
138 }
139 }
140
141 pub(crate) fn __xor__(lhs: PyObjectRef, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
142 if lhs.fast_isinstance(vm.ctx.types.bool_type)
143 && rhs.fast_isinstance(vm.ctx.types.bool_type)
144 {
145 let lhs = get_value(&lhs);
146 let rhs = get_value(&rhs);
147 (lhs ^ rhs).to_pyobject(vm)
148 } else if let Some(lhs) = lhs.downcast_ref::<PyInt>() {
149 lhs.__xor__(rhs).to_pyobject(vm)
150 } else {
151 vm.ctx.not_implemented()
152 }
153 }
154}
155
156impl AsNumber for PyBool {
157 fn as_number() -> &'static PyNumberMethods {
158 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
159 and: Some(|a, b, vm| PyBool::__and__(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
160 xor: Some(|a, b, vm| PyBool::__xor__(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
161 or: Some(|a, b, vm| PyBool::__or__(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
162 ..PyInt::AS_NUMBER
163 };
164 &AS_NUMBER
165 }
166}
167
168impl Representable for PyBool {
169 #[inline]
170 fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
171 let name = if get_value(zelf.as_object()) {
172 vm.ctx.names.True
173 } else {
174 vm.ctx.names.False
175 };
176 Ok(name.to_owned())
177 }
178
179 #[cold]
180 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
181 unreachable!("use slot_repr instead")
182 }
183}
184
185fn vectorcall_bool(
186 zelf_obj: &PyObject,
187 args: Vec<PyObjectRef>,
188 nargs: usize,
189 kwnames: Option<&[PyObjectRef]>,
190 vm: &VirtualMachine,
191) -> PyResult {
192 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
193 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
194 (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
195}
196
197pub(crate) fn init(context: &'static Context) {
198 PyBool::extend_class(context, context.types.bool_type);
199 context
200 .types
201 .bool_type
202 .slots
203 .vectorcall
204 .store(Some(vectorcall_bool));
205}
206
207pub(crate) fn get_value(obj: &PyObject) -> bool {
218 !obj.downcast_ref::<PyBool>()
219 .unwrap()
220 .0
221 .as_bigint()
222 .is_zero()
223}