rustpython_vm/builtins/
type.rs

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
116/// For attributes we do not use a dict, but an IndexMap, which is an Hash Table
117/// that maintains order and is compatible with the standard HashMap  This is probably
118/// faster and only supports strings as keys.
119pub 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        // TODO: ensure clean slot name
169        // assert_eq!(slots.name.borrow(), "");
170
171        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        // Check for duplicates in bases.
185        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    // This is used for class initialisation where the vm is not yet available.
324    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    /// This is the internal get_attr implementation for fast lookup on a class.
340    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    // This is the internal has_attr implementation for fast lookup on a class.
359    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        // Gather all members here:
370        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    // bound method for every type
385    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    /// Determines if `subclass` is actually a subclass of `cls`, this doesn't call __subclasscheck__,
426    /// so only use this if `cls` is known to have not overridden the base __subclasscheck__ magic
427    /// method.
428    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        // the hot path will be primitive types which usually hit the result from itself.
454        // try std::intrinsics::likely once it is stablized
455        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        // TODO: Assigning to __bases__ is only used in typing.NamedTupleMeta.__new__
492        // Rather than correctly reinitializing the class, we are skipping a few steps for now
493        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        // TODO: check for mro cycles
507
508        // TODO: Remove this class from all subclass lists
509        // for base in self.bases.read().iter() {
510        //     let subclasses = base.subclasses.write();
511        //     // TODO: how to uniquely identify the subclasses to remove?
512        // }
513
514        *zelf.bases.write() = bases;
515        // Recursively update the mros of this class and all subclasses
516        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        // TODO: do any old slots need to be cleaned up first?
529        zelf.init_slots(&vm.ctx);
530
531        // Register this type as a subclass of its new bases
532        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            // We need to exclude this method from going into recursion:
584            .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        // TODO: we should replace heaptype flag check to immutable flag check
597        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            // We need to exclude this method from going into recursion:
683            .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            // Search the bases for the proper metatype to deal with this:
843            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                    // Pass it to the winner
847                    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            // if __eq__ exists but __hash__ doesn't, overwrite it with None so it doesn't inherit the default hash
893            // https://docs.python.org/3/reference/datamodel.html#object.__hash__
894            attributes.insert(identifier!(vm, __hash__), vm.ctx.none.clone().into());
895        }
896
897        // All *classes* should have a dict. Exceptions are *instances* of
898        // classes that define __slots__ and instances of built-in classes
899        // (with exceptions, e.g function)
900        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        // TODO: Flags is currently initialized with HAS_DICT. Should be
913        // updated when __slots__ are supported (toggling the flag off if
914        // a class has __slots__ defined).
915        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        // FIXME: this is a temporary fix. multi bases with multiple slots will break object
938        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        // avoid deadlock
1013        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        // TODO: pass PyRefExact instead of &str
1164        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); // TODO: swap_remove applicable?
1177            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        // TODO: should actually be some translation of:
1252        // cls.slot_dictoffset != 0 && !cls.flags.contains(HEAPTYPE)
1253        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    // TODO: obj.class().as_pyref() need to be supported
1263    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
1297/*
1298 * The magical type type
1299 */
1300
1301pub(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            // Remove from other heads.
1332            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    // Python requires that the class direct bases are kept in the same order.
1348    // This is called local precedence ordering.
1349    // This means we must verify that for classes A(), B(A) we must reject C(A, B) even though this
1350    // algorithm will allow the mro ordering of [C, B, A, object].
1351    // To verify this, we make sure non of the direct bases are in the mro of bases after them.
1352    for (i, base_mro) in bases.iter().enumerate() {
1353        let base = &base_mro[0]; // MROs cannot be empty.
1354        for later_mro in &bases[i + 1..] {
1355            // We start at index 1 to skip direct bases.
1356            // This will not catch duplicate bases, but such a thing is already tested for.
1357            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            // Take the head class of each class here. Now that we have reached the problematic bases.
1369            // Because this failed, we assume the lists cannot be empty.
1370            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    // = _PyType_CalculateMetaclass
1389    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    // TODO: requires itemsize comparison too
1416    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.fast_issubclass(vm.ctx.types.type_type) {
1429        //     println!("base_i type : {}", base_i.name());
1430        //     return Err(vm.new_type_error("best must be types".into()));
1431        // }
1432
1433        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            // Do nothing
1446        } 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}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.