1use super::{PyInt, PyStrRef, PyType, PyTypeRef, PyUtf8StrRef};
2use crate::common::format::FormatSpec;
3use crate::{
4 AsObject, Context, Py, PyObject, PyObjectRef, PyResult, TryFromBorrowedObject, VirtualMachine,
5 class::{PyClassDef, 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 PyObject {
36 #[inline(always)]
42 pub fn try_to_bool(&self, vm: &VirtualMachine) -> PyResult<bool> {
43 if self.is(&vm.ctx.true_value) {
44 return Ok(true);
45 } else if self.is(&vm.ctx.false_value) {
46 return Ok(false);
47 }
48
49 self.try_to_bool_slow(vm)
50 }
51
52 #[cold]
53 #[inline(never)]
54 fn try_to_bool_slow(&self, vm: &VirtualMachine) -> PyResult<bool> {
55 let slots = self.class().slots();
56
57 if let Some(nb_bool) = slots.as_number.boolean.load() {
59 return nb_bool(self.as_object().number(), vm);
60 }
61
62 if let Some(mp_length) = slots.as_mapping.length.load() {
64 let len = mp_length(self.as_object().mapping_unchecked(), vm)?;
65 return Ok(len != 0);
66 }
67
68 if let Some(sq_length) = slots.as_sequence.length.load() {
70 let len = sq_length(self.as_object().sequence_unchecked(), vm)?;
71 return Ok(len != 0);
72 }
73
74 Ok(true)
76 }
77}
78
79#[pyclass(name = "bool", module = false, base = PyInt, ctx = "bool_type")]
80#[repr(transparent)]
81pub struct PyBool(pub PyInt);
82
83impl Debug for PyBool {
84 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
85 let value = !self.0.as_bigint().is_zero();
86 write!(f, "PyBool({value})")
87 }
88}
89
90#[derive(FromArgs)]
91pub struct BoolArgs {
92 #[pyarg(positional, default, py_default = "False")]
94 object: OptionalArg<PyObjectRef>,
95}
96
97impl Constructor for PyBool {
98 type Args = BoolArgs;
99
100 fn slot_new(zelf: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
101 let x: Self::Args = args.bind_for(vm, Self::NAME)?;
102 let x = x.object;
103 if !zelf.fast_isinstance(vm.ctx.types.type_type) {
104 return Err(vm.new_type_error(format!(
105 "requires a 'type' object but received a '{}'",
106 zelf.class().name()
107 )));
108 }
109 let val = x.map_or(Ok(false), |val| val.try_to_bool(vm))?;
110 Ok(vm.ctx.new_bool(val).into())
111 }
112
113 fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
114 unimplemented!("use slot_new")
115 }
116}
117
118#[pyclass(with(Constructor, AsNumber, Representable), flags(_MATCH_SELF))]
119impl Py<PyBool> {
120 #[pymethod]
121 fn __format__(
122 zelf: PyObjectRef,
123 format_spec: PyUtf8StrRef,
124 vm: &VirtualMachine,
125 ) -> PyResult<String> {
126 let new_bool = zelf.try_to_bool(vm)?;
127 FormatSpec::parse(format_spec.as_str())
128 .and_then(|format_spec| format_spec.format_bool(new_bool))
129 .map_err(|err| err.into_pyexception(vm))
130 }
131}
132
133impl PyBool {
134 pub(crate) fn __or__(lhs: &PyObject, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
135 if lhs.fast_isinstance(vm.ctx.types.bool_type)
136 && rhs.fast_isinstance(vm.ctx.types.bool_type)
137 {
138 let lhs = get_value(lhs);
139 let rhs = get_value(&rhs);
140 (lhs || rhs).to_pyobject(vm)
141 } else if let Some(lhs) = lhs.downcast_ref::<PyInt>() {
142 lhs.__or__(rhs).to_pyobject(vm)
143 } else {
144 vm.ctx.not_implemented()
145 }
146 }
147
148 pub(crate) fn __and__(lhs: &PyObject, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
149 if lhs.fast_isinstance(vm.ctx.types.bool_type)
150 && rhs.fast_isinstance(vm.ctx.types.bool_type)
151 {
152 let lhs = get_value(lhs);
153 let rhs = get_value(&rhs);
154 (lhs && rhs).to_pyobject(vm)
155 } else if let Some(lhs) = lhs.downcast_ref::<PyInt>() {
156 lhs.__and__(rhs).to_pyobject(vm)
157 } else {
158 vm.ctx.not_implemented()
159 }
160 }
161
162 pub(crate) fn __xor__(lhs: &PyObject, rhs: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
163 if lhs.fast_isinstance(vm.ctx.types.bool_type)
164 && rhs.fast_isinstance(vm.ctx.types.bool_type)
165 {
166 let lhs = get_value(lhs);
167 let rhs = get_value(&rhs);
168 (lhs ^ rhs).to_pyobject(vm)
169 } else if let Some(lhs) = lhs.downcast_ref::<PyInt>() {
170 lhs.__xor__(rhs).to_pyobject(vm)
171 } else {
172 vm.ctx.not_implemented()
173 }
174 }
175}
176
177impl AsNumber for PyBool {
178 fn as_number() -> &'static PyNumberMethods {
179 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
180 and: Some(|a, b, vm| PyBool::__and__(a, b.to_owned(), vm).to_pyresult(vm)),
181 xor: Some(|a, b, vm| PyBool::__xor__(a, b.to_owned(), vm).to_pyresult(vm)),
182 or: Some(|a, b, vm| PyBool::__or__(a, b.to_owned(), vm).to_pyresult(vm)),
183 ..PyNumberMethods::NOT_IMPLEMENTED
184 };
185 &AS_NUMBER
186 }
187}
188
189impl Representable for PyBool {
190 #[inline]
191 fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyStrRef> {
192 let name = if get_value(zelf.as_object()) {
193 vm.ctx.names.True
194 } else {
195 vm.ctx.names.False
196 };
197 Ok(name.to_owned())
198 }
199
200 #[cold]
201 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
202 unreachable!("use slot_repr instead")
203 }
204}
205
206fn vectorcall_bool(
207 zelf_obj: &PyObject,
208 args: Vec<PyObjectRef>,
209 nargs: usize,
210 kwnames: Option<&[PyObjectRef]>,
211 vm: &VirtualMachine,
212) -> PyResult {
213 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
214 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
215 (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)
216}
217
218pub(crate) fn init(context: &'static Context) {
219 PyBool::extend_class(context, context.types.bool_type);
220 context
221 .types
222 .bool_type
223 .slots
224 .vectorcall
225 .store(Some(vectorcall_bool));
226}
227
228pub(crate) fn get_value(obj: &PyObject) -> bool {
239 !obj.downcast_ref::<PyBool>()
240 .unwrap()
241 .0
242 .as_bigint()
243 .is_zero()
244}