1use super::{
2 mappingproxy::PyMappingProxy, object, union_, PyClassMethod, PyDictRef, PyList, PyStr,
3 PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
4};
5use crate::{
6 builtins::{
7 descriptor::{
8 MemberGetter, MemberKind, MemberSetter, PyDescriptorOwned, PyMemberDef,
9 PyMemberDescriptor,
10 },
11 function::PyCellRef,
12 tuple::{IntoPyTuple, PyTupleTyped},
13 PyBaseExceptionRef,
14 },
15 class::{PyClassImpl, StaticType},
16 common::{
17 ascii,
18 borrow::BorrowedValue,
19 lock::{PyRwLock, PyRwLockReadGuard},
20 },
21 convert::ToPyResult,
22 function::{FuncArgs, KwArgs, OptionalArg, PyMethodDef, PySetterValue},
23 identifier,
24 object::{Traverse, TraverseFn},
25 protocol::{PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
26 types::{
27 AsNumber, Callable, Constructor, GetAttr, PyTypeFlags, PyTypeSlots, Representable, SetAttr,
28 },
29 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
30 VirtualMachine,
31};
32use indexmap::{map::Entry, IndexMap};
33use itertools::Itertools;
34use std::{borrow::Borrow, collections::HashSet, fmt, ops::Deref, pin::Pin, ptr::NonNull};
35
36#[pyclass(module = false, name = "type", traverse = "manual")]
37pub struct PyType {
38 pub base: Option<PyTypeRef>,
39 pub bases: PyRwLock<Vec<PyTypeRef>>,
40 pub mro: PyRwLock<Vec<PyTypeRef>>,
41 pub subclasses: PyRwLock<Vec<PyRef<PyWeak>>>,
42 pub attributes: PyRwLock<PyAttributes>,
43 pub slots: PyTypeSlots,
44 pub heaptype_ext: Option<Pin<Box<HeapTypeExt>>>,
45}
46
47unsafe impl crate::object::Traverse for PyType {
48 fn traverse(&self, tracer_fn: &mut crate::object::TraverseFn) {
49 self.base.traverse(tracer_fn);
50 self.bases.traverse(tracer_fn);
51 self.mro.traverse(tracer_fn);
52 self.subclasses.traverse(tracer_fn);
53 self.attributes
54 .read_recursive()
55 .iter()
56 .map(|(_, v)| v.traverse(tracer_fn))
57 .count();
58 }
59}
60
61pub struct HeapTypeExt {
62 pub name: PyRwLock<PyStrRef>,
63 pub slots: Option<PyTupleTyped<PyStrRef>>,
64 pub sequence_methods: PySequenceMethods,
65 pub mapping_methods: PyMappingMethods,
66}
67
68pub struct PointerSlot<T>(NonNull<T>);
69
70impl<T> PointerSlot<T> {
71 pub unsafe fn borrow_static(&self) -> &'static T {
72 self.0.as_ref()
73 }
74}
75
76impl<T> Clone for PointerSlot<T> {
77 fn clone(&self) -> Self {
78 *self
79 }
80}
81
82impl<T> Copy for PointerSlot<T> {}
83
84impl<T> From<&'static T> for PointerSlot<T> {
85 fn from(x: &'static T) -> Self {
86 Self(NonNull::from(x))
87 }
88}
89
90impl<T> AsRef<T> for PointerSlot<T> {
91 fn as_ref(&self) -> &T {
92 unsafe { self.0.as_ref() }
93 }
94}
95
96impl<T> PointerSlot<T> {
97 pub unsafe fn from_heaptype<F>(typ: &PyType, f: F) -> Option<Self>
98 where
99 F: FnOnce(&HeapTypeExt) -> &T,
100 {
101 typ.heaptype_ext
102 .as_ref()
103 .map(|ext| Self(NonNull::from(f(ext))))
104 }
105}
106
107pub type PyTypeRef = PyRef<PyType>;
108
109cfg_if::cfg_if! {
110 if #[cfg(feature = "threading")] {
111 unsafe impl Send for PyType {}
112 unsafe impl Sync for PyType {}
113 }
114}
115
116pub type PyAttributes = IndexMap<&'static PyStrInterned, PyObjectRef, ahash::RandomState>;
120
121unsafe impl Traverse for PyAttributes {
122 fn traverse(&self, tracer_fn: &mut TraverseFn) {
123 self.values().for_each(|v| v.traverse(tracer_fn));
124 }
125}
126
127impl fmt::Display for PyType {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 fmt::Display::fmt(&self.name(), f)
130 }
131}
132
133impl fmt::Debug for PyType {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 write!(f, "[PyType {}]", &self.name())
136 }
137}
138
139impl PyPayload for PyType {
140 fn class(ctx: &Context) -> &'static Py<PyType> {
141 ctx.types.type_type
142 }
143}
144
145impl PyType {
146 pub fn new_simple_heap(
147 name: &str,
148 base: &PyTypeRef,
149 ctx: &Context,
150 ) -> Result<PyRef<Self>, String> {
151 Self::new_heap(
152 name,
153 vec![base.clone()],
154 Default::default(),
155 Default::default(),
156 Self::static_type().to_owned(),
157 ctx,
158 )
159 }
160 pub fn new_heap(
161 name: &str,
162 bases: Vec<PyRef<Self>>,
163 attrs: PyAttributes,
164 slots: PyTypeSlots,
165 metaclass: PyRef<Self>,
166 ctx: &Context,
167 ) -> Result<PyRef<Self>, String> {
168 let name = ctx.new_str(name);
172 let heaptype_ext = HeapTypeExt {
173 name: PyRwLock::new(name),
174 slots: None,
175 sequence_methods: PySequenceMethods::default(),
176 mapping_methods: PyMappingMethods::default(),
177 };
178 let base = bases[0].clone();
179
180 Self::new_heap_inner(base, bases, attrs, slots, heaptype_ext, metaclass, ctx)
181 }
182
183 fn resolve_mro(bases: &[PyRef<Self>]) -> Result<Vec<PyTypeRef>, String> {
184 let mut unique_bases = HashSet::new();
186 for base in bases {
187 if !unique_bases.insert(base.get_id()) {
188 return Err(format!("duplicate base class {}", base.name()));
189 }
190 }
191
192 let mros = bases
193 .iter()
194 .map(|base| base.mro_map_collect(|t| t.to_owned()))
195 .collect();
196 linearise_mro(mros)
197 }
198
199 #[allow(clippy::too_many_arguments)]
200 fn new_heap_inner(
201 base: PyRef<Self>,
202 bases: Vec<PyRef<Self>>,
203 attrs: PyAttributes,
204 mut slots: PyTypeSlots,
205 heaptype_ext: HeapTypeExt,
206 metaclass: PyRef<Self>,
207 ctx: &Context,
208 ) -> Result<PyRef<Self>, String> {
209 let mro = Self::resolve_mro(&bases)?;
210
211 if base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
212 slots.flags |= PyTypeFlags::HAS_DICT
213 }
214 if slots.basicsize == 0 {
215 slots.basicsize = base.slots.basicsize;
216 }
217
218 if let Some(qualname) = attrs.get(identifier!(ctx, __qualname__)) {
219 if !qualname.fast_isinstance(ctx.types.str_type) {
220 return Err(format!(
221 "type __qualname__ must be a str, not {}",
222 qualname.class().name()
223 ));
224 }
225 }
226
227 let new_type = PyRef::new_ref(
228 PyType {
229 base: Some(base),
230 bases: PyRwLock::new(bases),
231 mro: PyRwLock::new(mro),
232 subclasses: PyRwLock::default(),
233 attributes: PyRwLock::new(attrs),
234 slots,
235 heaptype_ext: Some(Pin::new(Box::new(heaptype_ext))),
236 },
237 metaclass,
238 None,
239 );
240
241 new_type.init_slots(ctx);
242
243 let weakref_type = super::PyWeak::static_type();
244 for base in new_type.bases.read().iter() {
245 base.subclasses.write().push(
246 new_type
247 .as_object()
248 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
249 .unwrap(),
250 );
251 }
252
253 Ok(new_type)
254 }
255
256 pub fn new_static(
257 base: PyRef<Self>,
258 attrs: PyAttributes,
259 mut slots: PyTypeSlots,
260 metaclass: PyRef<Self>,
261 ) -> Result<PyRef<Self>, String> {
262 if base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
263 slots.flags |= PyTypeFlags::HAS_DICT
264 }
265 if slots.basicsize == 0 {
266 slots.basicsize = base.slots.basicsize;
267 }
268
269 let bases = PyRwLock::new(vec![base.clone()]);
270 let mro = base.mro_map_collect(|x| x.to_owned());
271
272 let new_type = PyRef::new_ref(
273 PyType {
274 base: Some(base),
275 bases,
276 mro: PyRwLock::new(mro),
277 subclasses: PyRwLock::default(),
278 attributes: PyRwLock::new(attrs),
279 slots,
280 heaptype_ext: None,
281 },
282 metaclass,
283 None,
284 );
285
286 let weakref_type = super::PyWeak::static_type();
287 for base in new_type.bases.read().iter() {
288 base.subclasses.write().push(
289 new_type
290 .as_object()
291 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
292 .unwrap(),
293 );
294 }
295
296 Ok(new_type)
297 }
298
299 pub(crate) fn init_slots(&self, ctx: &Context) {
300 #[allow(clippy::mutable_key_type)]
301 let mut slot_name_set = std::collections::HashSet::new();
302
303 for cls in self.mro.read().iter() {
304 for &name in cls.attributes.read().keys() {
305 if name == identifier!(ctx, __new__) {
306 continue;
307 }
308 if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
309 slot_name_set.insert(name);
310 }
311 }
312 }
313 for &name in self.attributes.read().keys() {
314 if name.as_str().starts_with("__") && name.as_str().ends_with("__") {
315 slot_name_set.insert(name);
316 }
317 }
318 for attr_name in slot_name_set {
319 self.update_slot::<true>(attr_name, ctx);
320 }
321 }
322
323 pub fn set_str_attr<V: Into<PyObjectRef>>(
325 &self,
326 attr_name: &str,
327 value: V,
328 ctx: impl AsRef<Context>,
329 ) {
330 let ctx = ctx.as_ref();
331 let attr_name = ctx.intern_str(attr_name);
332 self.set_attr(attr_name, value.into())
333 }
334
335 pub fn set_attr(&self, attr_name: &'static PyStrInterned, value: PyObjectRef) {
336 self.attributes.write().insert(attr_name, value);
337 }
338
339 pub fn get_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
341 flame_guard!(format!("class_get_attr({:?})", attr_name));
342
343 self.get_direct_attr(attr_name)
344 .or_else(|| self.get_super_attr(attr_name))
345 }
346
347 pub fn get_direct_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
348 self.attributes.read().get(attr_name).cloned()
349 }
350
351 pub fn get_super_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
352 self.mro
353 .read()
354 .iter()
355 .find_map(|class| class.attributes.read().get(attr_name).cloned())
356 }
357
358 pub fn has_attr(&self, attr_name: &'static PyStrInterned) -> bool {
360 self.attributes.read().contains_key(attr_name)
361 || self
362 .mro
363 .read()
364 .iter()
365 .any(|c| c.attributes.read().contains_key(attr_name))
366 }
367
368 pub fn get_attributes(&self) -> PyAttributes {
369 let mut attributes = PyAttributes::default();
371
372 for bc in std::iter::once(self)
373 .chain(self.mro.read().iter().map(|cls| -> &PyType { cls }))
374 .rev()
375 {
376 for (name, value) in bc.attributes.read().iter() {
377 attributes.insert(name.to_owned(), value.clone());
378 }
379 }
380
381 attributes
382 }
383
384 pub(crate) fn __new__(zelf: PyRef<PyType>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
386 let (subtype, args): (PyRef<Self>, FuncArgs) = args.bind(vm)?;
387 if !subtype.fast_issubclass(&zelf) {
388 return Err(vm.new_type_error(format!(
389 "{zelf}.__new__({subtype}): {subtype} is not a subtype of {zelf}",
390 zelf = zelf.name(),
391 subtype = subtype.name(),
392 )));
393 }
394 call_slot_new(zelf, subtype, args, vm)
395 }
396
397 fn name_inner<'a, R: 'a>(
398 &'a self,
399 static_f: impl FnOnce(&'static str) -> R,
400 heap_f: impl FnOnce(&'a HeapTypeExt) -> R,
401 ) -> R {
402 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
403 static_f(self.slots.name)
404 } else {
405 heap_f(self.heaptype_ext.as_ref().unwrap())
406 }
407 }
408
409 pub fn slot_name(&self) -> BorrowedValue<str> {
410 self.name_inner(
411 |name| name.into(),
412 |ext| PyRwLockReadGuard::map(ext.name.read(), |name| name.as_str()).into(),
413 )
414 }
415
416 pub fn name(&self) -> BorrowedValue<str> {
417 self.name_inner(
418 |name| name.rsplit_once('.').map_or(name, |(_, name)| name).into(),
419 |ext| PyRwLockReadGuard::map(ext.name.read(), |name| name.as_str()).into(),
420 )
421 }
422}
423
424impl Py<PyType> {
425 pub fn fast_issubclass(&self, cls: &impl Borrow<crate::PyObject>) -> bool {
429 self.as_object().is(cls.borrow()) || self.mro.read().iter().any(|c| c.is(cls.borrow()))
430 }
431
432 pub fn mro_map_collect<F, R>(&self, f: F) -> Vec<R>
433 where
434 F: Fn(&Self) -> R,
435 {
436 std::iter::once(self)
437 .chain(self.mro.read().iter().map(|x| x.deref()))
438 .map(f)
439 .collect()
440 }
441
442 pub fn mro_collect(&self) -> Vec<PyRef<PyType>> {
443 std::iter::once(self)
444 .chain(self.mro.read().iter().map(|x| x.deref()))
445 .map(|x| x.to_owned())
446 .collect()
447 }
448
449 pub(crate) fn mro_find_map<F, R>(&self, f: F) -> Option<R>
450 where
451 F: Fn(&Self) -> Option<R>,
452 {
453 if let Some(r) = f(self) {
456 Some(r)
457 } else {
458 self.mro.read().iter().find_map(|cls| f(cls))
459 }
460 }
461
462 pub fn iter_base_chain(&self) -> impl Iterator<Item = &Py<PyType>> {
463 std::iter::successors(Some(self), |cls| cls.base.as_deref())
464 }
465
466 pub fn extend_methods(&'static self, method_defs: &'static [PyMethodDef], ctx: &Context) {
467 for method_def in method_defs {
468 let method = method_def.to_proper_method(self, ctx);
469 self.set_attr(ctx.intern_str(method_def.name), method);
470 }
471 }
472}
473
474#[pyclass(
475 with(Py, Constructor, GetAttr, SetAttr, Callable, AsNumber, Representable),
476 flags(BASETYPE)
477)]
478impl PyType {
479 #[pygetset(magic)]
480 fn bases(&self, vm: &VirtualMachine) -> PyTupleRef {
481 vm.ctx.new_tuple(
482 self.bases
483 .read()
484 .iter()
485 .map(|x| x.as_object().to_owned())
486 .collect(),
487 )
488 }
489 #[pygetset(setter, name = "__bases__")]
490 fn set_bases(zelf: &Py<Self>, bases: Vec<PyTypeRef>, vm: &VirtualMachine) -> PyResult<()> {
491 if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
494 return Err(vm.new_type_error(format!(
495 "cannot set '__bases__' attribute of immutable type '{}'",
496 zelf.name()
497 )));
498 }
499 if bases.is_empty() {
500 return Err(vm.new_type_error(format!(
501 "can only assign non-empty tuple to %s.__bases__, not {}",
502 zelf.name()
503 )));
504 }
505
506 *zelf.bases.write() = bases;
515 fn update_mro_recursively(cls: &PyType, vm: &VirtualMachine) -> PyResult<()> {
517 *cls.mro.write() =
518 PyType::resolve_mro(&cls.bases.read()).map_err(|msg| vm.new_type_error(msg))?;
519 for subclass in cls.subclasses.write().iter() {
520 let subclass = subclass.upgrade().unwrap();
521 let subclass: &PyType = subclass.payload().unwrap();
522 update_mro_recursively(subclass, vm)?;
523 }
524 Ok(())
525 }
526 update_mro_recursively(zelf, vm)?;
527
528 zelf.init_slots(&vm.ctx);
530
531 let weakref_type = super::PyWeak::static_type();
533 for base in zelf.bases.read().iter() {
534 base.subclasses.write().push(
535 zelf.as_object()
536 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
537 .unwrap(),
538 );
539 }
540
541 Ok(())
542 }
543
544 #[pygetset(magic)]
545 fn base(&self) -> Option<PyTypeRef> {
546 self.base.clone()
547 }
548
549 #[pygetset(magic)]
550 fn flags(&self) -> u64 {
551 self.slots.flags.bits()
552 }
553
554 #[pygetset(magic)]
555 fn basicsize(&self) -> usize {
556 self.slots.basicsize
557 }
558
559 #[pygetset]
560 pub fn __name__(&self, vm: &VirtualMachine) -> PyStrRef {
561 self.name_inner(
562 |name| {
563 vm.ctx
564 .interned_str(name.rsplit_once('.').map_or(name, |(_, name)| name))
565 .unwrap_or_else(|| {
566 panic!(
567 "static type name must be already interned but {} is not",
568 self.slot_name()
569 )
570 })
571 .to_owned()
572 },
573 |ext| ext.name.read().clone(),
574 )
575 }
576
577 #[pygetset(magic)]
578 pub fn qualname(&self, vm: &VirtualMachine) -> PyObjectRef {
579 self.attributes
580 .read()
581 .get(identifier!(vm, __qualname__))
582 .cloned()
583 .and_then(|found| {
585 if found.fast_isinstance(vm.ctx.types.getset_type) {
586 None
587 } else {
588 Some(found)
589 }
590 })
591 .unwrap_or_else(|| vm.ctx.new_str(self.name().deref()).into())
592 }
593
594 #[pygetset(magic, setter)]
595 fn set_qualname(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
596 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
598 return Err(vm.new_type_error(format!(
599 "cannot set '__qualname__' attribute of immutable type '{}'",
600 self.name()
601 )));
602 };
603 let value = value.ok_or_else(|| {
604 vm.new_type_error(format!(
605 "cannot delete '__qualname__' attribute of immutable type '{}'",
606 self.name()
607 ))
608 })?;
609 if !value.class().fast_issubclass(vm.ctx.types.str_type) {
610 return Err(vm.new_type_error(format!(
611 "can only assign string to {}.__qualname__, not '{}'",
612 self.name(),
613 value.class().name()
614 )));
615 }
616 self.attributes
617 .write()
618 .insert(identifier!(vm, __qualname__), value);
619 Ok(())
620 }
621
622 #[pygetset(magic)]
623 fn annotations(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
624 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
625 return Err(vm.new_attribute_error(format!(
626 "type object '{}' has no attribute '__annotations__'",
627 self.name()
628 )));
629 }
630
631 let __annotations__ = identifier!(vm, __annotations__);
632 let annotations = self.attributes.read().get(__annotations__).cloned();
633
634 let annotations = if let Some(annotations) = annotations {
635 annotations
636 } else {
637 let annotations: PyObjectRef = vm.ctx.new_dict().into();
638 let removed = self
639 .attributes
640 .write()
641 .insert(__annotations__, annotations.clone());
642 debug_assert!(removed.is_none());
643 annotations
644 };
645 Ok(annotations)
646 }
647
648 #[pygetset(magic, setter)]
649 fn set_annotations(&self, value: Option<PyObjectRef>, vm: &VirtualMachine) -> PyResult<()> {
650 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
651 return Err(vm.new_type_error(format!(
652 "cannot set '__annotations__' attribute of immutable type '{}'",
653 self.name()
654 )));
655 }
656
657 let __annotations__ = identifier!(vm, __annotations__);
658 if let Some(value) = value {
659 self.attributes.write().insert(__annotations__, value);
660 } else {
661 self.attributes
662 .read()
663 .get(__annotations__)
664 .cloned()
665 .ok_or_else(|| {
666 vm.new_attribute_error(format!(
667 "'{}' object has no attribute '__annotations__'",
668 self.name()
669 ))
670 })?;
671 }
672
673 Ok(())
674 }
675
676 #[pygetset(magic)]
677 pub fn module(&self, vm: &VirtualMachine) -> PyObjectRef {
678 self.attributes
679 .read()
680 .get(identifier!(vm, __module__))
681 .cloned()
682 .and_then(|found| {
684 if found.fast_isinstance(vm.ctx.types.getset_type) {
685 None
686 } else {
687 Some(found)
688 }
689 })
690 .unwrap_or_else(|| vm.ctx.new_str(ascii!("builtins")).into())
691 }
692
693 #[pygetset(magic, setter)]
694 fn set_module(&self, value: PyObjectRef, vm: &VirtualMachine) {
695 self.attributes
696 .write()
697 .insert(identifier!(vm, __module__), value);
698 }
699
700 #[pyclassmethod(magic)]
701 fn prepare(
702 _cls: PyTypeRef,
703 _name: OptionalArg<PyObjectRef>,
704 _bases: OptionalArg<PyObjectRef>,
705 _kwargs: KwArgs,
706 vm: &VirtualMachine,
707 ) -> PyDictRef {
708 vm.ctx.new_dict()
709 }
710
711 #[pymethod(magic)]
712 fn subclasses(&self) -> PyList {
713 let mut subclasses = self.subclasses.write();
714 subclasses.retain(|x| x.upgrade().is_some());
715 PyList::from(
716 subclasses
717 .iter()
718 .map(|x| x.upgrade().unwrap())
719 .collect::<Vec<_>>(),
720 )
721 }
722
723 #[pymethod(magic)]
724 pub fn ror(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
725 or_(other, zelf, vm)
726 }
727
728 #[pymethod(magic)]
729 pub fn or(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
730 or_(zelf, other, vm)
731 }
732
733 #[pygetset(magic)]
734 fn dict(zelf: PyRef<Self>) -> PyMappingProxy {
735 PyMappingProxy::from(zelf)
736 }
737
738 #[pygetset(magic, setter)]
739 fn set_dict(&self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
740 Err(vm.new_not_implemented_error(
741 "Setting __dict__ attribute on a type isn't yet implemented".to_owned(),
742 ))
743 }
744
745 fn check_set_special_type_attr(
746 &self,
747 _value: &PyObject,
748 name: &PyStrInterned,
749 vm: &VirtualMachine,
750 ) -> PyResult<()> {
751 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
752 return Err(vm.new_type_error(format!(
753 "cannot set '{}' attribute of immutable type '{}'",
754 name,
755 self.slot_name()
756 )));
757 }
758 Ok(())
759 }
760
761 #[pygetset(magic, setter)]
762 fn set_name(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
763 self.check_set_special_type_attr(&value, identifier!(vm, __name__), vm)?;
764 let name = value.downcast::<PyStr>().map_err(|value| {
765 vm.new_type_error(format!(
766 "can only assign string to {}.__name__, not '{}'",
767 self.slot_name(),
768 value.class().slot_name(),
769 ))
770 })?;
771 if name.as_str().as_bytes().contains(&0) {
772 return Err(vm.new_value_error("type name must not contain null characters".to_owned()));
773 }
774
775 *self.heaptype_ext.as_ref().unwrap().name.write() = name;
776
777 Ok(())
778 }
779
780 #[pygetset(magic)]
781 fn text_signature(&self) -> Option<String> {
782 self.slots
783 .doc
784 .and_then(|doc| get_text_signature_from_internal_doc(&self.name(), doc))
785 .map(|signature| signature.to_string())
786 }
787}
788
789impl Constructor for PyType {
790 type Args = FuncArgs;
791
792 fn py_new(metatype: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
793 vm_trace!("type.__new__ {:?}", args);
794
795 let is_type_type = metatype.is(vm.ctx.types.type_type);
796 if is_type_type && args.args.len() == 1 && args.kwargs.is_empty() {
797 return Ok(args.args[0].class().to_owned().into());
798 }
799
800 if args.args.len() != 3 {
801 return Err(vm.new_type_error(if is_type_type {
802 "type() takes 1 or 3 arguments".to_owned()
803 } else {
804 format!(
805 "type.__new__() takes exactly 3 arguments ({} given)",
806 args.args.len()
807 )
808 }));
809 }
810
811 let (name, bases, dict, kwargs): (PyStrRef, PyTupleRef, PyDictRef, KwArgs) =
812 args.clone().bind(vm)?;
813
814 if name.as_str().as_bytes().contains(&0) {
815 return Err(vm.new_value_error("type name must not contain null characters".to_owned()));
816 }
817
818 let (metatype, base, bases) = if bases.is_empty() {
819 let base = vm.ctx.types.object_type.to_owned();
820 (metatype, base.clone(), vec![base])
821 } else {
822 let bases = bases
823 .iter()
824 .map(|obj| {
825 obj.clone().downcast::<PyType>().or_else(|obj| {
826 if vm
827 .get_attribute_opt(obj, identifier!(vm, __mro_entries__))?
828 .is_some()
829 {
830 Err(vm.new_type_error(
831 "type() doesn't support MRO entry resolution; \
832 use types.new_class()"
833 .to_owned(),
834 ))
835 } else {
836 Err(vm.new_type_error("bases must be types".to_owned()))
837 }
838 })
839 })
840 .collect::<PyResult<Vec<_>>>()?;
841
842 let winner = calculate_meta_class(metatype.clone(), &bases, vm)?;
844 let metatype = if !winner.is(&metatype) {
845 if let Some(ref slot_new) = winner.slots.new.load() {
846 return slot_new(winner, args, vm);
848 }
849 winner
850 } else {
851 metatype
852 };
853
854 let base = best_base(&bases, vm)?;
855
856 (metatype, base.to_owned(), bases)
857 };
858
859 let mut attributes = dict.to_attributes(vm);
860
861 if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__)) {
862 if f.class().is(vm.ctx.types.function_type) {
863 *f = PyClassMethod::from(f.clone()).into_pyobject(vm);
864 }
865 }
866
867 if let Some(f) = attributes.get_mut(identifier!(vm, __class_getitem__)) {
868 if f.class().is(vm.ctx.types.function_type) {
869 *f = PyClassMethod::from(f.clone()).into_pyobject(vm);
870 }
871 }
872
873 if let Some(current_frame) = vm.current_frame() {
874 let entry = attributes.entry(identifier!(vm, __module__));
875 if matches!(entry, Entry::Vacant(_)) {
876 let module_name = vm.unwrap_or_none(
877 current_frame
878 .globals
879 .get_item_opt(identifier!(vm, __name__), vm)?,
880 );
881 entry.or_insert(module_name);
882 }
883 }
884
885 attributes
886 .entry(identifier!(vm, __qualname__))
887 .or_insert_with(|| vm.ctx.new_str(name.as_str()).into());
888
889 if attributes.get(identifier!(vm, __eq__)).is_some()
890 && attributes.get(identifier!(vm, __hash__)).is_none()
891 {
892 attributes.insert(identifier!(vm, __hash__), vm.ctx.none.clone().into());
895 }
896
897 let __dict__ = identifier!(vm, __dict__);
901 attributes.entry(__dict__).or_insert_with(|| {
902 vm.ctx
903 .new_getset(
904 "__dict__",
905 vm.ctx.types.object_type,
906 subtype_get_dict,
907 subtype_set_dict,
908 )
909 .into()
910 });
911
912 let heaptype_slots: Option<PyTupleTyped<PyStrRef>> =
916 if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
917 Some(if x.to_owned().class().is(vm.ctx.types.str_type) {
918 PyTupleTyped::<PyStrRef>::try_from_object(
919 vm,
920 vec![x.to_owned()].into_pytuple(vm).into(),
921 )?
922 } else {
923 let iter = x.to_owned().get_iter(vm)?;
924 let elements = {
925 let mut elements = Vec::new();
926 while let PyIterReturn::Return(element) = iter.next(vm)? {
927 elements.push(element);
928 }
929 elements
930 };
931 PyTupleTyped::<PyStrRef>::try_from_object(vm, elements.into_pytuple(vm).into())?
932 })
933 } else {
934 None
935 };
936
937 let base_member_count = bases
939 .iter()
940 .map(|base| base.slots.member_count)
941 .max()
942 .unwrap();
943 let heaptype_member_count = heaptype_slots.as_ref().map(|x| x.len()).unwrap_or(0);
944 let member_count: usize = base_member_count + heaptype_member_count;
945
946 let flags = PyTypeFlags::heap_type_flags() | PyTypeFlags::HAS_DICT;
947 let (slots, heaptype_ext) = {
948 let slots = PyTypeSlots {
949 member_count,
950 flags,
951 ..PyTypeSlots::heap_default()
952 };
953 let heaptype_ext = HeapTypeExt {
954 name: PyRwLock::new(name),
955 slots: heaptype_slots.to_owned(),
956 sequence_methods: PySequenceMethods::default(),
957 mapping_methods: PyMappingMethods::default(),
958 };
959 (slots, heaptype_ext)
960 };
961
962 let typ = Self::new_heap_inner(
963 base,
964 bases,
965 attributes,
966 slots,
967 heaptype_ext,
968 metatype,
969 &vm.ctx,
970 )
971 .map_err(|e| vm.new_type_error(e))?;
972
973 if let Some(ref slots) = heaptype_slots {
974 let mut offset = base_member_count;
975 for member in slots.as_slice() {
976 let member_def = PyMemberDef {
977 name: member.to_string(),
978 kind: MemberKind::ObjectEx,
979 getter: MemberGetter::Offset(offset),
980 setter: MemberSetter::Offset(offset),
981 doc: None,
982 };
983 let member_descriptor: PyRef<PyMemberDescriptor> =
984 vm.ctx.new_pyref(PyMemberDescriptor {
985 common: PyDescriptorOwned {
986 typ: typ.clone(),
987 name: vm.ctx.intern_str(member.as_str()),
988 qualname: PyRwLock::new(None),
989 },
990 member: member_def,
991 });
992
993 let attr_name = vm.ctx.intern_str(member.to_string());
994 if !typ.has_attr(attr_name) {
995 typ.set_attr(attr_name, member_descriptor.into());
996 }
997
998 offset += 1;
999 }
1000 }
1001
1002 if let Some(cell) = typ.attributes.write().get(identifier!(vm, __classcell__)) {
1003 let cell = PyCellRef::try_from_object(vm, cell.clone()).map_err(|_| {
1004 vm.new_type_error(format!(
1005 "__classcell__ must be a nonlocal cell, not {}",
1006 cell.class().name()
1007 ))
1008 })?;
1009 cell.set(Some(typ.clone().into()));
1010 };
1011
1012 let attributes = typ
1014 .attributes
1015 .read()
1016 .iter()
1017 .filter_map(|(name, obj)| {
1018 vm.get_method(obj.clone(), identifier!(vm, __set_name__))
1019 .map(|res| res.map(|meth| (obj.clone(), name.to_owned(), meth)))
1020 })
1021 .collect::<PyResult<Vec<_>>>()?;
1022 for (obj, name, set_name) in attributes {
1023 set_name.call((typ.clone(), name), vm).map_err(|e| {
1024 let err = vm.new_runtime_error(format!(
1025 "Error calling __set_name__ on '{}' instance {} in '{}'",
1026 obj.class().name(),
1027 name,
1028 typ.name()
1029 ));
1030 err.set_cause(Some(e));
1031 err
1032 })?;
1033 }
1034
1035 if let Some(init_subclass) = typ.get_super_attr(identifier!(vm, __init_subclass__)) {
1036 let init_subclass = vm
1037 .call_get_descriptor_specific(&init_subclass, None, Some(typ.clone().into()))
1038 .unwrap_or(Ok(init_subclass))?;
1039 init_subclass.call(kwargs, vm)?;
1040 };
1041
1042 Ok(typ.into())
1043 }
1044}
1045
1046const SIGNATURE_END_MARKER: &str = ")\n--\n\n";
1047fn get_signature(doc: &str) -> Option<&str> {
1048 doc.find(SIGNATURE_END_MARKER).map(|index| &doc[..=index])
1049}
1050
1051fn find_signature<'a>(name: &str, doc: &'a str) -> Option<&'a str> {
1052 let name = name.rsplit('.').next().unwrap();
1053 let doc = doc.strip_prefix(name)?;
1054 doc.starts_with('(').then_some(doc)
1055}
1056
1057pub(crate) fn get_text_signature_from_internal_doc<'a>(
1058 name: &str,
1059 internal_doc: &'a str,
1060) -> Option<&'a str> {
1061 find_signature(name, internal_doc).and_then(get_signature)
1062}
1063
1064impl GetAttr for PyType {
1065 fn getattro(zelf: &Py<Self>, name_str: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
1066 #[cold]
1067 fn attribute_error(
1068 zelf: &Py<PyType>,
1069 name: &str,
1070 vm: &VirtualMachine,
1071 ) -> PyBaseExceptionRef {
1072 vm.new_attribute_error(format!(
1073 "type object '{}' has no attribute '{}'",
1074 zelf.slot_name(),
1075 name,
1076 ))
1077 }
1078
1079 let Some(name) = vm.ctx.interned_str(name_str) else {
1080 return Err(attribute_error(zelf, name_str.as_str(), vm));
1081 };
1082 vm_trace!("type.__getattribute__({:?}, {:?})", zelf, name);
1083 let mcl = zelf.class();
1084 let mcl_attr = mcl.get_attr(name);
1085
1086 if let Some(ref attr) = mcl_attr {
1087 let attr_class = attr.class();
1088 let has_descr_set = attr_class
1089 .mro_find_map(|cls| cls.slots.descr_set.load())
1090 .is_some();
1091 if has_descr_set {
1092 let descr_get = attr_class.mro_find_map(|cls| cls.slots.descr_get.load());
1093 if let Some(descr_get) = descr_get {
1094 let mcl = mcl.to_owned().into();
1095 return descr_get(attr.clone(), Some(zelf.to_owned().into()), Some(mcl), vm);
1096 }
1097 }
1098 }
1099
1100 let zelf_attr = zelf.get_attr(name);
1101
1102 if let Some(attr) = zelf_attr {
1103 let descr_get = attr.class().mro_find_map(|cls| cls.slots.descr_get.load());
1104 if let Some(descr_get) = descr_get {
1105 descr_get(attr, None, Some(zelf.to_owned().into()), vm)
1106 } else {
1107 Ok(attr)
1108 }
1109 } else if let Some(attr) = mcl_attr {
1110 vm.call_if_get_descriptor(&attr, zelf.to_owned().into())
1111 } else {
1112 Err(attribute_error(zelf, name_str.as_str(), vm))
1113 }
1114 }
1115}
1116
1117#[pyclass]
1118impl Py<PyType> {
1119 #[pygetset(name = "__mro__")]
1120 fn get_mro(&self) -> PyTuple {
1121 let elements: Vec<PyObjectRef> = self.mro_map_collect(|x| x.as_object().to_owned());
1122 PyTuple::new_unchecked(elements.into_boxed_slice())
1123 }
1124
1125 #[pymethod(magic)]
1126 fn dir(&self) -> PyList {
1127 let attributes: Vec<PyObjectRef> = self
1128 .get_attributes()
1129 .into_iter()
1130 .map(|(k, _)| k.to_object())
1131 .collect();
1132 PyList::from(attributes)
1133 }
1134
1135 #[pymethod(magic)]
1136 fn instancecheck(&self, obj: PyObjectRef) -> bool {
1137 obj.fast_isinstance(self)
1138 }
1139
1140 #[pymethod(magic)]
1141 fn subclasscheck(&self, subclass: PyTypeRef) -> bool {
1142 subclass.fast_issubclass(self)
1143 }
1144
1145 #[pyclassmethod(magic)]
1146 fn subclasshook(_args: FuncArgs, vm: &VirtualMachine) -> PyObjectRef {
1147 vm.ctx.not_implemented()
1148 }
1149
1150 #[pymethod]
1151 fn mro(&self) -> Vec<PyObjectRef> {
1152 self.mro_map_collect(|cls| cls.to_owned().into())
1153 }
1154}
1155
1156impl SetAttr for PyType {
1157 fn setattro(
1158 zelf: &Py<Self>,
1159 attr_name: &Py<PyStr>,
1160 value: PySetterValue,
1161 vm: &VirtualMachine,
1162 ) -> PyResult<()> {
1163 let attr_name = vm.ctx.intern_str(attr_name.as_str());
1165 if let Some(attr) = zelf.get_class_attr(attr_name) {
1166 let descr_set = attr.class().mro_find_map(|cls| cls.slots.descr_set.load());
1167 if let Some(descriptor) = descr_set {
1168 return descriptor(&attr, zelf.to_owned().into(), value, vm);
1169 }
1170 }
1171 let assign = value.is_assign();
1172
1173 if let PySetterValue::Assign(value) = value {
1174 zelf.attributes.write().insert(attr_name, value);
1175 } else {
1176 let prev_value = zelf.attributes.write().shift_remove(attr_name); if prev_value.is_none() {
1178 return Err(vm.new_attribute_error(format!(
1179 "type object '{}' has no attribute '{}'",
1180 zelf.name(),
1181 attr_name.as_str(),
1182 )));
1183 }
1184 }
1185 if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
1186 if assign {
1187 zelf.update_slot::<true>(attr_name, &vm.ctx);
1188 } else {
1189 zelf.update_slot::<false>(attr_name, &vm.ctx);
1190 }
1191 }
1192 Ok(())
1193 }
1194}
1195
1196impl Callable for PyType {
1197 type Args = FuncArgs;
1198 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1199 vm_trace!("type_call: {:?}", zelf);
1200 let obj = call_slot_new(zelf.to_owned(), zelf.to_owned(), args.clone(), vm)?;
1201
1202 if (zelf.is(vm.ctx.types.type_type) && args.kwargs.is_empty()) || !obj.fast_isinstance(zelf)
1203 {
1204 return Ok(obj);
1205 }
1206
1207 let init = obj.class().mro_find_map(|cls| cls.slots.init.load());
1208 if let Some(init_method) = init {
1209 init_method(obj.clone(), args, vm)?;
1210 }
1211 Ok(obj)
1212 }
1213}
1214
1215impl AsNumber for PyType {
1216 fn as_number() -> &'static PyNumberMethods {
1217 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
1218 or: Some(|a, b, vm| or_(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
1219 ..PyNumberMethods::NOT_IMPLEMENTED
1220 };
1221 &AS_NUMBER
1222 }
1223}
1224
1225impl Representable for PyType {
1226 #[inline]
1227 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
1228 let module = zelf.module(vm);
1229 let module = module.downcast_ref::<PyStr>().map(|m| m.as_str());
1230
1231 let repr = match module {
1232 Some(module) if module != "builtins" => {
1233 let name = zelf.name();
1234 format!(
1235 "<class '{}.{}'>",
1236 module,
1237 zelf.qualname(vm)
1238 .downcast_ref::<PyStr>()
1239 .map(|n| n.as_str())
1240 .unwrap_or_else(|| &name)
1241 )
1242 }
1243 _ => format!("<class '{}'>", zelf.slot_name()),
1244 };
1245 Ok(repr)
1246 }
1247}
1248
1249fn find_base_dict_descr(cls: &Py<PyType>, vm: &VirtualMachine) -> Option<PyObjectRef> {
1250 cls.iter_base_chain().skip(1).find_map(|cls| {
1251 if cls.is(vm.ctx.types.type_type) {
1254 cls.get_attr(identifier!(vm, __dict__))
1255 } else {
1256 None
1257 }
1258 })
1259}
1260
1261fn subtype_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
1262 let ret = match find_base_dict_descr(obj.class(), vm) {
1264 Some(descr) => vm.call_get_descriptor(&descr, obj).unwrap_or_else(|| {
1265 Err(vm.new_type_error(format!(
1266 "this __dict__ descriptor does not support '{}' objects",
1267 descr.class()
1268 )))
1269 })?,
1270 None => object::object_get_dict(obj, vm)?.into(),
1271 };
1272 Ok(ret)
1273}
1274
1275fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1276 let cls = obj.class();
1277 match find_base_dict_descr(cls, vm) {
1278 Some(descr) => {
1279 let descr_set = descr
1280 .class()
1281 .mro_find_map(|cls| cls.slots.descr_set.load())
1282 .ok_or_else(|| {
1283 vm.new_type_error(format!(
1284 "this __dict__ descriptor does not support '{}' objects",
1285 cls.name()
1286 ))
1287 })?;
1288 descr_set(&descr, obj, PySetterValue::Assign(value), vm)
1289 }
1290 None => {
1291 object::object_set_dict(obj, value.try_into_value(vm)?, vm)?;
1292 Ok(())
1293 }
1294 }
1295}
1296
1297pub(crate) fn init(ctx: &Context) {
1302 PyType::extend_class(ctx, ctx.types.type_type);
1303}
1304
1305pub(crate) fn call_slot_new(
1306 typ: PyTypeRef,
1307 subtype: PyTypeRef,
1308 args: FuncArgs,
1309 vm: &VirtualMachine,
1310) -> PyResult {
1311 let slot_new = typ
1312 .deref()
1313 .mro_find_map(|cls| cls.slots.new.load())
1314 .expect("Should be able to find a new slot somewhere in the mro");
1315 slot_new(subtype, args, vm)
1316}
1317
1318pub(super) fn or_(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
1319 if !union_::is_unionable(zelf.clone(), vm) || !union_::is_unionable(other.clone(), vm) {
1320 return vm.ctx.not_implemented();
1321 }
1322
1323 let tuple = PyTuple::new_ref(vec![zelf, other], &vm.ctx);
1324 union_::make_union(&tuple, vm)
1325}
1326
1327fn take_next_base(bases: &mut [Vec<PyTypeRef>]) -> Option<PyTypeRef> {
1328 for base in bases.iter() {
1329 let head = base[0].clone();
1330 if !bases.iter().any(|x| x[1..].iter().any(|x| x.is(&head))) {
1331 for item in bases.iter_mut() {
1333 if item[0].is(&head) {
1334 item.remove(0);
1335 }
1336 }
1337
1338 return Some(head);
1339 }
1340 }
1341
1342 None
1343}
1344
1345fn linearise_mro(mut bases: Vec<Vec<PyTypeRef>>) -> Result<Vec<PyTypeRef>, String> {
1346 vm_trace!("Linearise MRO: {:?}", bases);
1347 for (i, base_mro) in bases.iter().enumerate() {
1353 let base = &base_mro[0]; for later_mro in &bases[i + 1..] {
1355 if later_mro[1..].iter().any(|cls| cls.is(base)) {
1358 return Err(
1359 "Unable to find mro order which keeps local precedence ordering".to_owned(),
1360 );
1361 }
1362 }
1363 }
1364
1365 let mut result = vec![];
1366 while !bases.is_empty() {
1367 let head = take_next_base(&mut bases).ok_or_else(|| {
1368 format!(
1371 "Cannot create a consistent method resolution order (MRO) for bases {}",
1372 bases.iter().map(|x| x.first().unwrap()).format(", ")
1373 )
1374 })?;
1375
1376 result.push(head);
1377
1378 bases.retain(|x| !x.is_empty());
1379 }
1380 Ok(result)
1381}
1382
1383fn calculate_meta_class(
1384 metatype: PyTypeRef,
1385 bases: &[PyTypeRef],
1386 vm: &VirtualMachine,
1387) -> PyResult<PyTypeRef> {
1388 let mut winner = metatype;
1390 for base in bases {
1391 let base_type = base.class();
1392 if winner.fast_issubclass(base_type) {
1393 continue;
1394 } else if base_type.fast_issubclass(&winner) {
1395 winner = base_type.to_owned();
1396 continue;
1397 }
1398
1399 return Err(vm.new_type_error(
1400 "metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass \
1401 of the metaclasses of all its bases"
1402 .to_owned(),
1403 ));
1404 }
1405 Ok(winner)
1406}
1407
1408fn solid_base<'a>(typ: &'a Py<PyType>, vm: &VirtualMachine) -> &'a Py<PyType> {
1409 let base = if let Some(base) = &typ.base {
1410 solid_base(base, vm)
1411 } else {
1412 vm.ctx.types.object_type
1413 };
1414
1415 if typ.basicsize() != base.basicsize() {
1417 typ
1418 } else {
1419 base
1420 }
1421}
1422
1423fn best_base<'a>(bases: &'a [PyTypeRef], vm: &VirtualMachine) -> PyResult<&'a Py<PyType>> {
1424 let mut base: Option<&Py<PyType>> = None;
1425 let mut winner: Option<&Py<PyType>> = None;
1426
1427 for base_i in bases {
1428 if !base_i.slots.flags.has_feature(PyTypeFlags::BASETYPE) {
1434 return Err(vm.new_type_error(format!(
1435 "type '{}' is not an acceptable base type",
1436 base_i.name()
1437 )));
1438 }
1439
1440 let candidate = solid_base(base_i, vm);
1441 if winner.is_none() {
1442 winner = Some(candidate);
1443 base = Some(base_i.deref());
1444 } else if winner.unwrap().fast_issubclass(candidate) {
1445 } else if candidate.fast_issubclass(winner.unwrap()) {
1447 winner = Some(candidate);
1448 base = Some(base_i.deref());
1449 } else {
1450 return Err(
1451 vm.new_type_error("multiple bases have instance layout conflict".to_string())
1452 );
1453 }
1454 }
1455
1456 debug_assert!(base.is_some());
1457 Ok(base.unwrap())
1458}
1459
1460#[cfg(test)]
1461mod tests {
1462 use super::*;
1463
1464 fn map_ids(obj: Result<Vec<PyTypeRef>, String>) -> Result<Vec<usize>, String> {
1465 Ok(obj?.into_iter().map(|x| x.get_id()).collect())
1466 }
1467
1468 #[test]
1469 fn test_linearise() {
1470 let context = Context::genesis();
1471 let object = context.types.object_type.to_owned();
1472 let type_type = context.types.type_type.to_owned();
1473
1474 let a = PyType::new_heap(
1475 "A",
1476 vec![object.clone()],
1477 PyAttributes::default(),
1478 Default::default(),
1479 type_type.clone(),
1480 context,
1481 )
1482 .unwrap();
1483 let b = PyType::new_heap(
1484 "B",
1485 vec![object.clone()],
1486 PyAttributes::default(),
1487 Default::default(),
1488 type_type,
1489 context,
1490 )
1491 .unwrap();
1492
1493 assert_eq!(
1494 map_ids(linearise_mro(vec![
1495 vec![object.clone()],
1496 vec![object.clone()]
1497 ])),
1498 map_ids(Ok(vec![object.clone()]))
1499 );
1500 assert_eq!(
1501 map_ids(linearise_mro(vec![
1502 vec![a.clone(), object.clone()],
1503 vec![b.clone(), object.clone()],
1504 ])),
1505 map_ids(Ok(vec![a, b, object]))
1506 );
1507 }
1508}