1use super::{
2 PyClassMethod, PyDict, PyDictRef, PyList, PyStaticMethod, PyStr, PyStrInterned, PyStrRef,
3 PyTupleRef, PyUtf8StrRef, PyWeak, mappingproxy::PyMappingProxy, object, union_,
4};
5use crate::{
6 AsObject, Context, Py, PyAtomicRef, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
7 TryFromObject, VirtualMachine,
8 builtins::{
9 PyBaseExceptionRef,
10 descriptor::{
11 MemberAccess, MemberKind, PyDescriptorOwned, PyMemberDef, PyMemberDescriptor,
12 PyMemberFlags,
13 },
14 function::{PyCellRef, PyFunction},
15 tuple::{IntoPyTuple, PyTuple},
16 },
17 class::{PyClassDef, PyClassImpl, StaticType},
18 common::{
19 borrow::BorrowedValue,
20 lock::{PyRwLock, PyRwLockReadGuard},
21 },
22 function::{
23 ArgumentError, FromArgs, FuncArgs, ItemDoc, KwArgs, Param, PyMethodDef, PySetterValue,
24 db_doc,
25 },
26 object::{Traverse, TraverseFn},
27 protocol::{PyIterReturn, PyNumberMethods},
28 types::{
29 AsNumber, Callable, Constructor, GetAttr, Initializer, NewFunc, PyTypeFlags, PyTypeSlots,
30 Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot, fn_addr,
31 new_wrapper,
32 },
33};
34use core::{
35 any::Any,
36 borrow::Borrow,
37 cell::Cell,
38 ops::Deref,
39 pin::Pin,
40 sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering},
41};
42use indexmap::{IndexMap, map::Entry};
43use itertools::Itertools;
44use num_traits::ToPrimitive;
45use rustpython_common::wtf8::Wtf8;
46use std::collections::HashSet;
47
48pub(crate) type PyTypeTupleRef = PyRef<PyTuple<PyTypeRef>>;
49
50#[pyclass(module = false, name = "type", traverse = "manual")]
51pub struct PyType {
52 #[pymember(name = "__base__", doc = false)]
54 pub base: PyAtomicRef<Option<Self>>,
55 pub bases: PyRwLock<PyTypeTupleRef>,
56 pub mro: PyRwLock<Vec<PyTypeRef>>,
57 pub subclasses: PyRwLock<Vec<PyRef<PyWeak>>>,
58 pub attributes: TypeNamespace,
59 #[pymember(name = "__itemsize__", path = "itemsize")]
60 #[pymember(name = "__basicsize__", path = "basicsize")]
61 #[pymember(name = "__flags__", path = "flags")]
62 pub slots: PyTypeSlots,
63 pub heaptype_ext: Option<Pin<Box<HeapTypeExt>>>,
64 pub tp_version_tag: AtomicU32,
66}
67
68static NEXT_TYPE_VERSION: AtomicU32 = AtomicU32::new(1);
73
74const MANAGED_DICT_FLAGS: PyTypeFlags =
75 PyTypeFlags::from_slice(&[PyTypeFlags::HAS_DICT, PyTypeFlags::MANAGED_DICT]);
76
77const MANAGED_DICT_INLINE_FLAGS: PyTypeFlags = PyTypeFlags::from_slice(&[
78 PyTypeFlags::HAS_DICT,
79 PyTypeFlags::MANAGED_DICT,
80 PyTypeFlags::INLINE_VALUES,
81]);
82
83const WEAKREF_FLAGS: PyTypeFlags =
84 PyTypeFlags::from_slice(&[PyTypeFlags::HAS_WEAKREF, PyTypeFlags::MANAGED_WEAKREF]);
85
86const TYPE_CACHE_SIZE_EXP: u32 = 12;
95const TYPE_CACHE_SIZE: usize = 1 << TYPE_CACHE_SIZE_EXP;
96const TYPE_CACHE_MASK: usize = TYPE_CACHE_SIZE - 1;
97
98struct TypeCacheEntry {
99 sequence: AtomicU32,
101 version: AtomicU32,
103 name: AtomicPtr<PyStrInterned>,
105 value: AtomicPtr<PyObject>,
113}
114
115unsafe impl Send for TypeCacheEntry {}
120unsafe impl Sync for TypeCacheEntry {}
121
122impl TypeCacheEntry {
123 fn new() -> Self {
124 Self {
125 sequence: AtomicU32::new(0),
126 version: AtomicU32::new(0),
127 name: AtomicPtr::new(core::ptr::null_mut()),
128 value: AtomicPtr::new(core::ptr::null_mut()),
129 }
130 }
131
132 #[inline]
133 fn begin_write(&self) {
134 let mut seq = self.sequence.load(Ordering::Acquire);
135 loop {
136 while (seq & 1) != 0 {
137 core::hint::spin_loop();
138 seq = self.sequence.load(Ordering::Acquire);
139 }
140 match self.sequence.compare_exchange_weak(
141 seq,
142 seq.wrapping_add(1),
143 Ordering::AcqRel,
144 Ordering::Acquire,
145 ) {
146 Ok(_) => {
147 core::sync::atomic::fence(Ordering::Release);
148 break;
149 }
150 Err(observed) => {
151 core::hint::spin_loop();
152 seq = observed;
153 }
154 }
155 }
156 }
157
158 #[inline]
159 fn end_write(&self) {
160 self.sequence.fetch_add(1, Ordering::Release);
161 }
162
163 #[inline]
164 fn begin_read(&self) -> u32 {
165 let mut sequence = self.sequence.load(Ordering::Acquire);
166 while (sequence & 1) != 0 {
167 core::hint::spin_loop();
168 sequence = self.sequence.load(Ordering::Acquire);
169 }
170 sequence
171 }
172
173 #[inline]
174 fn end_read(&self, previous: u32) -> bool {
175 core::sync::atomic::fence(Ordering::Acquire);
176 self.sequence.load(Ordering::Relaxed) == previous
177 }
178
179 fn clear_value(&self) {
183 self.value.store(core::ptr::null_mut(), Ordering::Relaxed);
184 }
185}
186
187static TYPE_CACHE: std::sync::LazyLock<Box<[TypeCacheEntry]>> = std::sync::LazyLock::new(|| {
191 (0..TYPE_CACHE_SIZE)
192 .map(|_| TypeCacheEntry::new())
193 .collect::<Vec<_>>()
194 .into_boxed_slice()
195});
196
197static TYPE_CACHE_CLEARING: AtomicBool = AtomicBool::new(false);
200
201#[inline]
203fn type_cache_hash(version: u32, name: &'static PyStrInterned) -> usize {
204 let name_hash = (name as *const PyStrInterned as usize >> 3) as u32;
205 ((version ^ name_hash) as usize) & TYPE_CACHE_MASK
206}
207
208fn type_cache_clear_version(version: u32) {
211 for entry in TYPE_CACHE.iter() {
212 if entry.version.load(Ordering::Relaxed) == version {
213 entry.begin_write();
214 if entry.version.load(Ordering::Relaxed) == version {
215 entry.version.store(0, Ordering::Release);
216 entry.clear_value();
217 }
218 entry.end_write();
219 }
220 }
221}
222
223pub(crate) fn type_cache_clear() {
231 TYPE_CACHE_CLEARING.store(true, Ordering::Release);
232 for entry in TYPE_CACHE.iter() {
233 entry.begin_write();
234 entry.version.store(0, Ordering::Release);
235 entry.clear_value();
236 entry.end_write();
237 }
238 TYPE_CACHE_CLEARING.store(false, Ordering::Release);
239}
240
241#[cfg(all(feature = "host_env", unix))]
247pub(crate) unsafe fn type_cache_after_fork() {
248 for entry in TYPE_CACHE.iter() {
249 let seq = entry.sequence.load(Ordering::Relaxed);
250 if (seq & 1) == 0 {
251 continue;
252 }
253 entry.value.store(core::ptr::null_mut(), Ordering::Relaxed);
254 entry.name.store(core::ptr::null_mut(), Ordering::Relaxed);
255 entry.version.store(0, Ordering::Relaxed);
256 entry.sequence.store(0, Ordering::Relaxed);
257 }
258}
259
260unsafe impl crate::object::Traverse for PyType {
261 fn traverse(&self, tracer_fn: &mut crate::object::TraverseFn<'_>) {
262 if let Some(base) = self.base.deref() {
263 tracer_fn(base.as_object());
264 }
265 if let Some(bases) = self.bases.try_read_recursive() {
267 tracer_fn(bases.as_untyped().as_object());
268 }
269 self.mro.traverse(tracer_fn);
270 self.subclasses.traverse(tracer_fn);
271 self.attributes.traverse(tracer_fn);
272 if let Some(ext) = self.heaptype_ext.as_ref() {
273 ext.specialization_cache.traverse(tracer_fn);
274 }
275 }
276
277 fn clear(&mut self, out: &mut Vec<crate::PyObjectRef>) {
279 if let Some(base) = unsafe { self.base.swap(None) } {
281 out.push(base.into());
282 }
283 let empty = object::PyBaseObject::static_type().bases.read().clone();
287 if let Some(mut bases) = self.bases.try_write() {
288 let old_bases = core::mem::replace(&mut *bases, empty);
289 out.push(old_bases.into_untyped().into());
290 }
291 if let Some(mut guard) = self.mro.try_write() {
292 for typ in guard.drain(..) {
293 out.push(typ.into());
294 }
295 }
296 if let Some(mut guard) = self.subclasses.try_write() {
297 for weak in guard.drain(..) {
298 out.push(weak.into());
299 }
300 }
301 self.attributes.drain_into(out);
302 if let Some(ext) = self.heaptype_ext.as_ref() {
303 ext.specialization_cache.clear_into(out);
304 }
305 }
306}
307
308pub struct HeapTypeExt {
310 pub name: PyRwLock<PyUtf8StrRef>,
311 pub qualname: PyRwLock<PyStrRef>,
312 pub slots: Option<PyRef<PyTuple<PyStrRef>>>,
313 pub type_data: PyRwLock<Option<TypeDataSlot>>,
314 pub specialization_cache: TypeSpecializationCache,
315 pub interpreter_id: Option<i64>,
318}
319
320impl HeapTypeExt {
321 fn creating_interpreter_id() -> Option<i64> {
323 crate::vm::thread::try_with_current_vm(|vm| vm.state.interpreter_id)
324 }
325}
326
327pub struct TypeSpecializationCache {
328 pub init: PyAtomicRef<Option<PyFunction>>,
329 pub init_version: AtomicU32,
330 pub getitem: PyAtomicRef<Option<PyFunction>>,
331 pub getitem_version: AtomicU32,
332}
333
334impl TypeSpecializationCache {
335 fn new() -> Self {
336 Self {
337 init: PyAtomicRef::from(None::<PyRef<PyFunction>>),
338 init_version: AtomicU32::new(0),
339 getitem: PyAtomicRef::from(None::<PyRef<PyFunction>>),
340 getitem_version: AtomicU32::new(0),
341 }
342 }
343
344 #[inline]
345 fn swap_init(&self, new_init: Option<PyRef<PyFunction>>) {
346 if let Some(new) = &new_init {
347 new.as_object().mark_cache_published();
348 }
349 let old = unsafe { self.init.swap(new_init) };
352 if let Some(old) = old {
353 rustpython_common::refcount::try_defer_drop(move || drop(old));
355 }
356 }
357
358 #[inline]
359 fn swap_getitem(&self, new_getitem: Option<PyRef<PyFunction>>) {
360 if let Some(new) = &new_getitem {
361 new.as_object().mark_cache_published();
362 }
363 let old = unsafe { self.getitem.swap(new_getitem) };
365 if let Some(old) = old {
366 rustpython_common::refcount::try_defer_drop(move || drop(old));
367 }
368 }
369
370 #[inline]
371 fn invalidate_for_type_modified(&self) {
372 self.swap_init(None);
373 self.init_version.store(0, Ordering::Release);
374 self.swap_getitem(None);
375 self.getitem_version.store(0, Ordering::Release);
376 }
377
378 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
379 if let Some(init) = self.init.deref() {
380 tracer_fn(init.as_object());
381 }
382 if let Some(getitem) = self.getitem.deref() {
383 tracer_fn(getitem.as_object());
384 }
385 }
386
387 fn clear_into(&self, out: &mut Vec<PyObjectRef>) {
388 let old_init = unsafe { self.init.swap(None) };
389 if let Some(old_init) = old_init {
390 out.push(old_init.into());
391 }
392 self.init_version.store(0, Ordering::Release);
393 let old_getitem = unsafe { self.getitem.swap(None) };
394 if let Some(old_getitem) = old_getitem {
395 out.push(old_getitem.into());
396 }
397 self.getitem_version.store(0, Ordering::Release);
398 }
399}
400
401pub type PyTypeRef = PyRef<PyType>;
402
403cfg_select! {
404 feature = "threading" => {
405 unsafe impl Send for PyType {}
406 unsafe impl Sync for PyType {}
407 }
408 _ => {}
409}
410
411pub(crate) type PyAttributes =
415 IndexMap<&'static PyStrInterned, PyObjectRef, rapidhash::quality::RandomState>;
416
417unsafe impl Traverse for PyAttributes {
418 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
419 self.values().for_each(|v| v.traverse(tracer_fn));
420 }
421}
422
423pub enum TypeNamespace {
430 Attributes(PyRwLock<PyAttributes>),
431 Dict(PyDictRef),
432}
433
434unsafe impl Traverse for TypeNamespace {
435 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
436 match self {
437 Self::Attributes(attrs) => {
438 if let Some(attrs) = attrs.try_read_recursive() {
439 attrs.traverse(tracer_fn);
440 }
441 }
442 Self::Dict(dict) => tracer_fn(dict.as_object()),
443 }
444 }
445}
446
447impl Default for TypeNamespace {
448 fn default() -> Self {
449 Self::Attributes(PyRwLock::default())
450 }
451}
452
453impl From<PyAttributes> for TypeNamespace {
454 fn from(attrs: PyAttributes) -> Self {
455 Self::Attributes(PyRwLock::new(attrs))
456 }
457}
458
459impl TypeNamespace {
460 pub fn as_dict(&self) -> Option<&Py<PyDict>> {
462 match self {
463 Self::Attributes(_) => None,
464 Self::Dict(dict) => Some(dict),
465 }
466 }
467
468 fn new(attrs: PyAttributes, ctx: &Context) -> Self {
471 let built = crate::vm::thread::try_with_current_vm(|vm| {
472 let dict = ctx.new_dict();
473 for (key, value) in &attrs {
474 dict.set_item(*key, value.clone(), vm)?;
475 }
476 PyResult::Ok(dict)
477 });
478 match built {
479 Some(Ok(dict)) => Self::Dict(dict),
480 _ => attrs.into(),
481 }
482 }
483
484 pub fn get(&self, name: &'static PyStrInterned) -> Option<PyObjectRef> {
485 match self {
486 Self::Attributes(attrs) => attrs.read().get(name).cloned(),
487 Self::Dict(dict) => dict_get(dict, name),
488 }
489 }
490
491 pub fn contains(&self, name: &'static PyStrInterned) -> bool {
492 match self {
493 Self::Attributes(attrs) => attrs.read().contains_key(name),
494 Self::Dict(dict) => {
495 match crate::vm::thread::try_with_current_vm(|vm| dict.contains_key(name, vm)) {
496 Some(found) => found,
497 None => dict_get(dict, name).is_some(),
498 }
499 }
500 }
501 }
502
503 pub fn set(&self, name: &'static PyStrInterned, value: PyObjectRef) {
505 drop(self.insert(name, value));
506 }
507
508 pub fn insert(&self, name: &'static PyStrInterned, value: PyObjectRef) -> Option<PyObjectRef> {
511 match self {
512 Self::Attributes(attrs) => attrs.write().insert(name, value),
513 Self::Dict(dict) => {
514 let previous = dict_get(dict, name);
515 if let Some(Err(_)) | None =
516 crate::vm::thread::try_with_current_vm(|vm| dict.set_item(name, value, vm))
517 {
518 debug_assert!(false, "type namespace write without a running VM");
519 }
520 previous
521 }
522 }
523 }
524
525 pub fn remove(&self, name: &'static PyStrInterned) -> Option<PyObjectRef> {
526 match self {
527 Self::Attributes(attrs) => attrs.write().shift_remove(name),
528 Self::Dict(dict) => {
529 let previous = dict_get(dict, name)?;
530 crate::vm::thread::try_with_current_vm(|vm| dict.del_item(name, vm).ok());
531 Some(previous)
532 }
533 }
534 }
535
536 pub fn entries(&self) -> Vec<(PyObjectRef, PyObjectRef)> {
538 match self {
539 Self::Attributes(attrs) => attrs
540 .read()
541 .iter()
542 .map(|(name, value)| ((*name).to_object(), value.clone()))
543 .collect(),
544 Self::Dict(dict) => dict.into_iter().collect(),
545 }
546 }
547
548 pub fn attributes(&self, ctx: &Context) -> PyAttributes {
551 match self {
552 Self::Attributes(attrs) => attrs.read().clone(),
553 Self::Dict(dict) => dict
554 .into_iter()
555 .filter_map(|(key, value)| {
556 let key = key.downcast_ref::<PyStr>()?;
557 Some((ctx.intern_str(key.as_wtf8()), value))
558 })
559 .collect(),
560 }
561 }
562
563 pub fn interned_names(&self, ctx: &Context) -> Vec<&'static PyStrInterned> {
566 match self {
567 Self::Attributes(attrs) => attrs.read().keys().copied().collect(),
568 Self::Dict(dict) => dict
569 .into_iter()
570 .filter_map(|(key, _)| {
571 let key = key.downcast_ref::<PyStr>()?;
572 Some(ctx.intern_str(key.as_wtf8()))
573 })
574 .collect(),
575 }
576 }
577
578 fn drain_into(&mut self, out: &mut Vec<PyObjectRef>) {
580 match self {
581 Self::Attributes(attrs) => {
582 if let Some(mut guard) = attrs.try_write() {
583 out.extend(guard.drain(..).map(|(_, value)| value));
584 }
585 }
586 Self::Dict(dict) => {
587 out.extend((&**dict).into_iter().map(|(_, value)| value));
588 Py::<PyDict>::clear(dict);
589 }
590 }
591 }
592}
593
594fn dict_get(dict: &Py<PyDict>, name: &'static PyStrInterned) -> Option<PyObjectRef> {
597 match crate::vm::thread::try_with_current_vm(|vm| dict.get_item_opt(name, vm)) {
598 Some(found) => found.ok().flatten(),
599 None => dict
600 .into_iter()
601 .find(|(key, _)| {
602 key.downcast_ref::<PyStr>()
603 .is_some_and(|key| key.as_wtf8() == name.as_wtf8())
604 })
605 .map(|(_, value)| value),
606 }
607}
608
609impl core::fmt::Display for PyType {
610 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
611 core::fmt::Display::fmt(&self.name(), f)
612 }
613}
614
615impl core::fmt::Debug for PyType {
616 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
617 write!(f, "[PyType {}]", self.name())
618 }
619}
620
621impl PyPayload for PyType {
622 #[inline]
623 fn class(ctx: &Context) -> &'static Py<PyType> {
624 ctx.types.type_type
625 }
626}
627
628fn downcast_qualname(value: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
631 match value.downcast::<PyStr>() {
632 Ok(value) => Ok(value),
633 Err(value) => Err(vm.new_type_error(format!(
634 "type __qualname__ must be a str, not {}",
635 value.class().name()
636 ))),
637 }
638}
639
640fn is_subtype_with_mro(a_mro: &[PyTypeRef], a: &Py<PyType>, b: &Py<PyType>) -> bool {
641 if a_mro.is_empty() {
642 let mut current = Some(a);
643 while let Some(typ) = current {
644 if typ.is(b) {
645 return true;
646 }
647 current = typ.base.deref();
648 }
649 return false;
650 }
651 a_mro.iter().any(|item| item.is(b))
652}
653
654impl PyType {
655 #[inline]
656 fn with_type_lock<R>(vm: &VirtualMachine, f: impl FnOnce() -> R) -> R {
657 thread_local! {
663 static HELD: Cell<bool> = const { Cell::new(false) };
664 }
665 rustpython_common::refcount::with_deferred_drops(|| {
666 HELD.with(|held| {
667 if held.get() {
668 f()
669 } else {
670 let _guard = vm.state.type_mutex.lock();
671 held.set(true);
672 let result = f();
673 held.set(false);
674 result
675 }
676 })
677 })
678 }
679
680 fn assign_version_tag_inner(&self) -> u32 {
683 let v = self.tp_version_tag.load(Ordering::Acquire);
684 if v != 0 {
685 return v;
686 }
687
688 for base in self.bases.read().as_slice() {
690 if base.assign_version_tag_inner() == 0 {
691 return 0;
692 }
693 }
694
695 loop {
696 let current = NEXT_TYPE_VERSION.load(Ordering::Relaxed);
697 let Some(next) = current.checked_add(1) else {
698 return 0; };
700 if NEXT_TYPE_VERSION
701 .compare_exchange_weak(current, next, Ordering::Relaxed, Ordering::Relaxed)
702 .is_ok()
703 {
704 self.tp_version_tag.store(current, Ordering::Release);
705 return current;
706 }
707 }
708 }
709
710 pub(crate) fn version_for_specialization(&self, vm: &VirtualMachine) -> u32 {
711 let version = self.tp_version_tag.load(Ordering::Acquire);
712 if version != 0 {
713 return version;
714 }
715 Self::with_type_lock(vm, || {
716 let version = self.tp_version_tag.load(Ordering::Acquire);
717 if version == 0 {
718 self.assign_version_tag_inner()
719 } else {
720 version
721 }
722 })
723 }
724
725 fn modified_inner(&self) {
727 let old_version = self.tp_version_tag.load(Ordering::Acquire);
728 if old_version == 0 {
729 return;
730 }
731 let subclasses = self.subclasses.read();
732 for weak_ref in subclasses.iter() {
733 if let Some(sub) = weak_ref.upgrade() {
734 sub.downcast_ref::<Self>().unwrap().modified_inner();
735 }
736 }
737 self.tp_version_tag.store(0, Ordering::SeqCst);
738 type_cache_clear_version(old_version);
741 if let Some(ext) = self.heaptype_ext.as_ref() {
742 ext.specialization_cache.invalidate_for_type_modified();
743 }
744 }
745
746 pub(crate) fn assign_specific_version(&self, version: u32) {
748 self.tp_version_tag.store(version, Ordering::Release);
749 }
750
751 pub fn modified(&self) {
752 if self.tp_version_tag.load(Ordering::Acquire) == 0 {
753 return;
754 }
755 if let Some(()) = crate::vm::thread::try_with_current_vm(|vm| {
756 Self::with_type_lock(vm, || self.modified_inner());
757 }) {
758 return;
759 }
760 self.modified_inner();
761 }
762
763 pub fn is_visible_to_interpreter(&self, interpreter_id: i64) -> bool {
769 match self
770 .heaptype_ext
771 .as_ref()
772 .and_then(|ext| ext.interpreter_id)
773 {
774 Some(owner) => owner == interpreter_id,
775 None => true,
776 }
777 }
778
779 pub fn new_simple_heap(
780 name: &str,
781 base: &Py<Self>,
782 ctx: &Context,
783 ) -> Result<PyRef<Self>, String> {
784 Self::new_heap(
785 name,
786 vec![base.to_owned()],
787 Default::default(),
788 Default::default(),
789 Self::static_type().to_owned(),
790 ctx,
791 )
792 }
793 pub fn new_heap(
794 name: &str,
795 bases: Vec<PyRef<Self>>,
796 attrs: PyAttributes,
797 slots: PyTypeSlots,
798 metaclass: PyRef<Self>,
799 ctx: &Context,
800 ) -> Result<PyRef<Self>, String> {
801 slots.flags.insert(PyTypeFlags::HEAPTYPE);
806
807 let name_utf8 = ctx.new_utf8_str(name);
808 let name = name_utf8.clone().into_wtf8();
809 let heaptype_ext = HeapTypeExt {
810 name: PyRwLock::new(name_utf8),
811 qualname: PyRwLock::new(name),
812 slots: None,
813 type_data: PyRwLock::new(None),
814 specialization_cache: TypeSpecializationCache::new(),
815 interpreter_id: HeapTypeExt::creating_interpreter_id(),
816 };
817 let bases = PyTuple::new_ref_typed(bases, ctx);
818 let base = bases.as_slice()[0].clone();
819
820 Self::new_heap_inner(
821 base,
822 bases,
823 attrs,
824 slots,
825 heaptype_ext,
826 metaclass,
827 ctx,
828 false,
829 )
830 }
831
832 pub(crate) fn check(obj: &PyObject) -> Option<&Py<Self>> {
835 obj.downcast_ref::<Self>()
836 }
837
838 fn resolve_mro(bases: &[PyRef<Self>]) -> Result<Vec<PyTypeRef>, String> {
839 let mut unique_bases = HashSet::new();
841 for base in bases {
842 if !unique_bases.insert(base.get_id()) {
843 return Err(format!("duplicate base class {}", base.name()));
844 }
845 }
846
847 let mros = bases
848 .iter()
849 .map(|base| base.mro_map_collect(|t| t.to_owned()))
850 .collect();
851 linearise_mro(mros)
852 }
853
854 fn inherit_patma_flags(slots: &mut PyTypeSlots, bases: &[PyRef<Self>]) {
857 if !slots.flags.load().is_disjoint(&PyTypeFlags::COLLECTION) {
859 return;
860 }
861
862 for base in bases {
864 let base_flags = base.slots.flags.load() & PyTypeFlags::COLLECTION;
865 if !base_flags.is_empty() {
866 slots.flags |= base_flags;
867 return;
868 }
869 }
870 }
871
872 pub fn has_patma_collection_flag(&self, flag: u8) -> bool {
873 debug_assert!(flag == PyTypeFlags::SEQUENCE || flag == PyTypeFlags::MAPPING);
874 self.slots.flags.has_feature(flag)
875 }
876
877 pub fn set_is_abstract(&self, is_abstract: bool) {
878 if is_abstract {
879 self.slots.flags.insert(PyTypeFlags::IS_ABSTRACT);
880 } else {
881 self.slots.flags.remove(PyTypeFlags::IS_ABSTRACT);
882 }
883 self.modified();
884 }
885
886 pub fn set_abc_collection_flags_recursive(&self, flags: PyTypeFlags) {
887 let flags = flags & PyTypeFlags::COLLECTION;
888 if flags.is_empty() {
889 return;
890 }
891 self.slots
892 .flags
893 .replace_masked(PyTypeFlags::COLLECTION, flags);
894 self.modified();
895 for weak_ref in self.subclasses.read().iter() {
896 if let Some(subclass) = weak_ref.upgrade()
897 && let Some(subclass) = subclass.downcast_ref::<Self>()
898 {
899 subclass.set_abc_collection_flags_recursive(flags);
900 }
901 }
902 }
903
904 fn check_abc_tpflags(
907 slots: &mut PyTypeSlots,
908 attrs: &PyAttributes,
909 bases: &[PyRef<Self>],
910 ctx: &Context,
911 ) -> Result<(), String> {
912 let abc_tpflags_name = ctx.intern_str("__abc_tpflags__");
916 if let Some(abc_tpflags_obj) = attrs.get(abc_tpflags_name)
917 && let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>()
918 {
919 let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
920 let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
921 let masked = abc_flags & PyTypeFlags::COLLECTION;
922 if masked == PyTypeFlags::COLLECTION {
923 return Err(
924 "__abc_tpflags__ cannot be both Py_TPFLAGS_SEQUENCE and Py_TPFLAGS_MAPPING"
925 .to_owned(),
926 );
927 }
928 slots.flags.remove_masked(PyTypeFlags::COLLECTION);
929 slots.flags |= masked;
930 return Ok(());
931 }
932
933 if !slots.flags.load().is_disjoint(&PyTypeFlags::COLLECTION) {
936 return Ok(());
937 }
938
939 for base in bases {
942 if let Some(abc_tpflags_obj) = base.find_name_in_mro(abc_tpflags_name)
943 && let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>()
944 {
945 let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
946 let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
947 let masked = abc_flags & PyTypeFlags::COLLECTION;
948 if masked == PyTypeFlags::COLLECTION {
949 return Err(
950 "__abc_tpflags__ cannot be both Py_TPFLAGS_SEQUENCE and Py_TPFLAGS_MAPPING"
951 .to_owned(),
952 );
953 }
954 slots.flags.remove_masked(PyTypeFlags::COLLECTION);
955 slots.flags |= masked;
956 return Ok(());
957 }
958 }
959 Ok(())
960 }
961
962 #[allow(clippy::too_many_arguments)]
963 fn new_heap_inner(
964 base: PyRef<Self>,
965 bases: PyTypeTupleRef,
966 attrs: PyAttributes,
967 mut slots: PyTypeSlots,
968 heaptype_ext: HeapTypeExt,
969 metaclass: PyRef<Self>,
970 ctx: &Context,
971 defer_mro: bool,
972 ) -> Result<PyRef<Self>, String> {
973 let mro = if defer_mro {
974 Vec::new()
976 } else {
977 Self::resolve_mro(bases.as_slice())?
978 };
979
980 if bases
983 .as_slice()
984 .iter()
985 .any(|b| b.slots.flags.has_feature(PyTypeFlags::HAS_DICT))
986 {
987 slots.flags.insert(PyTypeFlags::HAS_DICT);
988 }
989 if bases
990 .as_slice()
991 .iter()
992 .any(|b| b.slots.flags.has_feature(PyTypeFlags::MANAGED_DICT))
993 {
994 slots.flags.insert(PyTypeFlags::MANAGED_DICT);
995 }
996
997 if bases
998 .as_slice()
999 .iter()
1000 .any(|b| b.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF))
1001 {
1002 slots.flags |= WEAKREF_FLAGS;
1003 }
1004
1005 Self::inherit_patma_flags(&mut slots, bases.as_slice());
1007
1008 Self::check_abc_tpflags(&mut slots, &attrs, bases.as_slice(), ctx)?;
1010
1011 if slots.basicsize == 0 {
1012 slots.basicsize = base.slots.basicsize;
1013 }
1014
1015 if slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
1017 slots.flags.insert(PyTypeFlags::MANAGED_WEAKREF);
1018 }
1019
1020 if let Some(qualname) = attrs.get(identifier!(ctx, __qualname__))
1021 && !qualname.fast_isinstance(ctx.types.str_type)
1022 {
1023 return Err(format!(
1024 "type __qualname__ must be a str, not {}",
1025 qualname.class().name()
1026 ));
1027 }
1028
1029 let new_type = PyRef::new_ref(
1030 Self {
1031 base: Some(base).into(),
1032 bases: PyRwLock::new(bases),
1033 mro: PyRwLock::new(mro),
1034 subclasses: PyRwLock::default(),
1035 attributes: TypeNamespace::new(attrs, ctx),
1036 slots,
1037 heaptype_ext: Some(Pin::new(Box::new(heaptype_ext))),
1038 tp_version_tag: AtomicU32::new(0),
1039 },
1040 metaclass,
1041 None,
1042 );
1043 if !defer_mro {
1044 new_type.mro.write().insert(0, new_type.clone());
1045 new_type.init_slots(ctx);
1046 }
1047
1048 let weakref_type = super::PyWeak::static_type();
1049 for base in new_type.bases.read().as_slice() {
1050 base.subclasses.write().push(
1051 new_type
1052 .as_object()
1053 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
1054 .unwrap(),
1055 );
1056 }
1057
1058 Ok(new_type)
1059 }
1060
1061 pub fn new_static(
1062 base: PyRef<Self>,
1063 attrs: PyAttributes,
1064 mut slots: PyTypeSlots,
1065 metaclass: PyRef<Self>,
1066 ) -> Result<PyRef<Self>, String> {
1067 if base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
1068 slots.flags.insert(PyTypeFlags::HAS_DICT);
1069 }
1070 if base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
1071 slots.flags |= WEAKREF_FLAGS;
1072 }
1073
1074 Self::inherit_patma_flags(&mut slots, core::slice::from_ref(&base));
1077
1078 if slots.basicsize == 0 {
1079 slots.basicsize = base.slots.basicsize;
1080 }
1081
1082 if slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
1084 slots.flags.insert(PyTypeFlags::MANAGED_WEAKREF);
1085 }
1086
1087 let bases =
1088 PyTuple::new_ref_typed_with_type(vec![base.clone()], PyTuple::static_type().to_owned());
1089 let mro = base.mro_map_collect(|x| x.to_owned());
1090
1091 let new_type = PyRef::new_ref(
1092 Self {
1093 base: Some(base).into(),
1094 bases: PyRwLock::new(bases),
1095 mro: PyRwLock::new(mro),
1096 subclasses: PyRwLock::default(),
1097 attributes: attrs.into(),
1098 slots,
1099 heaptype_ext: None,
1100 tp_version_tag: AtomicU32::new(0),
1101 },
1102 metaclass,
1103 None,
1104 );
1105
1106 if !new_type.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
1110 unsafe {
1111 crate::gc_state::gc_state()
1112 .untrack_object(core::ptr::NonNull::from(new_type.as_object()));
1113 }
1114 new_type.as_object().clear_gc_tracked();
1115 }
1116
1117 new_type.mro.write().insert(0, new_type.clone());
1118
1119 Self::set_new(&new_type.slots, new_type.base.deref());
1123 Self::set_alloc(&new_type.slots, new_type.base.deref());
1124
1125 let weakref_type = super::PyWeak::static_type();
1126 for base in new_type.bases.read().as_slice() {
1127 base.subclasses.write().push(
1128 new_type
1129 .as_object()
1130 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
1131 .unwrap(),
1132 );
1133 }
1134
1135 Ok(new_type)
1136 }
1137
1138 pub(crate) fn init_slots(&self, ctx: &Context) {
1139 let mro_guard = self.mro.read();
1141 if mro_guard.is_empty() {
1142 return;
1143 }
1144 let mro: Vec<_> = mro_guard[1..].to_vec();
1145 drop(mro_guard);
1146 for base in &mro {
1147 self.inherit_slots(base);
1148 }
1149
1150 #[allow(clippy::mutable_key_type)]
1152 let mut slot_name_set = std::collections::HashSet::new();
1153
1154 for cls in &self.mro.read()[1..] {
1156 for name in cls.attributes.interned_names(ctx) {
1157 if name.as_bytes().starts_with(b"__") && name.as_bytes().ends_with(b"__") {
1158 slot_name_set.insert(name);
1159 }
1160 }
1161 }
1162 for name in self.attributes.interned_names(ctx) {
1163 if name.as_bytes().starts_with(b"__") && name.as_bytes().ends_with(b"__") {
1164 slot_name_set.insert(name);
1165 }
1166 }
1167 let mut slot_names: Vec<_> = slot_name_set.into_iter().collect();
1169 slot_names.sort_by_key(|name| name.as_str());
1170 for attr_name in slot_names {
1171 self.update_slot::<true>(attr_name, ctx);
1172 }
1173
1174 Self::set_new(&self.slots, self.base.deref());
1175 Self::set_alloc(&self.slots, self.base.deref());
1176 }
1177
1178 pub(crate) fn update_all_slots(&self, ctx: &Context) {
1185 self.modified_inner();
1187 let mut seen = std::collections::HashSet::new();
1190 for def in SLOT_DEFS {
1191 if seen.insert(def.name) {
1192 let name = ctx.intern_str(def.name);
1193 self.update_slot::<true>(name, ctx);
1194 }
1195 }
1196 }
1197
1198 fn set_new(slots: &PyTypeSlots, base: Option<&Py<Self>>) {
1199 if slots.flags.has_feature(PyTypeFlags::DISALLOW_INSTANTIATION) {
1200 slots.new.store(None)
1201 } else if slots.new.load().is_none() {
1202 slots.new.store(base.and_then(|base| base.slots.new.load()))
1203 }
1204 }
1205
1206 fn set_alloc(slots: &PyTypeSlots, base: Option<&Py<Self>>) {
1207 if slots.alloc.load().is_none() {
1208 slots
1209 .alloc
1210 .store(base.and_then(|base| base.slots.alloc.load()));
1211 }
1212 }
1213
1214 pub(crate) fn finalize_bootstrap_static(typ: &Py<Self>) {
1215 Self::set_new(&typ.slots, typ.base.deref());
1216 Self::set_alloc(&typ.slots, typ.base.deref());
1217 }
1218
1219 pub(crate) fn inherit_slots(&self, base: &Self) {
1221 for def in SLOT_DEFS {
1223 def.accessor.copyslot_if_none(self, base);
1224 }
1225 }
1226
1227 pub fn set_str_attr<V: Into<PyObjectRef>>(
1229 &self,
1230 attr_name: &str,
1231 value: V,
1232 ctx: impl AsRef<Context>,
1233 ) {
1234 let ctx = ctx.as_ref();
1235 let attr_name = ctx.intern_str(attr_name);
1236 self.set_attr(attr_name, value.into())
1237 }
1238
1239 pub fn set_attr(&self, attr_name: &'static PyStrInterned, value: PyObjectRef) {
1240 self.modified();
1244 self.attributes.set(attr_name, value);
1245 }
1246
1247 pub fn get_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
1250 self.find_name_in_mro(attr_name)
1251 }
1252
1253 pub(crate) fn lookup_ref_and_version_interned(
1261 &self,
1262 name: &'static PyStrInterned,
1263 vm: &VirtualMachine,
1264 ) -> (Option<PyObjectRef>, u32) {
1265 #[cfg(all(feature = "threading", debug_assertions))]
1266 crate::vm::thread::debug_assert_current_thread_attached();
1267
1268 let version = self.tp_version_tag.load(Ordering::Acquire);
1269 if version != 0 {
1270 let idx = type_cache_hash(version, name);
1271 let entry = &TYPE_CACHE[idx];
1272 let name_ptr = name as *const _ as *mut _;
1273 loop {
1274 let seq1 = entry.begin_read();
1275 let entry_version = entry.version.load(Ordering::Acquire);
1276 let type_version = self.tp_version_tag.load(Ordering::Acquire);
1277 if entry_version != type_version
1278 || !core::ptr::eq(entry.name.load(Ordering::Relaxed), name_ptr)
1279 {
1280 break;
1281 }
1282 let ptr = entry.value.load(Ordering::Acquire);
1283 if ptr.is_null() {
1284 if entry.end_read(seq1) {
1285 return (None, entry_version);
1286 }
1287 continue;
1288 }
1289 if let Some(cloned) = unsafe { PyObject::try_to_owned_from_ptr(ptr) } {
1290 let same_ptr = core::ptr::eq(entry.value.load(Ordering::Relaxed), ptr);
1291 if same_ptr && entry.end_read(seq1) {
1292 return (Some(cloned), entry_version);
1293 }
1294 drop(cloned);
1295 continue;
1296 }
1297 break;
1298 }
1299 }
1300
1301 Self::with_type_lock(vm, || {
1302 let assigned = if self.tp_version_tag.load(Ordering::Acquire) == 0 {
1303 self.assign_version_tag_inner()
1304 } else {
1305 self.tp_version_tag.load(Ordering::Acquire)
1306 };
1307 let result = self.find_name_in_mro_uncached(name);
1308 if assigned != 0
1309 && !TYPE_CACHE_CLEARING.load(Ordering::Acquire)
1310 && self.tp_version_tag.load(Ordering::Acquire) == assigned
1311 {
1312 let idx = type_cache_hash(assigned, name);
1313 let entry = &TYPE_CACHE[idx];
1314 let name_ptr = name as *const _ as *mut _;
1315 entry.begin_write();
1316 entry.version.store(0, Ordering::Release);
1317 let new_ptr = result.as_ref().map_or(core::ptr::null_mut(), |found| {
1318 found.mark_cache_published();
1321 &**found as *const PyObject as *mut _
1322 });
1323 entry.value.store(new_ptr, Ordering::Relaxed);
1324 entry.name.store(name_ptr, Ordering::Relaxed);
1325 entry.version.store(assigned, Ordering::Release);
1326 entry.end_write();
1327 }
1328 (result, assigned)
1329 })
1330 }
1331
1332 pub(crate) fn cache_init_for_specialization(
1335 &self,
1336 init: PyRef<PyFunction>,
1337 tp_version: u32,
1338 vm: &VirtualMachine,
1339 ) -> bool {
1340 let Some(ext) = self.heaptype_ext.as_ref() else {
1341 return false;
1342 };
1343 if tp_version == 0 {
1344 return false;
1345 }
1346 Self::with_type_lock(vm, || {
1347 if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
1348 return false;
1349 }
1350 let func_version = init.get_version_for_current_state();
1351 if func_version == 0 {
1352 return false;
1353 }
1354 ext.specialization_cache.swap_init(Some(init));
1355 ext.specialization_cache
1356 .init_version
1357 .store(func_version, Ordering::Release);
1358 true
1359 })
1360 }
1361
1362 pub(crate) fn get_cached_init_for_specialization(
1364 &self,
1365 tp_version: u32,
1366 ) -> Option<(PyRef<PyFunction>, u32)> {
1367 let ext = self.heaptype_ext.as_ref()?;
1368 if tp_version == 0 {
1369 return None;
1370 }
1371 if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
1372 return None;
1373 }
1374 let init = ext.specialization_cache.init.load_owned()?;
1376 let cached_version = ext
1377 .specialization_cache
1378 .init_version
1379 .load(Ordering::Acquire);
1380 if cached_version == 0 {
1381 return None;
1382 }
1383 Some((init, cached_version))
1384 }
1385
1386 pub(crate) fn cache_getitem_for_specialization(
1389 &self,
1390 getitem: PyRef<PyFunction>,
1391 tp_version: u32,
1392 vm: &VirtualMachine,
1393 ) -> bool {
1394 let Some(ext) = self.heaptype_ext.as_ref() else {
1395 return false;
1396 };
1397 if tp_version == 0 {
1398 return false;
1399 }
1400 Self::with_type_lock(vm, || {
1401 if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
1402 return false;
1403 }
1404 let func_version = getitem.get_version_for_current_state();
1405 if func_version == 0 {
1406 return false;
1407 }
1408 ext.specialization_cache.swap_getitem(Some(getitem));
1409 ext.specialization_cache
1410 .getitem_version
1411 .store(func_version, Ordering::Release);
1412 true
1413 })
1414 }
1415
1416 pub(crate) fn get_cached_getitem_for_specialization(&self) -> Option<(PyRef<PyFunction>, u32)> {
1418 let ext = self.heaptype_ext.as_ref()?;
1419 let getitem = ext.specialization_cache.getitem.load_owned()?;
1421 let cached_version = ext
1422 .specialization_cache
1423 .getitem_version
1424 .load(Ordering::Acquire);
1425 if cached_version == 0 {
1426 return None;
1427 }
1428 Some((getitem, cached_version))
1429 }
1430
1431 pub fn get_direct_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
1432 self.attributes.get(attr_name)
1433 }
1434
1435 fn find_name_in_mro(&self, name: &'static PyStrInterned) -> Option<PyObjectRef> {
1438 crate::vm::thread::try_with_current_vm(|vm| {
1439 self.lookup_ref_and_version_interned(name, vm).0
1440 })
1441 .unwrap_or_else(|| self.find_name_in_mro_uncached(name))
1445 }
1446
1447 fn find_name_in_mro_uncached(&self, name: &'static PyStrInterned) -> Option<PyObjectRef> {
1449 let mro: Vec<PyTypeRef> = self.mro.read().iter().cloned().collect();
1452 for cls in &mro {
1453 if let Some(value) = cls.attributes.get(name) {
1454 return Some(value);
1455 }
1456 }
1457 None
1458 }
1459
1460 pub fn lookup_ref(&self, name: &Py<PyStr>, vm: &VirtualMachine) -> Option<PyObjectRef> {
1462 let interned_name = vm.ctx.interned_str(name)?;
1463 self.lookup_ref_and_version_interned(interned_name, vm).0
1464 }
1465
1466 pub fn get_super_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
1467 let mro = self.mro.read();
1468 mro.get(1..)?
1469 .iter()
1470 .find_map(|class| class.attributes.get(attr_name))
1471 }
1472
1473 pub fn has_attr(&self, attr_name: &'static PyStrInterned) -> bool {
1475 self.has_name_in_mro(attr_name)
1476 }
1477
1478 fn has_name_in_mro(&self, name: &'static PyStrInterned) -> bool {
1481 #[cfg(all(feature = "threading", debug_assertions))]
1482 crate::vm::thread::debug_assert_current_thread_attached();
1483
1484 let version = self.tp_version_tag.load(Ordering::Acquire);
1485 if version != 0 {
1486 let idx = type_cache_hash(version, name);
1487 let entry = &TYPE_CACHE[idx];
1488 let name_ptr = name as *const _ as *mut _;
1489 loop {
1490 let seq1 = entry.begin_read();
1491 let v1 = entry.version.load(Ordering::Acquire);
1492 let type_version = self.tp_version_tag.load(Ordering::Acquire);
1493 if v1 != type_version
1494 || !core::ptr::eq(entry.name.load(Ordering::Relaxed), name_ptr)
1495 {
1496 break;
1497 }
1498 let ptr = entry.value.load(Ordering::Acquire);
1499 if entry.end_read(seq1) {
1500 if !ptr.is_null() {
1501 return true;
1502 }
1503 break;
1504 }
1505 continue;
1506 }
1507 }
1508
1509 self.find_name_in_mro(name).is_some()
1511 }
1512
1513 pub fn get_attributes(&self, ctx: &Context) -> PyAttributes {
1514 let mut attributes = PyAttributes::default();
1516
1517 for bc in self.mro.read().iter().map(|cls| -> &Self { cls }).rev() {
1519 attributes.extend(bc.attributes.attributes(ctx));
1520 }
1521
1522 attributes
1523 }
1524
1525 pub(crate) fn __new__(zelf: PyRef<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1527 let (subtype, args): (PyRef<Self>, FuncArgs) = args.bind(vm)?;
1528 if !subtype.fast_issubclass(&zelf) {
1529 return Err(vm.new_type_error(format!(
1530 "{zelf}.__new__({subtype}): {subtype} is not a subtype of {zelf}",
1531 zelf = zelf.name(),
1532 subtype = subtype.name(),
1533 )));
1534 }
1535 call_slot_new(&zelf, subtype, args, vm)
1536 }
1537
1538 fn name_inner<'a, R: 'a>(
1539 &'a self,
1540 static_f: impl FnOnce(&'static str) -> R,
1541 heap_f: impl FnOnce(&'a HeapTypeExt) -> R,
1542 ) -> R {
1543 if let Some(ref ext) = self.heaptype_ext {
1544 heap_f(ext)
1545 } else {
1546 static_f(self.slots.name)
1547 }
1548 }
1549
1550 pub fn slot_name(&self) -> BorrowedValue<'_, str> {
1551 self.name_inner(
1552 |name| name.into(),
1553 |ext| PyRwLockReadGuard::map(ext.name.read(), |name| name.as_str()).into(),
1554 )
1555 }
1556
1557 pub fn __qualname__(&self, vm: &VirtualMachine) -> PyObjectRef {
1558 if let Some(ref heap_type) = self.heaptype_ext {
1559 heap_type.qualname.read().clone().into()
1560 } else {
1561 vm.ctx.new_str(self.name().deref()).into()
1563 }
1564 }
1565 pub fn __module__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
1566 if self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
1569 return self
1570 .attributes
1571 .get(identifier!(vm, __module__))
1572 .ok_or_else(|| vm.new_attribute_error("__module__"));
1573 }
1574 let slot_name = self.slot_name();
1575 let module = if let Some((module, _)) = slot_name.rsplit_once('.') {
1576 vm.ctx.intern_str(module)
1577 } else {
1578 vm.ctx.intern_str("builtins")
1579 };
1580 Ok(module.to_object())
1581 }
1582
1583 pub fn fully_qualified_name(&self, vm: &VirtualMachine) -> PyResult<String> {
1586 let qualname = self.__qualname__(vm);
1587 let qualname = qualname
1588 .downcast_ref::<PyStr>()
1589 .and_then(|qualname| qualname.to_str())
1590 .map_or_else(|| self.name().to_string(), str::to_owned);
1591 let module = self.__module__(vm)?;
1592 Ok(
1593 match module.downcast_ref::<PyStr>().and_then(|m| m.to_str()) {
1594 Some("builtins" | "__main__") | None => qualname,
1595 Some(module) => format!("{module}.{qualname}"),
1596 },
1597 )
1598 }
1599
1600 pub fn name(&self) -> BorrowedValue<'_, str> {
1601 self.name_inner(
1602 |name| name.rsplit_once('.').map_or(name, |(_, name)| name).into(),
1603 |ext| PyRwLockReadGuard::map(ext.name.read(), |name| name.as_str()).into(),
1604 )
1605 }
1606
1607 pub fn init_type_data<T: Any + Send + Sync + 'static>(&self, data: T) -> Result<(), String> {
1612 let ext = self
1613 .heaptype_ext
1614 .as_ref()
1615 .ok_or_else(|| "Cannot set type data on non-heap types".to_string())?;
1616
1617 let mut type_data = ext.type_data.write();
1618 if type_data.is_some() {
1619 return Err("Type data already initialized".to_string());
1620 }
1621 *type_data = Some(TypeDataSlot::new(data));
1622 Ok(())
1623 }
1624
1625 pub fn get_type_data<T: Any + 'static>(&self) -> Option<TypeDataRef<'_, T>> {
1628 self.heaptype_ext
1629 .as_ref()
1630 .and_then(|ext| TypeDataRef::try_new(ext.type_data.read()))
1631 }
1632
1633 pub fn get_type_data_mut<T: Any + 'static>(&self) -> Option<TypeDataRefMut<'_, T>> {
1636 self.heaptype_ext
1637 .as_ref()
1638 .and_then(|ext| TypeDataRefMut::try_new(ext.type_data.write()))
1639 }
1640
1641 pub fn has_type_data<T: Any + 'static>(&self) -> bool {
1643 self.heaptype_ext.as_ref().is_some_and(|ext| {
1644 ext.type_data
1645 .read()
1646 .as_ref()
1647 .is_some_and(|slot| slot.get::<T>().is_some())
1648 })
1649 }
1650}
1651
1652impl Py<PyType> {
1653 #[inline]
1654 pub fn slots(&self) -> &PyTypeSlots {
1655 &self.payload().slots
1656 }
1657
1658 #[inline]
1659 pub fn attributes(&self) -> &TypeNamespace {
1660 &self.payload().attributes
1661 }
1662
1663 #[inline]
1664 pub fn tp_version_tag(&self) -> &AtomicU32 {
1665 &self.payload().tp_version_tag
1666 }
1667
1668 #[inline]
1669 pub fn heaptype_ext(&self) -> Option<&HeapTypeExt> {
1670 self.payload().heaptype_ext.as_deref()
1671 }
1672
1673 pub fn is_subtype(&self, other: &Self) -> bool {
1674 is_subtype_with_mro(&self.mro.read(), self, other)
1675 }
1676
1677 pub fn check_exact<'a>(obj: &'a PyObject, vm: &VirtualMachine) -> Option<&'a Self> {
1680 obj.downcast_ref_if_exact::<PyType>(vm)
1681 }
1682
1683 pub fn fast_issubclass(&self, cls: &impl Borrow<PyObject>) -> bool {
1687 let mro = self.mro.read();
1688 if mro.is_empty() {
1689 return self.as_object().is(cls.borrow());
1690 }
1691 mro.iter().any(|c| c.is(cls.borrow()))
1692 }
1693
1694 pub fn mro_map_collect<F, R>(&self, f: F) -> Vec<R>
1695 where
1696 F: Fn(&Self) -> R,
1697 {
1698 self.mro.read().iter().map(|x| &**x).map(f).collect()
1699 }
1700
1701 pub fn mro_collect(&self) -> Vec<PyRef<PyType>> {
1702 self.mro
1703 .read()
1704 .iter()
1705 .map(|x| &**x)
1706 .map(|x| x.to_owned())
1707 .collect()
1708 }
1709
1710 pub fn iter_base_chain(&self) -> impl Iterator<Item = &Self> {
1711 core::iter::successors(Some(self), |cls| cls.base.deref())
1712 }
1713
1714 pub fn extend_methods(&'static self, method_defs: &'static [PyMethodDef], ctx: &Context) {
1715 for method_def in method_defs {
1716 let method = method_def.to_proper_method(self, ctx);
1717 self.set_attr(ctx.intern_str(method_def.name), method);
1718 }
1719 }
1720}
1721
1722impl PyType {
1723 pub fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
1724 or_(other, zelf, vm)
1725 }
1726
1727 pub fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
1728 or_(zelf, other, vm)
1729 }
1730
1731 fn check_set_special_type_attr(
1732 &self,
1733 name: &PyStrInterned,
1734 vm: &VirtualMachine,
1735 ) -> PyResult<()> {
1736 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
1737 return Err(vm.new_type_error(format!(
1738 "cannot set '{}' attribute of immutable type '{}'",
1739 name,
1740 self.slot_name()
1741 )));
1742 }
1743 Ok(())
1744 }
1745}
1746
1747#[pyclass(
1748 itemsize = core::mem::size_of::<crate::builtins::descriptor::PyMemberDefLayout>(),
1749 with(
1750 Py,
1751 Constructor,
1752 Initializer,
1753 GetAttr,
1754 SetAttr,
1755 Callable,
1756 AsNumber,
1757 Representable
1758 ),
1759 flags(BASETYPE, HAS_DICT, HAS_WEAKREF)
1760)]
1761impl PyType {}
1762
1763struct PrepareArgs;
1765
1766impl FromArgs for PrepareArgs {
1767 const PARAMS: Option<&'static [Param]> = Some(&[
1768 Param::positional_only("name"),
1769 Param::positional_only("bases"),
1770 Param::var_keyword("kwds"),
1771 ]);
1772
1773 fn from_args(_vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
1774 core::mem::take(args);
1775 Ok(Self)
1776 }
1777}
1778
1779impl Constructor for PyType {
1780 type Args = FuncArgs;
1781
1782 fn slot_new(metatype: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1783 vm_trace!("type.__new__ {:?}", args);
1784
1785 let is_type_type = metatype.is(vm.ctx.types.type_type);
1786 if is_type_type && args.args.len() == 1 && args.kwargs.is_empty() {
1787 return Ok(args.args[0].class().to_owned().into());
1788 }
1789
1790 if args.args.len() != 3 {
1791 return Err(vm.new_type_error(if is_type_type {
1792 "type() takes 1 or 3 arguments".to_owned()
1793 } else {
1794 format!(
1795 "type.__new__() takes exactly 3 arguments ({} given)",
1796 args.args.len()
1797 )
1798 }));
1799 }
1800
1801 let (name, bases, dict, kwargs): (PyStrRef, PyTupleRef, PyDictRef, KwArgs) =
1802 args.clone().bind_for(vm, Self::NAME)?;
1803
1804 let dict = if args.args[2].class().is(vm.ctx.types.dict_type) {
1808 dict
1809 } else {
1810 let copied = vm.ctx.new_dict();
1811 copied.merge_object(args.args[2].clone(), vm)?;
1812 copied
1813 };
1814
1815 if name.as_bytes().contains(&0) {
1816 return Err(vm.new_value_error("type name must not contain null characters"));
1817 }
1818 let name = name.try_into_utf8(vm)?;
1819
1820 let (metatype, base, bases, base_is_type) = if bases.as_slice().is_empty() {
1821 let base = vm.ctx.types.object_type.to_owned();
1822 let bases = PyTuple::new_ref_typed(vec![base.clone()], &vm.ctx);
1823 (metatype, base, bases, false)
1824 } else {
1825 for obj in bases.as_slice() {
1826 if obj.downcast_ref::<Self>().is_none() {
1827 if vm
1828 .get_attribute_opt(obj, identifier!(vm, __mro_entries__))?
1829 .is_some()
1830 {
1831 return Err(vm.new_type_error(
1832 "type() doesn't support MRO entry resolution; \
1833 use types.new_class()",
1834 ));
1835 }
1836 return Err(vm.new_type_error("bases must be types"));
1837 }
1838 }
1839 let bases = bases.try_into_typed::<Self>(vm)?;
1840
1841 let winner = calculate_meta_class(metatype.clone(), bases.as_slice(), vm)?;
1843 let metatype = if !winner.is(&metatype) {
1844 if let Some(ref slot_new) = winner.slots.new.load() {
1845 return slot_new(winner, args, vm);
1847 }
1848 winner
1849 } else {
1850 metatype
1851 };
1852
1853 let base = best_base(bases.as_slice(), vm)?;
1854 let base_is_type = base.is(vm.ctx.types.type_type);
1855
1856 (metatype, base.to_owned(), bases, base_is_type)
1857 };
1858
1859 let qualname = dict
1860 .get_item_opt(identifier!(vm, __qualname__), vm)?
1861 .map(|obj| downcast_qualname(obj, vm))
1862 .transpose()?
1863 .unwrap_or_else(|| {
1864 name.clone().into_wtf8()
1866 });
1867
1868 let mut attributes = dict.to_attributes(vm, |vm| {
1869 crate::stdlib::_warnings::warn(
1870 vm.ctx.exceptions.runtime_warning,
1871 format!("non-string key in the __dict__ of class {name}"),
1872 1,
1873 vm,
1874 )
1875 })?;
1876 attributes.shift_remove(identifier!(vm, __qualname__));
1877
1878 if let Some(doc) = attributes.get(identifier!(vm, __doc__))
1880 && let Some(doc_str) = doc.downcast_ref::<PyStr>()
1881 {
1882 doc_str.ensure_valid_utf8(vm)?;
1883 }
1884
1885 if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__))
1886 && f.class().is(vm.ctx.types.function_type)
1887 {
1888 *f = PyClassMethod::from(f.clone()).into_pyobject(vm);
1889 }
1890
1891 if let Some(f) = attributes.get_mut(identifier!(vm, __class_getitem__))
1892 && f.class().is(vm.ctx.types.function_type)
1893 {
1894 *f = PyClassMethod::from(f.clone()).into_pyobject(vm);
1895 }
1896
1897 if let Some(f) = attributes.get_mut(identifier!(vm, __new__))
1898 && f.class().is(vm.ctx.types.function_type)
1899 {
1900 *f = PyStaticMethod::from(f.clone()).into_pyobject(vm);
1901 }
1902
1903 if let Some(globals) = crate::frame::current_globals() {
1904 let entry = attributes.entry(identifier!(vm, __module__));
1905 if let Entry::Vacant(entry) = entry
1906 && let Some(module_name) = globals.get_item_opt(identifier!(vm, __name__), vm)?
1907 {
1908 entry.insert(module_name);
1909 }
1910 }
1911
1912 if attributes.get(identifier!(vm, __eq__)).is_some()
1913 && attributes.get(identifier!(vm, __hash__)).is_none()
1914 {
1915 attributes.insert(identifier!(vm, __hash__), vm.ctx.none.clone().into());
1918 }
1919
1920 let (heaptype_slots, add_dict, add_weakref): (
1921 Option<PyRef<PyTuple<PyStrRef>>>,
1922 bool,
1923 bool,
1924 ) = if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
1925 if x.class().is(vm.ctx.types.bytes_type) {
1927 return Err(vm.new_type_error("__slots__ items must be strings, not 'bytes'"));
1928 }
1929
1930 let slots = if x.class().is(vm.ctx.types.str_type) {
1931 let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
1932 PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
1933 } else {
1934 let iter = x.get_iter(vm)?;
1935 let elements = {
1936 let mut elements = Vec::new();
1937 while let PyIterReturn::Return(element) = iter.next(vm)? {
1938 if element.class().is(vm.ctx.types.bytes_type) {
1940 return Err(
1941 vm.new_type_error("__slots__ items must be strings, not 'bytes'")
1942 );
1943 }
1944 elements.push(element);
1945 }
1946 elements
1947 };
1948 let tuple = elements.into_pytuple(vm);
1949 tuple.try_into_typed(vm)?
1950 };
1951
1952 if !slots.as_slice().is_empty() && base.slots.itemsize > 0 {
1956 return Err(vm.new_type_error(format!(
1957 "nonempty __slots__ not supported for subtype of '{}'",
1958 base.name()
1959 )));
1960 }
1961
1962 let mut seen_dict = false;
1964 let mut seen_weakref = false;
1965 for slot in slots.as_slice() {
1966 if !slot.isidentifier() {
1968 return Err(vm.new_type_error("__slots__ must be identifiers"));
1969 }
1970
1971 let slot_name = slot.as_bytes();
1972
1973 if slot_name == b"__dict__" {
1975 if seen_dict {
1976 return Err(
1977 vm.new_type_error("__dict__ slot disallowed: we already got one")
1978 );
1979 }
1980 seen_dict = true;
1981 }
1982
1983 if slot_name == b"__weakref__" {
1985 if seen_weakref {
1986 return Err(
1987 vm.new_type_error("__weakref__ slot disallowed: we already got one")
1988 );
1989 }
1990 seen_weakref = true;
1991 }
1992
1993 if slot_name != b"__qualname__"
1996 && slot_name != b"__classcell__"
1997 && slot_name != b"__classdictcell__"
1998 && attributes.contains_key(vm.ctx.intern_str(slot.as_wtf8()))
1999 {
2000 return Err(vm.new_value_error(format!(
2001 "'{}' in __slots__ conflicts with class variable",
2002 slot.as_wtf8()
2003 )));
2004 }
2005 }
2006
2007 if seen_dict && base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
2009 return Err(vm.new_type_error("__dict__ slot disallowed: we already got one"));
2010 }
2011
2012 if seen_weakref && base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
2014 return Err(vm.new_type_error("__weakref__ slot disallowed: we already got one"));
2015 }
2016
2017 let dict_name = "__dict__";
2019 let weakref_name = "__weakref__";
2020 let has_dict = slots.as_slice().iter().any(|s| s.as_wtf8() == dict_name);
2021 let add_weakref = seen_weakref;
2022
2023 let mut filtered: Vec<PyStrRef> = slots
2027 .as_slice()
2028 .iter()
2029 .filter(|s| s.as_wtf8() != dict_name && s.as_wtf8() != weakref_name)
2030 .cloned()
2031 .collect();
2032 filtered.sort_by(|a, b| a.as_bytes().cmp(b.as_bytes()));
2033 let filtered_slots = PyTuple::new_ref_typed(filtered, &vm.ctx);
2034
2035 (Some(filtered_slots), has_dict, add_weakref)
2036 } else {
2037 (None, false, false)
2038 };
2039
2040 let base_member_count = bases
2042 .as_slice()
2043 .iter()
2044 .map(|base| base.slots.member_count)
2045 .max()
2046 .unwrap();
2047 let heaptype_member_count = heaptype_slots.as_ref().map_or(0, |x| x.as_slice().len());
2048 let member_count: usize = base_member_count + heaptype_member_count;
2049
2050 let mut flags = PyTypeFlags::HEAP_TYPE;
2051
2052 let may_add_dict = !base.slots.flags.has_feature(PyTypeFlags::HAS_DICT);
2056
2057 if (heaptype_slots.is_none() && may_add_dict) || add_dict {
2061 flags |= if base.slots.itemsize == 0 {
2064 MANAGED_DICT_INLINE_FLAGS
2065 } else {
2066 MANAGED_DICT_FLAGS
2067 };
2068 }
2069
2070 let may_add_weakref =
2075 base.slots.itemsize == 0 && !base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF);
2076 if (heaptype_slots.is_none() && may_add_weakref) || add_weakref {
2077 flags |= WEAKREF_FLAGS;
2078 }
2079
2080 let (slots, heaptype_ext) = {
2081 let slots = PyTypeSlots {
2082 flags: crate::types::AtomicPyTypeFlags::from_plain(flags),
2083 member_count,
2084 itemsize: base.slots.itemsize,
2085 ..PyTypeSlots::heap_default()
2086 };
2087 let heaptype_ext = HeapTypeExt {
2088 name: PyRwLock::new(name),
2089 qualname: PyRwLock::new(qualname),
2090 slots: heaptype_slots.clone(),
2091 type_data: PyRwLock::new(None),
2092 specialization_cache: TypeSpecializationCache::new(),
2093 interpreter_id: HeapTypeExt::creating_interpreter_id(),
2094 };
2095 (slots, heaptype_ext)
2096 };
2097
2098 let custom_mro = !metatype.is(vm.ctx.types.type_type);
2099 let typ = Self::new_heap_inner(
2100 base,
2101 bases,
2102 attributes,
2103 slots,
2104 heaptype_ext,
2105 metatype,
2106 &vm.ctx,
2107 custom_mro,
2108 )
2109 .map_err(|e| vm.new_type_error(e))?;
2110
2111 if let Some(tp_dict) = typ.attributes.as_dict() {
2113 for (key, value) in &*dict {
2114 if key.downcast_ref::<PyStr>().is_none() {
2115 tp_dict.set_item(&*key, value, vm)?;
2116 }
2117 }
2118 }
2119
2120 if let Some(cell) = typ.attributes.get(identifier!(vm, __classcell__)) {
2123 let cell = PyCellRef::try_from_object(vm, cell.clone()).map_err(|_| {
2124 vm.new_type_error(format!(
2125 "__classcell__ must be a nonlocal cell, not {}",
2126 cell.class().name()
2127 ))
2128 })?;
2129 cell.set(Some(typ.clone().into()));
2130 typ.attributes.remove(identifier!(vm, __classcell__));
2131 }
2132 if let Some(cell) = typ.attributes.get(identifier!(vm, __classdictcell__)) {
2133 let cell = PyCellRef::try_from_object(vm, cell.clone()).map_err(|_| {
2134 vm.new_type_error(format!(
2135 "__classdictcell__ must be a nonlocal cell, not {}",
2136 cell.class().name()
2137 ))
2138 })?;
2139 let namespace = typ
2140 .attributes
2141 .as_dict()
2142 .expect("a type built by type.__new__ has a dict namespace");
2143 cell.set(Some(namespace.to_owned().into()));
2144 typ.attributes.remove(identifier!(vm, __classdictcell__));
2145 }
2146
2147 if custom_mro {
2148 mro_internal(&typ, vm)?;
2149 typ.init_slots(&vm.ctx);
2150 }
2151
2152 if let Some(ref slots) = heaptype_slots {
2153 let class_name = typ.name().to_string();
2154 for (offset, member) in (base_member_count..).zip(slots.as_slice().iter()) {
2155 let member_str = member
2157 .to_str()
2158 .ok_or_else(|| vm.new_type_error("__slots__ must be valid UTF-8 strings"))?;
2159 let mangled_name = mangle_name(&class_name, member_str);
2160 let member_def = PyMemberDef {
2161 name: mangled_name.clone(),
2162 kind: MemberKind::ObjectEx,
2163 offset: crate::object::slot_member_offset(offset),
2164 flags: PyMemberFlags::empty(),
2165 doc: ItemDoc::NONE,
2166 };
2167 let attr_name = vm.ctx.intern_str(mangled_name.as_str());
2168 let member_descriptor: PyRef<PyMemberDescriptor> =
2169 vm.ctx.new_pyref(PyMemberDescriptor {
2170 common: PyDescriptorOwned {
2171 typ: typ.clone(),
2172 name: attr_name,
2173 qualname: PyRwLock::new(None),
2174 },
2175 member: member_def,
2176 access: MemberAccess::Offset,
2177 });
2178 typ.set_attr(attr_name, member_descriptor.into());
2181 if attr_name.as_bytes().starts_with(b"__") && attr_name.as_bytes().ends_with(b"__")
2188 {
2189 typ.update_slot::<true>(attr_name, &vm.ctx);
2190 }
2191 }
2192 }
2193
2194 if !base_is_type && typ.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
2206 let __dict__ = identifier!(vm, __dict__);
2207 let has_inherited_dict = typ
2208 .mro
2209 .read()
2210 .iter()
2211 .any(|base| base.attributes.contains(__dict__));
2212 if !typ.attributes.contains(__dict__) && !has_inherited_dict {
2213 let getset = super::PyGetSet::new("__dict__", &typ, &vm.ctx)
2214 .with_get(subtype_get_dict)
2215 .with_set(subtype_set_dict)
2216 .with_doc(ItemDoc::static_text("dictionary for instance variables"));
2217 let descriptor = PyRef::new_ref(getset, vm.ctx.types.getset_type.to_owned(), None);
2218 typ.attributes.set(__dict__, descriptor.into());
2219 }
2220 }
2221
2222 if typ.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
2224 let __weakref__ = vm.ctx.intern_str("__weakref__");
2225 let has_inherited_weakref = typ
2226 .mro
2227 .read()
2228 .iter()
2229 .any(|base| base.attributes.contains(__weakref__));
2230 if !typ.attributes.contains(__weakref__) && !has_inherited_weakref {
2231 let getset = super::PyGetSet::new("__weakref__", &typ, &vm.ctx)
2232 .with_get(subtype_get_weakref)
2233 .with_set(subtype_set_weakref)
2234 .with_doc(ItemDoc::static_text(
2235 "list of weak references to the object",
2236 ));
2237 let descriptor = PyRef::new_ref(getset, vm.ctx.types.getset_type.to_owned(), None);
2238 typ.attributes.set(__weakref__, descriptor.into());
2239 }
2240 }
2241
2242 {
2246 let __doc__ = identifier!(vm, __doc__);
2247 if !typ.attributes.contains(__doc__) {
2248 typ.attributes.set(__doc__, vm.ctx.none());
2249 }
2250 }
2251
2252 let attributes = typ
2254 .attributes
2255 .entries()
2256 .into_iter()
2257 .filter_map(|(name, obj)| {
2258 vm.get_method(obj.clone(), identifier!(vm, __set_name__))
2259 .map(|res| res.map(|meth| (obj, name, meth)))
2260 })
2261 .collect::<PyResult<Vec<_>>>()?;
2262 for (obj, name, set_name) in attributes {
2263 let name_repr = name
2264 .str(vm)
2265 .map_or_else(|_| "?".to_owned(), |name| name.to_string());
2266 set_name.call((typ.clone(), name), vm).inspect_err(|e| {
2267 let note = format!(
2270 "Error calling __set_name__ on '{}' instance '{}' in '{}'",
2271 obj.class().name(),
2272 name_repr,
2273 typ.name()
2274 );
2275 drop(vm.call_method(e.as_object(), "add_note", (vm.ctx.new_str(note.as_str()),)));
2277 })?;
2278 }
2279
2280 let super_obj = vm
2282 .ctx
2283 .types
2284 .super_type
2285 .as_object()
2286 .call((typ.clone(), typ.clone()), vm)?;
2287 super_obj
2288 .get_attr(identifier!(vm, __init_subclass__), vm)?
2289 .call(kwargs, vm)?;
2290
2291 Ok(typ.into())
2292 }
2293
2294 fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
2295 unimplemented!("use slot_new")
2296 }
2297}
2298
2299const SIGNATURE_END_MARKER: &str = ")\n--\n\n";
2300fn get_signature(doc: &str) -> Option<&str> {
2301 doc.find(SIGNATURE_END_MARKER).map(|index| &doc[..=index])
2302}
2303
2304fn find_signature<'a>(name: &str, doc: &'a str) -> Option<&'a str> {
2305 let name = name.rsplit('.').next().unwrap();
2306 let doc = doc.strip_prefix(name)?;
2307 doc.starts_with('(').then_some(doc)
2308}
2309
2310pub(crate) fn get_text_signature_from_internal_doc<'a>(
2311 name: &str,
2312 internal_doc: &'a str,
2313) -> Option<&'a str> {
2314 find_signature(name, internal_doc).and_then(get_signature)
2315}
2316
2317fn doc_without_signature<'a>(name: &str, internal_doc: &'a str) -> &'a str {
2319 if let Some(doc_without_sig) = find_signature(name, internal_doc) {
2321 if let Some(sig_end_pos) = doc_without_sig.find(SIGNATURE_END_MARKER) {
2323 let after_sig = &doc_without_sig[sig_end_pos + SIGNATURE_END_MARKER.len()..];
2324 return after_sig;
2326 }
2327 }
2328 internal_doc
2330}
2331
2332pub(crate) fn get_doc_from_internal_doc<'a>(name: &str, internal_doc: &'a str) -> Option<&'a str> {
2334 let doc = doc_without_signature(name, internal_doc);
2335 (!doc.is_empty()).then_some(doc)
2336}
2337
2338pub(crate) fn rendered_item_doc(name: &str, doc: ItemDoc) -> Option<&'static str> {
2339 if doc.len != 0 {
2340 return db_doc(doc.offset, doc.len);
2341 }
2342 doc.text
2343 .and_then(|text| get_doc_from_internal_doc(name, text))
2344}
2345
2346impl Initializer for PyType {
2347 type Args = FuncArgs;
2348
2349 fn slot_init(_zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2351 if args.args.len() == 1 && !args.kwargs.is_empty() {
2353 return Err(vm.new_type_error("type.__init__() takes no keyword arguments"));
2354 }
2355 if args.args.len() != 1 && args.args.len() != 3 {
2356 return Err(vm.new_type_error("type.__init__() takes 1 or 3 arguments"));
2357 }
2358 Ok(())
2359 }
2360
2361 fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2362 unreachable!("slot_init is defined")
2363 }
2364}
2365
2366impl GetAttr for PyType {
2367 fn getattro(zelf: &Py<Self>, name_str: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
2368 #[cold]
2369 fn attribute_error(
2370 zelf: &Py<PyType>,
2371 name: &Wtf8,
2372 vm: &VirtualMachine,
2373 ) -> PyBaseExceptionRef {
2374 vm.new_attribute_error(format!(
2375 "type object '{}' has no attribute '{}'",
2376 zelf.slot_name(),
2377 name,
2378 ))
2379 }
2380
2381 let Some(name) = vm.ctx.interned_str(name_str) else {
2382 return Err(attribute_error(zelf, name_str.as_wtf8(), vm));
2383 };
2384 vm_trace!("type.__getattribute__({:?}, {:?})", zelf, name);
2385 let mcl = zelf.class();
2386 let mcl_attr = mcl.get_attr(name);
2387
2388 if let Some(ref attr) = mcl_attr {
2389 let attr_class = attr.class();
2390 let has_descr_set = attr_class.slots.descr_set.load().is_some();
2391 if has_descr_set {
2392 let descr_get = attr_class.slots.descr_get.load();
2393 if let Some(descr_get) = descr_get {
2394 return descr_get(
2395 attr.as_object(),
2396 Some(zelf.as_object()),
2397 Some(mcl.as_object()),
2398 vm,
2399 );
2400 }
2401 }
2402 }
2403
2404 let zelf_attr = zelf.get_attr(name);
2405
2406 if let Some(attr) = zelf_attr {
2407 let descr_get = attr.class().slots().descr_get.load();
2408 if let Some(descr_get) = descr_get {
2409 descr_get(attr.as_object(), None, Some(zelf.as_object()), vm)
2410 } else {
2411 Ok(attr)
2412 }
2413 } else if let Some(attr) = mcl_attr {
2414 vm.call_if_get_descriptor(&attr, zelf.to_owned().into())
2415 } else {
2416 Err(attribute_error(zelf, name_str.as_wtf8(), vm))
2417 }
2418 }
2419}
2420
2421#[pyclass]
2422impl Py<PyType> {
2423 #[pygetset]
2424 fn __mro__(&self, vm: &VirtualMachine) -> PyObjectRef {
2425 let mro = self.mro.read();
2426 if mro.is_empty() {
2427 return vm.ctx.none();
2428 }
2429 let elements: Vec<PyObjectRef> = mro.iter().map(|x| x.as_object().to_owned()).collect();
2430 drop(mro);
2431 vm.ctx.new_tuple(elements).into()
2432 }
2433
2434 #[pygetset]
2435 fn __doc__(&self, vm: &VirtualMachine) -> PyResult {
2436 let internal_doc = self.slots.doc;
2439 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
2440 && (internal_doc.text.is_some() || internal_doc.len != 0)
2441 {
2442 let doc_str = rendered_item_doc(&self.name(), internal_doc);
2443 return Ok(doc_str.map_or_else(|| vm.ctx.none(), |doc| vm.ctx.new_str(doc).into()));
2444 }
2445
2446 if let Some(doc_attr) = self.get_direct_attr(vm.ctx.intern_str("__doc__")) {
2449 let descr_get = doc_attr.class().slots().descr_get.load();
2451 if let Some(descr_get) = descr_get {
2452 descr_get(doc_attr.as_object(), None, Some(self.as_object()), vm)
2453 } else {
2454 Ok(doc_attr)
2455 }
2456 } else {
2457 Ok(vm.ctx.none())
2458 }
2459 }
2460
2461 #[pygetset(setter)]
2462 fn set___doc__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
2463 let value = value.ok_or_else(|| {
2465 vm.new_type_error(format!(
2466 "cannot delete '__doc__' attribute of immutable type '{}'",
2467 self.name()
2468 ))
2469 })?;
2470
2471 self.check_set_special_type_attr(identifier!(vm, __doc__), vm)?;
2473
2474 let _prev_value = PyType::with_type_lock(vm, || {
2475 self.modified_inner();
2476 self.attributes.insert(identifier!(vm, __doc__), value)
2477 });
2478
2479 Ok(())
2480 }
2481
2482 #[pymethod]
2483 fn __dir__(&self, vm: &VirtualMachine) -> PyList {
2484 let attributes: Vec<PyObjectRef> = self
2485 .get_attributes(&vm.ctx)
2486 .into_iter()
2487 .map(|(k, _)| k.to_object())
2488 .collect();
2489 PyList::from(attributes)
2490 }
2491
2492 #[pymethod]
2493 fn __instancecheck__(&self, instance: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
2494 instance.real_is_instance(self.as_object(), vm)
2496 }
2497
2498 #[pymethod]
2499 fn __subclasscheck__(&self, subclass: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
2500 subclass.real_is_subclass(self.as_object(), vm)
2503 }
2504
2505 #[pyclassmethod]
2506 fn __subclasshook__(_args: FuncArgs, vm: &VirtualMachine) -> PyObjectRef {
2507 vm.ctx.not_implemented()
2508 }
2509
2510 #[pymethod]
2511 fn mro(&self, vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
2512 Ok(mro_implementation(self, vm)?
2513 .into_iter()
2514 .map(Into::into)
2515 .collect())
2516 }
2517
2518 #[pygetset]
2519 fn __bases__(&self, vm: &VirtualMachine) -> PyTupleRef {
2520 PyType::with_type_lock(vm, || self.bases.read().clone().into_untyped())
2521 }
2522 #[pygetset(setter, name = "__bases__")]
2523 fn set_bases(zelf: &Self, bases_tuple: PyTupleRef, vm: &VirtualMachine) -> PyResult<()> {
2524 if zelf.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
2527 return Err(vm.new_type_error(format!(
2528 "cannot set '__bases__' attribute of immutable type '{}'",
2529 zelf.name()
2530 )));
2531 }
2532 if bases_tuple.as_slice().is_empty() {
2533 return Err(vm.new_type_error(format!(
2534 "can only assign non-empty tuple to {}.__bases__, not ()",
2535 zelf.name()
2536 )));
2537 }
2538 for base in bases_tuple.as_slice() {
2539 if base.downcast_ref::<PyType>().is_none() {
2540 return Err(vm.new_type_error(format!(
2541 "{}.__bases__ must be tuple of classes, not '{}'",
2542 zelf.name(),
2543 base.class().name()
2544 )));
2545 }
2546 }
2547 let bases = bases_tuple.try_into_typed::<PyType>(vm)?;
2548
2549 let new_base = best_base(bases.as_slice(), vm)?.to_owned();
2553
2554 let old_base_owned = zelf.base.load_owned();
2557 let old_base = old_base_owned
2558 .as_deref()
2559 .unwrap_or(vm.ctx.types.object_type);
2560 compatible_for_assignment(old_base, &new_base, "__bases__", vm)?;
2561
2562 let mut retired: Vec<PyObjectRef> = Vec::new();
2566
2567 let keep_alive = |type_ref: PyTypeRef, retired: &mut Vec<PyObjectRef>| {
2571 if let Some(frame) = vm.current_frame() {
2572 frame
2573 .iframe()
2574 .cold()
2575 .temporary_refs
2576 .lock()
2577 .push(type_ref.into());
2578 } else {
2579 retired.push(type_ref.into());
2580 }
2581 };
2582
2583 let add_as_subclass = |bases: &[PyTypeRef]| {
2584 let weakref_type = super::PyWeak::static_type();
2585 for base in bases {
2586 base.subclasses.write().push(
2587 zelf.as_object()
2588 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
2589 .unwrap(),
2590 );
2591 }
2592 };
2593
2594 let remove_as_subclass = |bases: &[PyTypeRef], retired: &mut Vec<PyObjectRef>| {
2595 for base in bases {
2596 let mut subclasses = base.subclasses.write();
2597 let mut kept = Vec::with_capacity(subclasses.len());
2598 for weak in subclasses.drain(..) {
2599 match weak.upgrade() {
2600 Some(obj) if obj.is(zelf.as_object()) => {
2601 retired.push(obj);
2602 retired.push(weak.into());
2603 }
2604 Some(obj) => {
2605 retired.push(obj);
2606 kept.push(weak);
2607 }
2608 None => retired.push(weak.into()),
2609 }
2610 }
2611 *subclasses = kept;
2612 }
2613 };
2614
2615 let result = PyType::with_type_lock(vm, || {
2616 for base in bases.as_slice() {
2617 if is_subtype_with_mro(&base.mro.read(), base, zelf)
2618 || (!base.mro.read().is_empty() && type_is_subtype_base_chain(base, zelf, vm))
2619 {
2620 return Err(vm.new_type_error("a __bases__ item causes an inheritance cycle"));
2621 }
2622 }
2623
2624 let old_bases = core::mem::replace(&mut *zelf.bases.write(), bases.clone());
2625 let old_base = unsafe { zelf.base.swap(Some(new_base.clone())) };
2626
2627 fn update_mro_recursively(
2630 cls: &Py<PyType>,
2631 undo: &mut Vec<(PyTypeRef, Vec<PyTypeRef>)>,
2632 vm: &VirtualMachine,
2633 ) -> PyResult<i32> {
2634 let old_ptr = cls.mro.read().as_ptr();
2635 let new_mro = mro_invoke(cls, vm)?;
2636 if cls.mro.read().as_ptr() != old_ptr {
2637 return Ok(0);
2639 }
2640 let old_mro = core::mem::replace(&mut *cls.mro.write(), new_mro);
2641 undo.push((cls.to_owned(), old_mro));
2642 cls.modified_inner();
2643 let subclasses: Vec<PyTypeRef> = cls
2644 .subclasses
2645 .read()
2646 .iter()
2647 .filter_map(|subclass| subclass.upgrade())
2648 .filter_map(|subclass| subclass.downcast::<PyType>().ok())
2649 .collect();
2650 for subclass in subclasses {
2651 update_mro_recursively(&subclass, undo, vm)?;
2652 }
2653 Ok(1)
2654 }
2655 let mut undo = Vec::new();
2656 if let Err(err) = update_mro_recursively(zelf, &mut undo, vm) {
2657 for (cls, old_mro) in undo.into_iter().rev() {
2661 let failed_mro = core::mem::replace(&mut *cls.mro.write(), old_mro);
2662 retired.extend(failed_mro.into_iter().map(Into::into));
2663 retired.push(cls.into());
2664 }
2665 if core::ptr::eq(&**zelf.bases.read(), &*bases) {
2667 let failed_bases = core::mem::replace(&mut *zelf.bases.write(), old_bases);
2668 if let Some(failed_base) = unsafe { zelf.base.swap(old_base) } {
2669 keep_alive(failed_base, &mut retired);
2670 }
2671 retired.push(failed_bases.into_untyped().into());
2672 zelf.modified_inner();
2673 } else {
2674 retired.push(old_bases.into_untyped().into());
2675 if let Some(old_base) = old_base {
2676 keep_alive(old_base, &mut retired);
2677 }
2678 }
2679 return Err(err);
2680 }
2681 for (cls, old_mro) in undo {
2684 retired.extend(old_mro.into_iter().map(Into::into));
2685 retired.push(cls.into());
2686 }
2687
2688 if core::ptr::eq(&**zelf.bases.read(), &*bases) {
2690 remove_as_subclass(old_bases.as_slice(), &mut retired);
2691 add_as_subclass(bases.as_slice());
2692 zelf.update_all_slots(&vm.ctx);
2693 }
2694
2695 retired.push(old_bases.into_untyped().into());
2696 if let Some(old_base) = old_base {
2697 keep_alive(old_base, &mut retired);
2698 }
2699 crate::stdlib::_testinternalcapi::note_set_bases();
2700 Ok(())
2701 });
2702 drop(retired);
2703 result
2704 }
2705
2706 #[pygetset]
2707 fn __abstractmethods__(zelf: &Self, vm: &VirtualMachine) -> PyResult {
2708 if zelf.is(vm.ctx.types.type_type) {
2709 return Err(vm.new_attribute_error("__abstractmethods__"));
2710 }
2711 zelf.get_direct_attr(identifier!(vm, __abstractmethods__))
2712 .ok_or_else(|| vm.new_attribute_error("__abstractmethods__"))
2713 }
2714
2715 #[pygetset(setter)]
2716 fn set___abstractmethods__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
2717 let key = identifier!(vm, __abstractmethods__);
2718 let is_abstract = match &value {
2719 PySetterValue::Assign(val) => val.try_to_bool(vm)?,
2720 PySetterValue::Delete => false,
2721 };
2722 match value {
2723 PySetterValue::Assign(val) => {
2724 PyType::with_type_lock(vm, || {
2725 self.modified_inner();
2726 let _prev = self.attributes.insert(key, val);
2727 self.set_is_abstract(is_abstract);
2728 });
2729 }
2730 PySetterValue::Delete => {
2731 let removed = PyType::with_type_lock(vm, || {
2732 self.modified_inner();
2733 let removed = self.attributes.remove(key);
2734 if removed.is_some() {
2735 self.set_is_abstract(false);
2736 }
2737 removed
2738 });
2739 if removed.is_none() {
2740 return Err(vm.new_attribute_error("__abstractmethods__"));
2741 }
2742 }
2743 }
2744 Ok(())
2745 }
2746
2747 #[pygetset]
2748 pub fn __name__(&self, vm: &VirtualMachine) -> PyStrRef {
2749 self.name_inner(
2750 |name| {
2751 vm.ctx
2752 .interned_str(name.rsplit_once('.').map_or(name, |(_, name)| name))
2753 .unwrap_or_else(|| {
2754 panic!(
2755 "static type name must be already interned but {} is not",
2756 self.slot_name()
2757 )
2758 })
2759 .to_owned()
2760 },
2761 |ext| ext.name.read().clone().into_wtf8(),
2762 )
2763 }
2764
2765 #[pygetset]
2766 pub fn __qualname__(&self, vm: &VirtualMachine) -> PyObjectRef {
2767 self.payload.__qualname__(vm)
2768 }
2769
2770 #[pygetset(setter)]
2771 fn set___qualname__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
2772 self.check_set_special_type_attr(identifier!(vm, __qualname__), vm)?;
2773 let value = value.ok_or_else(|| {
2774 vm.new_type_error(format!(
2775 "cannot delete '__qualname__' attribute of immutable type '{}'",
2776 self.name()
2777 ))
2778 })?;
2779
2780 let str_value = downcast_qualname(value, vm)?;
2781
2782 let heap_type = self.heaptype_ext.as_ref().ok_or_else(|| {
2783 vm.new_type_error(format!(
2784 "cannot set '__qualname__' attribute of immutable type '{}'",
2785 self.name()
2786 ))
2787 })?;
2788
2789 let _old_qualname = {
2792 let mut qualname_guard = heap_type.qualname.write();
2793 core::mem::replace(&mut *qualname_guard, str_value)
2794 };
2795 Ok(())
2798 }
2799
2800 #[pygetset]
2801 fn __annotate__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
2802 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
2803 return Err(vm.new_attribute_error(format!(
2804 "type object '{}' has no attribute '__annotate__'",
2805 self.name()
2806 )));
2807 }
2808
2809 let annotate_key = identifier!(vm, __annotate__);
2810 let annotate_func_key = identifier!(vm, __annotate_func__);
2811 if let Some(annotate) = self.attributes.get(annotate_key) {
2812 return Ok(annotate);
2813 }
2814 if let Some(annotate) = self.attributes.get(annotate_func_key) {
2815 return Ok(annotate);
2816 }
2817
2818 let none = vm.ctx.none();
2819 let (result, _prev) = PyType::with_type_lock(vm, || {
2820 if let Some(annotate) = self.attributes.get(annotate_key) {
2821 return (annotate, None);
2822 }
2823 if let Some(annotate) = self.attributes.get(annotate_func_key) {
2824 return (annotate, None);
2825 }
2826 self.modified_inner();
2827 let prev = self.attributes.insert(annotate_func_key, none.clone());
2828 (none, prev)
2829 });
2830 Ok(result)
2831 }
2832
2833 #[pygetset(setter)]
2834 fn set___annotate__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
2835 let value = match value {
2836 PySetterValue::Delete => {
2837 return Err(vm.new_type_error("cannot delete __annotate__ attribute"));
2838 }
2839 PySetterValue::Assign(v) => v,
2840 };
2841
2842 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
2843 return Err(vm.new_type_error(format!(
2844 "cannot set '__annotate__' attribute of immutable type '{}'",
2845 self.name()
2846 )));
2847 }
2848
2849 if !vm.is_none(&value) && !value.is_callable() {
2850 return Err(vm.new_type_error("__annotate__ must be callable or None"));
2851 }
2852
2853 let _prev_values = PyType::with_type_lock(vm, || {
2854 self.modified_inner();
2855 let removed = if !vm.is_none(&value) {
2857 self.attributes
2858 .remove(identifier!(vm, __annotations_cache__))
2859 } else {
2860 None
2861 };
2862 let prev = self
2863 .attributes
2864 .insert(identifier!(vm, __annotate_func__), value);
2865 (removed, prev)
2866 });
2867
2868 Ok(())
2869 }
2870
2871 #[pygetset]
2872 fn __annotations__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
2873 let annotations_key = identifier!(vm, __annotations__);
2874 let annotations_cache_key = identifier!(vm, __annotations_cache__);
2875 if let Some(annotations) = self.attributes.get(annotations_key) {
2876 if !annotations.class().is(vm.ctx.types.getset_type) {
2878 if vm.is_none(&annotations)
2879 || annotations.class().is(vm.ctx.types.dict_type)
2880 || self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
2881 {
2882 return Ok(annotations);
2883 }
2884 return Err(vm.new_attribute_error(format!(
2885 "type object '{}' has no attribute '__annotations__'",
2886 self.name()
2887 )));
2888 }
2889 }
2890 if let Some(annotations) = self.attributes.get(annotations_cache_key) {
2891 if vm.is_none(&annotations)
2892 || annotations.class().is(vm.ctx.types.dict_type)
2893 || self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
2894 {
2895 return Ok(annotations);
2896 }
2897 return Err(vm.new_attribute_error(format!(
2898 "type object '{}' has no attribute '__annotations__'",
2899 self.name()
2900 )));
2901 }
2902
2903 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
2904 return Err(vm.new_attribute_error(format!(
2905 "type object '{}' has no attribute '__annotations__'",
2906 self.name()
2907 )));
2908 }
2909
2910 let annotate = self.__annotate__(vm)?;
2912 let annotations = if annotate.is_callable() {
2913 let result = annotate.call((1i32,), vm)?;
2915 if !result.class().is(vm.ctx.types.dict_type) {
2916 return Err(vm.new_type_error(format!(
2917 "__annotate__ returned non-dict of type '{}'",
2918 result.class().name()
2919 )));
2920 }
2921 result
2922 } else {
2923 vm.ctx.new_dict().into()
2924 };
2925
2926 let (result, _prev) = PyType::with_type_lock(vm, || {
2927 if let Some(existing) = self.attributes.get(annotations_key)
2928 && !existing.class().is(vm.ctx.types.getset_type)
2929 {
2930 return (existing, None);
2931 }
2932 if let Some(existing) = self.attributes.get(annotations_cache_key) {
2933 return (existing, None);
2934 }
2935 self.modified_inner();
2936 let prev = self
2937 .attributes
2938 .insert(annotations_cache_key, annotations.clone());
2939 (annotations, prev)
2940 });
2941 Ok(result)
2942 }
2943
2944 #[pygetset(setter)]
2945 fn set___annotations__(
2946 &self,
2947 value: crate::function::PySetterValue<PyObjectRef>,
2948 vm: &VirtualMachine,
2949 ) -> PyResult<()> {
2950 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
2951 return Err(vm.new_type_error(format!(
2952 "cannot set '__annotations__' attribute of immutable type '{}'",
2953 self.name()
2954 )));
2955 }
2956
2957 let _prev_values = PyType::with_type_lock(vm, || {
2958 self.modified_inner();
2959 let has_annotations = self.attributes.contains(identifier!(vm, __annotations__));
2960
2961 let mut prev = Vec::new();
2962 match value {
2963 crate::function::PySetterValue::Assign(value) => {
2964 let key = if has_annotations {
2965 identifier!(vm, __annotations__)
2966 } else {
2967 identifier!(vm, __annotations_cache__)
2968 };
2969 prev.extend(self.attributes.insert(key, value));
2970 if has_annotations {
2971 prev.extend(
2972 self.attributes
2973 .remove(identifier!(vm, __annotations_cache__)),
2974 );
2975 }
2976 }
2977 crate::function::PySetterValue::Delete => {
2978 let removed = if has_annotations {
2979 self.attributes.remove(identifier!(vm, __annotations__))
2980 } else {
2981 self.attributes
2982 .remove(identifier!(vm, __annotations_cache__))
2983 };
2984 if removed.is_none() {
2985 return Err(vm.new_attribute_error("__annotations__"));
2986 }
2987 prev.extend(removed);
2988 if has_annotations {
2989 prev.extend(
2990 self.attributes
2991 .remove(identifier!(vm, __annotations_cache__)),
2992 );
2993 }
2994 }
2995 }
2996 prev.extend(self.attributes.remove(identifier!(vm, __annotate_func__)));
2997 prev.extend(self.attributes.remove(identifier!(vm, __annotate__)));
2998 Ok(prev)
2999 })?;
3000
3001 Ok(())
3002 }
3003
3004 #[pygetset]
3005 pub fn __module__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
3006 self.payload.__module__(vm)
3007 }
3008
3009 #[pygetset(setter)]
3010 fn set___module__(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
3011 self.check_set_special_type_attr(identifier!(vm, __module__), vm)?;
3012 let _prev_values = PyType::with_type_lock(vm, || {
3013 self.modified_inner();
3014 let removed = self.attributes.remove(identifier!(vm, __firstlineno__));
3015 let prev = self.attributes.insert(identifier!(vm, __module__), value);
3016 (removed, prev)
3017 });
3018 Ok(())
3019 }
3020
3021 #[pyclassmethod]
3022 fn __prepare__(_cls: PyTypeRef, _args: PrepareArgs, vm: &VirtualMachine) -> PyDictRef {
3023 vm.ctx.new_dict()
3024 }
3025
3026 #[pymethod]
3027 fn __subclasses__(&self, vm: &VirtualMachine) -> PyList {
3028 let mut subclasses = self.subclasses.write();
3029 subclasses.retain(|x| x.upgrade().is_some());
3030 let interpreter_id = vm.state.interpreter_id;
3031 PyList::from(
3032 subclasses
3033 .iter()
3034 .filter_map(|x| x.upgrade())
3035 .filter(|obj| {
3036 obj.downcast_ref::<PyType>()
3037 .is_none_or(|typ| typ.is_visible_to_interpreter(interpreter_id))
3038 })
3039 .collect::<Vec<_>>(),
3040 )
3041 }
3042
3043 #[pygetset]
3044 fn __dict__(zelf: PyRef<PyType>) -> PyMappingProxy {
3045 PyMappingProxy::from(zelf)
3046 }
3047
3048 #[pygetset(setter)]
3049 fn set___dict__(&self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
3050 Err(vm.new_attribute_error(format!(
3051 "attribute '__dict__' of '{}' objects is not writable",
3052 self.name()
3053 )))
3054 }
3055
3056 #[pygetset(setter)]
3057 fn set___name__(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
3058 self.check_set_special_type_attr(identifier!(vm, __name__), vm)?;
3059 let name = value.downcast::<PyStr>().map_err(|value| {
3060 vm.new_type_error(format!(
3061 "can only assign string to {}.__name__, not '{}'",
3062 self.slot_name(),
3063 value.class().slot_name(),
3064 ))
3065 })?;
3066 if name.as_bytes().contains(&0) {
3067 return Err(vm.new_value_error("type name must not contain null characters"));
3068 }
3069 let name = name.try_into_utf8(vm)?;
3070
3071 let heap_type = self.heaptype_ext.as_ref().ok_or_else(|| {
3072 vm.new_type_error(format!(
3073 "cannot set '__name__' attribute of immutable type '{}'",
3074 self.slot_name()
3075 ))
3076 })?;
3077
3078 let _old_name = {
3081 let mut name_guard = heap_type.name.write();
3082 core::mem::replace(&mut *name_guard, name)
3083 };
3084 Ok(())
3087 }
3088
3089 #[pygetset]
3090 fn __text_signature__(&self, vm: &VirtualMachine) -> Option<String> {
3091 let name = self.name();
3092 if let Some(text) = self.slots.doc.text
3093 && let Some(signature) = get_text_signature_from_internal_doc(&name, text)
3094 {
3095 return Some(signature.to_string());
3096 }
3097 if self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
3098 && let Some(doc_attr) = self.get_direct_attr(identifier!(vm, __doc__))
3099 && let Some(doc) = doc_attr.downcast_ref::<PyStr>()
3100 && let Some(doc) = doc.to_str()
3101 && let Some(signature) = get_text_signature_from_internal_doc(&name, doc)
3102 {
3103 return Some(signature.to_string());
3104 }
3105 None
3106 }
3107
3108 #[pygetset]
3109 fn __type_params__(&self, vm: &VirtualMachine) -> PyObjectRef {
3110 let key = identifier!(vm, __type_params__);
3111 if let Some(params) = self.attributes.get(key)
3112 && !params.class().is(vm.ctx.types.getset_type)
3115 {
3116 return params;
3117 }
3118 vm.ctx.empty_tuple.clone().into()
3119 }
3120
3121 #[pygetset(setter)]
3122 fn set___type_params__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
3123 let key = identifier!(vm, __type_params__);
3124 match value {
3125 PySetterValue::Assign(val) => {
3126 self.check_set_special_type_attr(key, vm)?;
3127 let _prev_value = PyType::with_type_lock(vm, || {
3128 self.modified_inner();
3129 self.attributes.insert(key, val)
3130 });
3131 }
3132 PySetterValue::Delete => {
3133 return Err(vm.new_type_error("cannot delete '__type_params__'"));
3134 }
3135 }
3136 Ok(())
3137 }
3138}
3139
3140impl SetAttr for PyType {
3141 fn setattro(
3142 zelf: &Py<Self>,
3143 attr_name: &Py<PyStr>,
3144 value: PySetterValue,
3145 vm: &VirtualMachine,
3146 ) -> PyResult<()> {
3147 let attr_name = vm.ctx.intern_str(attr_name.as_wtf8());
3148 if zelf.slots().flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
3149 return Err(vm.new_type_error(format!(
3150 "cannot set '{}' attribute of immutable type '{}'",
3151 attr_name,
3152 zelf.slot_name()
3153 )));
3154 }
3155 if let Some(attr) = zelf.get_class_attr(attr_name) {
3156 let descr_set = attr.class().slots().descr_set.load();
3157 if let Some(descriptor) = descr_set {
3158 return descriptor(&attr, zelf.to_owned().into(), value, vm);
3159 }
3160 }
3161 let assign = value.is_assign();
3162
3163 let _prev_value = Self::with_type_lock(vm, || {
3167 zelf.modified_inner();
3171
3172 let prev_value = if let PySetterValue::Assign(value) = value {
3173 zelf.attributes().insert(attr_name, value)
3174 } else {
3175 let prev_value = zelf.attributes().remove(attr_name);
3176 if prev_value.is_none() {
3177 return Err(vm.new_attribute_error(format!(
3178 "type object '{}' has no attribute '{}'",
3179 zelf.name(),
3180 attr_name,
3181 )));
3182 }
3183 prev_value
3184 };
3185
3186 if attr_name.as_wtf8().starts_with("__") && attr_name.as_wtf8().ends_with("__") {
3189 if assign {
3190 zelf.update_slot::<true>(attr_name, &vm.ctx);
3191 } else {
3192 zelf.update_slot::<false>(attr_name, &vm.ctx);
3193 }
3194 }
3195 Ok(prev_value)
3196 })?;
3197 Ok(())
3198 }
3199}
3200
3201impl Callable for PyType {
3202 type Args = FuncArgs;
3203 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
3204 vm_trace!("type_call: {:?}", zelf);
3205
3206 if zelf.is(vm.ctx.types.type_type) {
3207 let num_args = args.args.len();
3208 if num_args == 1 && args.kwargs.is_empty() {
3209 return Ok(args.args[0].obj_type());
3210 }
3211 if num_args != 3 {
3212 return Err(vm.new_type_error("type() takes 1 or 3 arguments"));
3213 }
3214 }
3215
3216 let Some(slot_new) = zelf.slots().new.load() else {
3217 return Err(
3218 vm.new_type_error(format!("cannot create '{}' instances", zelf.slots().name))
3219 );
3220 };
3221
3222 let init_args = if args.is_empty() {
3225 FuncArgs::default()
3228 } else {
3229 if zelf.slots().init.load().is_none()
3239 && !zelf.is(vm.ctx.types.type_type)
3240 && crate::types::fn_addr(slot_new)
3241 != crate::types::fn_addr(crate::types::new_wrapper as crate::types::NewFunc)
3242 {
3243 return slot_new(zelf.to_owned(), args, vm);
3244 }
3245 args.clone()
3246 };
3247
3248 let obj = slot_new(zelf.to_owned(), args, vm)?;
3249
3250 if !obj.class().fast_issubclass(zelf) {
3251 return Ok(obj);
3252 }
3253
3254 if let Some(init_method) = obj.class().slots().init.load() {
3255 init_method(&obj, init_args, vm)?;
3256 }
3257 Ok(obj)
3258 }
3259}
3260
3261impl AsNumber for PyType {
3262 fn as_number() -> &'static PyNumberMethods {
3263 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
3264 or: Some(|a, b, vm| or_(a.to_owned(), b.to_owned(), vm)),
3265 ..PyNumberMethods::NOT_IMPLEMENTED
3266 };
3267 &AS_NUMBER
3268 }
3269}
3270
3271impl Representable for PyType {
3272 #[inline]
3273 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
3274 let module = zelf.__module__(vm).ok();
3276 let module = module
3277 .as_ref()
3278 .and_then(|m| m.downcast_ref::<PyStr>())
3279 .map(|m| m.as_wtf8());
3280
3281 let repr = match module {
3282 Some(module) if module != "builtins" => {
3283 let qualname = zelf.__qualname__(vm);
3284 let qualname = qualname.downcast_ref::<PyStr>().map(|n| n.as_wtf8());
3285 let name = zelf.name();
3286 let qualname = qualname.unwrap_or_else(|| name.as_ref());
3287 format!("<class '{module}.{qualname}'>")
3288 }
3289 _ => format!("<class '{}'>", zelf.slot_name()),
3290 };
3291 Ok(repr)
3292 }
3293}
3294
3295fn get_builtin_base_with_dict(typ: &Py<PyType>, vm: &VirtualMachine) -> Option<PyTypeRef> {
3297 let mut current = Some(typ.to_owned());
3298 while let Some(t) = current {
3299 if t.is(vm.ctx.types.type_type) {
3302 return Some(t);
3303 }
3304 if t.slots.flags.has_feature(PyTypeFlags::HAS_DICT)
3306 && !t.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
3307 {
3308 return Some(t);
3309 }
3310 current = t.base.load_owned();
3311 }
3312 None
3313}
3314
3315fn get_dict_descriptor(base: &Py<PyType>, vm: &VirtualMachine) -> Option<PyObjectRef> {
3317 let dict_attr = identifier!(vm, __dict__);
3318 base.lookup_ref(dict_attr, vm)
3320}
3321
3322fn raise_dict_descriptor_error(obj: &PyObject, vm: &VirtualMachine) -> PyBaseExceptionRef {
3324 vm.new_type_error(format!(
3325 "this __dict__ descriptor does not support '{}' objects",
3326 obj.class().name()
3327 ))
3328}
3329
3330fn subtype_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
3331 let base = get_builtin_base_with_dict(obj.class(), vm);
3332
3333 if let Some(base_type) = base {
3334 if let Some(descr) = get_dict_descriptor(&base_type, vm) {
3335 vm.call_get_descriptor(&descr, &obj)
3337 .unwrap_or_else(|| Err(raise_dict_descriptor_error(&obj, vm)))
3338 } else {
3339 Err(raise_dict_descriptor_error(&obj, vm))
3340 }
3341 } else {
3342 object::object_get_dict(obj, vm).map(Into::into)
3344 }
3345}
3346
3347fn subtype_set_dict(obj: PyObjectRef, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
3349 let base = get_builtin_base_with_dict(obj.class(), vm);
3350
3351 if let Some(base_type) = base {
3352 if let Some(descr) = get_dict_descriptor(&base_type, vm) {
3353 let descr_set = descr
3355 .class()
3356 .slots
3357 .descr_set
3358 .load()
3359 .ok_or_else(|| raise_dict_descriptor_error(&obj, vm))?;
3360 descr_set(&descr, obj, value, vm)
3361 } else {
3362 Err(raise_dict_descriptor_error(&obj, vm))
3363 }
3364 } else {
3365 object::object_set_dict(obj, value, vm)?;
3367 Ok(())
3368 }
3369}
3370
3371fn subtype_get_weakref(obj: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
3373 let weakref = obj.get_weakrefs();
3375 weakref.unwrap_or_else(|| vm.ctx.none())
3376}
3377
3378fn subtype_set_weakref(obj: PyObjectRef, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
3380 Err(vm.new_attribute_error(format!(
3381 "attribute '__weakref__' of '{}' objects is not writable",
3382 obj.class().name()
3383 )))
3384}
3385
3386fn vectorcall_type(
3411 zelf_obj: &PyObject,
3412 args: Vec<PyObjectRef>,
3413 nargs: usize,
3414 kwnames: Option<&[PyObjectRef]>,
3415 vm: &VirtualMachine,
3416) -> PyResult {
3417 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
3418
3419 if zelf.is(vm.ctx.types.type_type) {
3421 let no_kwargs = kwnames.is_none_or(|kw| kw.is_empty());
3422 if nargs == 1 && no_kwargs {
3423 return Ok(args[0].obj_type());
3424 }
3425 } else if zelf.slots().call.load().is_none() && zelf.slots().new.load().is_some() {
3426 if let Some(type_vc) = zelf.slots().vectorcall.load() {
3429 return type_vc(zelf_obj, args, nargs, kwnames, vm);
3430 }
3431 }
3432
3433 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
3435 PyType::call(zelf, func_args, vm)
3436}
3437
3438pub(crate) fn init(ctx: &'static Context) {
3439 PyType::extend_class(ctx, ctx.types.type_type);
3440 ctx.types
3441 .type_type
3442 .slots
3443 .vectorcall
3444 .store(Some(vectorcall_type));
3445}
3446
3447pub(crate) fn call_slot_new(
3448 typ: &Py<PyType>,
3449 subtype: PyTypeRef,
3450 args: FuncArgs,
3451 vm: &VirtualMachine,
3452) -> PyResult {
3453 if subtype
3455 .slots
3456 .flags
3457 .has_feature(PyTypeFlags::DISALLOW_INSTANTIATION)
3458 {
3459 return Err(vm.new_type_error(format!("cannot create '{}' instances", subtype.slot_name())));
3460 }
3461
3462 let mut staticbase = subtype.clone();
3466 while staticbase
3467 .slots
3468 .new
3469 .load()
3470 .is_some_and(|f| fn_addr(f) == fn_addr(new_wrapper as NewFunc))
3471 {
3472 match staticbase.base.load_owned() {
3473 Some(base) => staticbase = base,
3474 None => break,
3475 }
3476 }
3477
3478 let typ_new = typ.slots.new.load();
3479 let staticbase_new = staticbase.slots.new.load();
3480 if typ_new.map(fn_addr) != staticbase_new.map(fn_addr) {
3481 return Err(vm.new_type_error(format!(
3482 "{}.__new__({}) is not safe, use {}.__new__()",
3483 typ.slot_name(),
3484 subtype.slot_name(),
3485 staticbase.slot_name()
3486 )));
3487 }
3488
3489 let slot_new = typ
3490 .slots
3491 .new
3492 .load()
3493 .expect("Should be able to find a new slot somewhere in the mro");
3494 slot_new(subtype, args, vm)
3495}
3496
3497pub(crate) fn or_(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
3498 union_::or_op(zelf, other, vm)
3499}
3500
3501fn take_next_base(bases: &mut [Vec<PyTypeRef>]) -> Option<PyTypeRef> {
3502 for base in bases.iter() {
3503 let head = base[0].clone();
3504 if !bases.iter().any(|x| x[1..].iter().any(|x| x.is(&head))) {
3505 for item in bases.iter_mut() {
3507 if item[0].is(&head) {
3508 item.remove(0);
3509 }
3510 }
3511
3512 return Some(head);
3513 }
3514 }
3515
3516 None
3517}
3518
3519fn linearise_mro(mut bases: Vec<Vec<PyTypeRef>>) -> Result<Vec<PyTypeRef>, String> {
3520 vm_trace!("Linearise MRO: {:?}", bases);
3521 for (i, base_mro) in bases.iter().enumerate() {
3527 let base = &base_mro[0]; for later_mro in &bases[i + 1..] {
3529 if later_mro[1..].iter().any(|cls| cls.is(base)) {
3532 return Err(format!(
3533 "Cannot create a consistent method resolution order (MRO) for bases {}",
3534 bases.iter().map(|x| x.first().unwrap()).format(", ")
3535 ));
3536 }
3537 }
3538 }
3539
3540 let mut result = vec![];
3541 while !bases.is_empty() {
3542 let head = take_next_base(&mut bases).ok_or_else(|| {
3543 format!(
3546 "Cannot create a consistent method resolution order (MRO) for bases {}",
3547 bases.iter().map(|x| x.first().unwrap()).format(", ")
3548 )
3549 })?;
3550
3551 result.push(head);
3552
3553 bases.retain(|x| !x.is_empty());
3554 }
3555 Ok(result)
3556}
3557
3558fn calculate_meta_class(
3559 metatype: PyTypeRef,
3560 bases: &[PyTypeRef],
3561 vm: &VirtualMachine,
3562) -> PyResult<PyTypeRef> {
3563 let mut winner = metatype;
3565 for base in bases {
3566 let base_type = base.class();
3567
3568 if winner.fast_issubclass(base_type) {
3570 continue;
3571 } else if base_type.fast_issubclass(&winner) {
3572 winner = base_type.to_owned();
3573 continue;
3574 }
3575
3576 let winner_is_subclass = winner.as_object().is_subclass(base_type.as_object(), vm)?;
3579 if winner_is_subclass {
3580 continue;
3581 }
3582
3583 let base_type_is_subclass = base_type.as_object().is_subclass(winner.as_object(), vm)?;
3584 if base_type_is_subclass {
3585 winner = base_type.to_owned();
3586 continue;
3587 }
3588
3589 return Err(vm.new_type_error(
3590 "metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass \
3591 of the metaclasses of all its bases",
3592 ));
3593 }
3594 Ok(winner)
3595}
3596
3597fn type_is_subtype_base_chain(a: &Py<PyType>, b: &Py<PyType>, vm: &VirtualMachine) -> bool {
3598 let mut current = Some(a);
3599 while let Some(typ) = current {
3600 if typ.is(b) {
3601 return true;
3602 }
3603 current = typ.base.deref();
3604 }
3605 b.is(vm.ctx.types.object_type)
3606}
3607
3608fn mro_implementation(typ: &Py<PyType>, vm: &VirtualMachine) -> PyResult<Vec<PyTypeRef>> {
3609 for base in typ.bases.read().as_slice() {
3610 if base.mro.read().is_empty() {
3611 return Err(vm.new_type_error(format!(
3612 "Cannot extend an incomplete type '{}'",
3613 base.name()
3614 )));
3615 }
3616 }
3617 let mut mro =
3618 PyType::resolve_mro(typ.bases.read().as_slice()).map_err(|msg| vm.new_type_error(msg))?;
3619 mro.insert(0, typ.to_owned());
3620 Ok(mro)
3621}
3622
3623fn mro_check(typ: &Py<PyType>, mro: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<()> {
3624 let solid = solid_base(typ, vm).to_owned();
3625 for obj in mro {
3626 let Some(base) = obj.downcast_ref::<PyType>() else {
3627 return Err(vm.new_type_error(format!(
3628 "mro() returned a non-class ('{}')",
3629 obj.class().name()
3630 )));
3631 };
3632 if !is_subtype_with_mro(&solid.mro.read(), &solid, solid_base(base, vm)) {
3633 return Err(vm.new_type_error(format!(
3634 "mro() returned base with unsuitable layout ('{}')",
3635 base.slot_name()
3636 )));
3637 }
3638 }
3639 Ok(())
3640}
3641
3642fn mro_invoke(typ: &Py<PyType>, vm: &VirtualMachine) -> PyResult<Vec<PyTypeRef>> {
3643 let custom = !typ.class().is(vm.ctx.types.type_type);
3644 if !custom {
3645 return mro_implementation(typ, vm);
3646 }
3647
3648 let mro_result = vm.call_special_method(typ.as_object(), identifier!(vm, mro), ())?;
3649 let items: Vec<PyObjectRef> = mro_result.try_to_value(vm)?;
3650 if items.is_empty() {
3651 return Err(vm.new_type_error("type MRO must not be empty"));
3652 }
3653 mro_check(typ, &items, vm)?;
3654 items
3655 .into_iter()
3656 .map(|obj| {
3657 obj.downcast::<PyType>().map_err(|obj| {
3658 vm.new_type_error(format!(
3659 "mro() returned a non-class ('{}')",
3660 obj.class().name()
3661 ))
3662 })
3663 })
3664 .collect()
3665}
3666
3667fn mro_internal(typ: &Py<PyType>, vm: &VirtualMachine) -> PyResult<i32> {
3668 let old_ptr = typ.mro.read().as_ptr();
3669 let new_mro = mro_invoke(typ, vm)?;
3670 if typ.mro.read().as_ptr() != old_ptr {
3671 return Ok(0);
3672 }
3673 *typ.mro.write() = new_mro;
3674 typ.modified();
3675 Ok(1)
3676}
3677
3678fn shape_differs(t1: &Py<PyType>, t2: &Py<PyType>) -> bool {
3680 t1.slots.basicsize != t2.slots.basicsize || t1.slots.itemsize != t2.slots.itemsize
3681}
3682
3683fn solid_base<'a>(typ: &'a Py<PyType>, vm: &VirtualMachine) -> &'a Py<PyType> {
3684 let base = if let Some(base) = typ.base.deref() {
3685 solid_base(base, vm)
3686 } else {
3687 vm.ctx.types.object_type
3688 };
3689
3690 if shape_differs(typ, base) { typ } else { base }
3691}
3692
3693fn best_base<'a>(bases: &'a [PyTypeRef], vm: &VirtualMachine) -> PyResult<&'a Py<PyType>> {
3694 let mut base: Option<&Py<PyType>> = None;
3695 let mut winner: Option<&Py<PyType>> = None;
3696
3697 for base_i in bases {
3698 if !base_i.slots.flags.has_feature(PyTypeFlags::BASETYPE) {
3704 return Err(vm.new_type_error(format!(
3705 "type '{}' is not an acceptable base type",
3706 base_i.slot_name()
3707 )));
3708 }
3709
3710 let candidate = solid_base(base_i, vm);
3711 if winner.is_none() {
3712 winner = Some(candidate);
3713 base = Some(&**base_i);
3714 } else if winner.unwrap().fast_issubclass(candidate) {
3715 } else if candidate.fast_issubclass(winner.unwrap()) {
3717 winner = Some(candidate);
3718 base = Some(&**base_i);
3719 } else {
3720 return Err(vm.new_type_error("multiple bases have instance layout conflict"));
3721 }
3722 }
3723
3724 debug_assert!(base.is_some());
3725 Ok(base.unwrap())
3726}
3727
3728fn type_has_dict(typ: &Py<PyType>) -> bool {
3729 typ.slots.flags.has_feature(PyTypeFlags::HAS_DICT)
3730}
3731
3732fn type_has_weakref(typ: &Py<PyType>) -> bool {
3733 typ.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF)
3734}
3735
3736fn compatible_with_base(child: &Py<PyType>) -> bool {
3739 let Some(parent) = child.base.deref() else {
3740 return false;
3741 };
3742 child.slots.basicsize == parent.slots.basicsize
3743 && child.slots.itemsize == parent.slots.itemsize
3744 && child.slots.member_count == parent.slots.member_count
3745 && type_has_dict(child) == type_has_dict(parent)
3746 && type_has_weakref(child) == type_has_weakref(parent)
3747}
3748
3749fn layout_solid_base(mut typ: &Py<PyType>) -> &Py<PyType> {
3751 while compatible_with_base(typ) {
3752 typ = typ.base.deref().unwrap();
3753 }
3754 typ
3755}
3756
3757fn same_slots_added(a: &Py<PyType>, b: &Py<PyType>) -> bool {
3760 if a.slots.basicsize != b.slots.basicsize
3761 || a.slots.itemsize != b.slots.itemsize
3762 || a.slots.member_count != b.slots.member_count
3763 || type_has_dict(a) != type_has_dict(b)
3764 || type_has_weakref(a) != type_has_weakref(b)
3765 {
3766 return false;
3767 }
3768 match (
3769 a.heaptype_ext.as_ref().and_then(|e| e.slots.as_ref()),
3770 b.heaptype_ext.as_ref().and_then(|e| e.slots.as_ref()),
3771 ) {
3772 (Some(x), Some(y)) => {
3773 x.as_slice().len() == y.as_slice().len()
3774 && x.as_slice()
3775 .iter()
3776 .zip(y.as_slice().iter())
3777 .all(|(p, q)| p.as_wtf8() == q.as_wtf8())
3778 }
3779 _ => true,
3780 }
3781}
3782
3783pub(crate) fn compatible_for_assignment(
3789 old_to: &Py<PyType>,
3790 new_to: &Py<PyType>,
3791 attr: &str,
3792 vm: &VirtualMachine,
3793) -> PyResult<()> {
3794 let newbase = layout_solid_base(new_to);
3795 let oldbase = layout_solid_base(old_to);
3796 let bases_equal = match (newbase.base.deref(), oldbase.base.deref()) {
3797 (Some(x), Some(y)) => x.is(y),
3798 (None, None) => true,
3799 _ => false,
3800 };
3801 let compatible = newbase.is(oldbase) || (bases_equal && same_slots_added(newbase, oldbase));
3802 if compatible {
3803 return Ok(());
3804 }
3805 Err(vm.new_type_error(format!(
3806 "{attr} assignment: '{}' object layout differs from '{}'",
3807 new_to.name(),
3808 old_to.name()
3809 )))
3810}
3811
3812fn mangle_name(class_name: &str, name: &str) -> String {
3815 if !name.starts_with("__") || name.ends_with("__") || name.contains('.') {
3817 return name.to_string();
3818 }
3819 let class_name = class_name.trim_start_matches('_');
3821 format!("_{class_name}{name}")
3822}
3823
3824#[cfg(test)]
3825mod tests {
3826 use super::*;
3827
3828 fn map_ids(obj: Result<Vec<PyTypeRef>, String>) -> Result<Vec<usize>, String> {
3829 Ok(obj?.into_iter().map(|x| x.get_id()).collect())
3830 }
3831
3832 #[test]
3833 fn linearise() {
3834 let context = Context::genesis();
3835 let object = context.types.object_type.to_owned();
3836 let type_type = context.types.type_type.to_owned();
3837
3838 let a = PyType::new_heap(
3839 "A",
3840 vec![object.clone()],
3841 PyAttributes::default(),
3842 Default::default(),
3843 type_type.clone(),
3844 context,
3845 )
3846 .unwrap();
3847 let b = PyType::new_heap(
3848 "B",
3849 vec![object.clone()],
3850 PyAttributes::default(),
3851 Default::default(),
3852 type_type,
3853 context,
3854 )
3855 .unwrap();
3856
3857 assert_eq!(
3858 map_ids(linearise_mro(vec![
3859 vec![object.clone()],
3860 vec![object.clone()]
3861 ])),
3862 map_ids(Ok(vec![object.clone()]))
3863 );
3864 assert_eq!(
3865 map_ids(linearise_mro(vec![
3866 vec![a.clone(), object.clone()],
3867 vec![b.clone(), object.clone()],
3868 ])),
3869 map_ids(Ok(vec![a, b, object]))
3870 );
3871 }
3872}