Skip to main content

rustpython_vm/object/
core.rs

1//! Essential types for object models
2//!
3//! +-------------------------+--------------+-----------------------+
4//! |       Management        |       Typed      |      Untyped      |
5//! +-------------------------+------------------+-------------------+
6//! | Interpreter-independent | [`Py<T>`]        | [`PyObject`]      |
7//! | Reference-counted       | [`PyRef<T>`]     | [`PyObjectRef`]   |
8//! | Weak                    | [`PyWeakRef<T>`] | [`PyRef<PyWeak>`] |
9//! +-------------------------+--------------+-----------------------+
10//!
11//! [`PyRef<PyWeak>`] may looking like to be called as PyObjectWeak by the rule,
12//! but not to do to remember it is a PyRef object.
13use super::{
14    PyAtomicRef,
15    ext::{AsObject, PyRefExact, PyResult},
16    payload::PyPayload,
17};
18use crate::object::traverse_object::PyObjVTable;
19use crate::{
20    builtins::{PyDictRef, PyTuple, PyTupleRef, PyType, PyTypeRef, type_::PyTypeTupleRef},
21    common::{
22        atomic::{Ordering, PyAtomic, Radium},
23        linked_list::{Link, Pointers},
24        lock::PyRwLock,
25        refcount::RefCount,
26    },
27    vm::VirtualMachine,
28};
29use crate::{
30    class::StaticType,
31    object::traverse::{MaybeTraverse, Traverse, TraverseFn},
32};
33
34use alloc::fmt;
35
36use core::{
37    any::TypeId,
38    borrow::Borrow,
39    cell::UnsafeCell,
40    marker::PhantomData,
41    mem::ManuallyDrop,
42    num::NonZeroUsize,
43    ops::Deref,
44    ptr::{self, NonNull},
45};
46
47// so, PyObjectRef is basically equivalent to `PyRc<Py<dyn PyObjectPayload>>`, except it's
48// only one pointer in width rather than 2. We do that by manually creating a vtable, and putting
49// a &'static reference to it inside the `PyRc` rather than adjacent to it, like trait objects do.
50// This can lead to faster code since there's just less data to pass around, as well as because of
51// some weird stuff with trait objects, alignment, and padding.
52//
53// So, every type has an alignment, which means that if you create a value of it it's location in
54// memory has to be a multiple of it's alignment. e.g., a type with alignment 4 (like i32) could be
55// at 0xb7befbc0, 0xb7befbc4, or 0xb7befbc8, but not 0xb7befbc2. If you have a struct and there are
56// 2 fields whose sizes/alignments don't perfectly fit in with each other, e.g.:
57// +-------------+-------------+---------------------------+
58// |     u16     |      ?      |            i32            |
59// | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 |
60// +-------------+-------------+---------------------------+
61// There has to be padding in the space between the 2 fields. But, if that field is a trait object
62// (like `dyn PyObjectPayload`) we don't *know* how much padding there is between the `payload`
63// field and the previous field. So, Rust has to consult the vtable to know the exact offset of
64// `payload` in `Py<dyn PyObjectPayload>`, which has a huge performance impact when *every
65// single payload access* requires a vtable lookup. Thankfully, we're able to avoid that because of
66// the way we use PyObjectRef, in that whenever we want to access the payload we (almost) always
67// access it from a generic function. So, rather than doing
68//
69// - check vtable for payload offset
70// - get offset in Py struct
71// - call as_any() method of PyObjectPayload
72// - call downcast_ref() method of Any
73// we can just do
74// - check vtable that typeid matches
75// - pointer cast directly to *const Py<T>
76//
77// and at that point the compiler can know the offset of `payload` for us because **we've given it a
78// concrete type to work with before we ever access the `payload` field**
79
80/// A type to just represent "we've erased the type of this object, cast it before you use it"
81#[derive(Debug)]
82pub(super) struct Erased;
83
84/// Trashcan mechanism to limit recursive deallocation depth (Py_TRASHCAN).
85/// Without this, deeply nested structures (e.g. 200k-deep list) cause stack overflow
86/// during deallocation because each level adds a stack frame.
87mod trashcan {
88    use core::cell::Cell;
89
90    /// Maximum nesting depth for deallocation before deferring.
91    /// CPython uses UNWIND_NO_NESTING = 50.
92    const TRASHCAN_LIMIT: usize = 50;
93
94    type DeallocFn = unsafe fn(*mut super::PyObject);
95    type DeallocQueue = Vec<(*mut super::PyObject, DeallocFn)>;
96
97    /// Per-thread trashcan state. Depth and deferral queue live in one
98    /// thread-local so a single access reaches both fields (one `_tlv_get_addr`
99    /// on platforms where thread-local access is a function call). Both fields
100    /// are `Cell`-based so reentrant deallocation (nested `begin`/`end` triggered
101    /// by draining deferred objects) never holds an outstanding borrow.
102    struct Trashcan {
103        depth: Cell<usize>,
104        queue: Cell<DeallocQueue>,
105    }
106
107    thread_local! {
108        static TRASHCAN: Trashcan = const {
109            Trashcan {
110                depth: Cell::new(0),
111                queue: Cell::new(Vec::new()),
112            }
113        };
114    }
115
116    /// Try to begin deallocation. Returns true if we should proceed,
117    /// false if the object was deferred (depth exceeded).
118    #[inline]
119    pub(super) unsafe fn begin(
120        obj: *mut super::PyObject,
121        dealloc: unsafe fn(*mut super::PyObject),
122    ) -> bool {
123        TRASHCAN.with(|t| {
124            let depth = t.depth.get();
125            if depth >= TRASHCAN_LIMIT {
126                // Depth exceeded: defer this deallocation
127                let mut queue = t.queue.take();
128                queue.push((obj, dealloc));
129                t.queue.set(queue);
130                false
131            } else {
132                t.depth.set(depth + 1);
133                true
134            }
135        })
136    }
137
138    /// End deallocation and process any deferred objects if at outermost level.
139    #[inline]
140    pub(super) unsafe fn end() {
141        TRASHCAN.with(|t| {
142            let depth = t.depth.get();
143            debug_assert!(depth > 0, "trashcan::end called without matching begin");
144            let depth = depth - 1;
145            t.depth.set(depth);
146            if depth != 0 {
147                return;
148            }
149            // Process deferred deallocations iteratively. The queue is set back
150            // before each `dealloc` call so a reentrant `begin` can push freely.
151            loop {
152                let next = {
153                    let mut queue = t.queue.take();
154                    let item = queue.pop();
155                    t.queue.set(queue);
156                    item
157                };
158                if let Some((obj, dealloc)) = next {
159                    unsafe { dealloc(obj) };
160                } else {
161                    break;
162                }
163            }
164        })
165    }
166}
167
168/// Default dealloc: handles __del__, weakref clearing, tp_clear, and memory free.
169/// Equivalent to subtype_dealloc.
170pub(super) unsafe fn default_dealloc<T: PyPayload>(obj: *mut PyObject) {
171    let obj_ref = unsafe { &*(obj as *const PyObject) };
172    if let Err(()) = obj_ref.drop_slow_inner() {
173        return; // resurrected by __del__
174    }
175
176    // Only tracked objects take the trashcan recursion guard and untrack path.
177    // Untracked objects either own no children (int, float, str, ...) or, like
178    // non-escaped frames, are released at interpreter depth with at most one
179    // unguarded link before their tracked children (dicts, functions, code)
180    // re-enter guarded deallocation, so recursion stays bounded. A frame stored
181    // in an object graph is forced to escape, becoming tracked and guarded here.
182    // Read once and reuse for both gates below.
183    let tracked = obj_ref.is_gc_tracked();
184
185    // Trashcan: limit recursive deallocation depth to prevent stack overflow
186    if tracked && !unsafe { trashcan::begin(obj, default_dealloc::<T>) } {
187        return; // deferred to queue
188    }
189
190    let vtable = obj_ref.0.vtable;
191
192    // Untrack from GC BEFORE deallocation.
193    // Must happen before memory is freed because intrusive list removal
194    // reads the object's gc_pointers (prev/next).
195    if tracked {
196        let ptr = unsafe { NonNull::new_unchecked(obj) };
197        unsafe {
198            crate::gc_state::gc_state().untrack_object(ptr);
199        }
200        // Verify untrack cleared the tracked flag and generation
201        debug_assert!(
202            !obj_ref.is_gc_tracked(),
203            "object still tracked after untrack_object"
204        );
205        debug_assert_eq!(
206            obj_ref.gc_generation(),
207            crate::object::GC_UNTRACKED,
208            "gc_generation not reset after untrack_object"
209        );
210    }
211
212    // Extract child references to break circular refs (tp_clear), then drop
213    // them. Some payloads (e.g. FrameObject) drop children in place inside clear_fn
214    // instead of extracting them, so user code (`__del__`) may run here.
215    let mut edges = Vec::new();
216    if let Some(clear_fn) = vtable.clear {
217        unsafe { clear_fn(obj, &mut edges) };
218    }
219    // Drop extracted child references - may trigger recursive destruction.
220    drop(edges);
221
222    // Try to store in freelist for reuse. This must happen AFTER clear_fn and
223    // after the extracted-children drop: both can run user code (`__del__`)
224    // that allocates, and `PyRef::new_ref` pops from the same thread-local
225    // freelist. If the husk were already in the freelist, a reentrant
226    // allocation could pop it and write a fresh payload into it while clear_fn
227    // still holds a `&mut` borrow of that payload (aliasing UB). Pushing only
228    // once no borrows into the payload can be live closes that window.
229    // Only exact base types (not heaptype or structseq subtypes) go into the freelist.
230    // Published objects (e.g. a tuple stored as a type attribute) must skip the
231    // freelist: `PyRef::new_ref` would reuse the slot and overwrite the refcount
232    // word with a non-atomic write, racing a reader's atomic try-incref. Route
233    // them through `Py::dealloc` instead, whose QSBR hook defers the actual
234    // memory free until readers can no longer observe it.
235    let typ = obj_ref.class();
236    let pushed = if T::HAS_FREELIST
237        && typ.heaptype_ext.is_none()
238        && core::ptr::eq(typ, T::class(crate::vm::Context::genesis()))
239        && !obj_ref.0.ref_count.is_published()
240    {
241        if let Some(ext) = obj_ref.0.ext_ref() {
242            if obj_ref
243                .class()
244                .slots
245                .flags
246                .has_feature(crate::types::PyTypeFlags::HAS_DICT)
247            {
248                drop(ext.dict.replace(None));
249            }
250            for slot in obj_ref.0.slot_cells() {
251                drop(slot.store(None));
252            }
253        }
254        unsafe { T::freelist_push(obj) }
255    } else {
256        false
257    };
258
259    if !pushed {
260        // Deallocate the object memory (handles ObjExt prefix if present)
261        unsafe { Py::dealloc(obj as *mut Py<T>) };
262    }
263
264    // Trashcan: decrement depth and process deferred objects at outermost level
265    if tracked {
266        unsafe { trashcan::end() };
267    }
268}
269pub(super) unsafe fn debug_obj<T: PyPayload + core::fmt::Debug>(
270    x: &PyObject,
271    f: &mut fmt::Formatter<'_>,
272) -> fmt::Result {
273    let x = unsafe { &*(x as *const PyObject as *const Py<T>) };
274    write!(f, "[PyObject {:?}]", x.payload)
275}
276
277/// Call `try_trace` on payload
278pub(super) unsafe fn try_traverse_obj<T: PyPayload>(x: &PyObject, tracer_fn: &mut TraverseFn<'_>) {
279    let x = unsafe { &*(x as *const PyObject as *const Py<T>) };
280    let payload = &x.payload;
281    payload.try_traverse(tracer_fn)
282}
283
284/// Call `try_clear` on payload to extract child references (tp_clear)
285pub(super) unsafe fn try_clear_obj<T: PyPayload>(x: *mut PyObject, out: &mut Vec<PyObjectRef>) {
286    let x = unsafe { &mut *(x as *mut Py<T>) };
287    x.payload.try_clear(out);
288}
289
290bitflags::bitflags! {
291    /// GC bits for free-threading support (like ob_gc_bits in Py_GIL_DISABLED)
292    /// These bits are stored in a separate atomic field for lock-free access.
293    /// See Include/internal/pycore_gc.h
294    #[derive(Copy, Clone, Debug, Default)]
295    pub(crate) struct GcBits: u8 {
296        /// Tracked by the GC
297        const TRACKED = 1 << 0;
298        /// tp_finalize was called (prevents __del__ from being called twice)
299        const FINALIZED = 1 << 1;
300        /// Object is unreachable (during GC collection)
301        const UNREACHABLE = 1 << 2;
302        /// Object is frozen (immutable)
303        const FROZEN = 1 << 3;
304        /// Memory the object references is shared between multiple threads
305        /// and needs special handling when freeing due to possible in-flight lock-free reads
306        const SHARED = 1 << 4;
307        /// Memory of the object itself is shared between multiple threads
308        /// Objects with this bit that are GC objects will automatically be delay-freed
309        const SHARED_INLINE = 1 << 5;
310        /// Use deferred reference counting
311        const DEFERRED = 1 << 6;
312        /// In the candidate set of the collection that is running, so its
313        /// `gc_refs` is meaningful. `_PyGC_PREV_MASK_COLLECTING`.
314        const COLLECTING = 1 << 7;
315    }
316}
317
318/// GC generation constants
319pub(crate) const GC_UNTRACKED: u8 = 0xFF;
320pub(crate) const GC_PERMANENT: u8 = 3;
321/// Width of an interpreter's `gc_owner` tag.
322///
323/// Sized to the padding the header alignment already forces, so the tag costs
324/// no space on either pointer width. Running out of tags is not an error: an
325/// interpreter that gets none uses [`GC_NO_OWNER`] and its objects stay
326/// collectable by every interpreter, which is how they behaved before tagging.
327pub(crate) type GcOwner = u16;
328
329/// `gc_owner` of an object that belongs to no single interpreter: everything
330/// the shared context allocates, and anything allocated with no interpreter
331/// current. Every interpreter collects these.
332pub(crate) const GC_NO_OWNER: GcOwner = 0;
333
334/// `gc_refs` of an object a running collection has proved reachable. One past
335/// the largest count [`PyObject::start_gc_refs`] stores, so no real count can
336/// be taken for it.
337pub(crate) const GC_REACHABLE: u32 = u32::MAX;
338
339/// Link implementation for GC intrusive linked list tracking
340pub(crate) struct GcLink;
341
342// SAFETY: PyObject (Py<Erased>) is heap-allocated and pinned in memory
343// once created. gc_pointers is at a fixed offset in Py.
344unsafe impl Link for GcLink {
345    type Handle = NonNull<PyObject>;
346    type Target = PyObject;
347
348    fn as_raw(handle: &NonNull<PyObject>) -> NonNull<PyObject> {
349        *handle
350    }
351
352    unsafe fn from_raw(ptr: NonNull<PyObject>) -> NonNull<PyObject> {
353        ptr
354    }
355
356    unsafe fn pointers(target: NonNull<PyObject>) -> NonNull<Pointers<PyObject>> {
357        let inner_ptr = target.as_ptr() as *mut Py<Erased>;
358        unsafe { NonNull::new_unchecked(&raw mut (*inner_ptr).gc_pointers) }
359    }
360}
361
362/// Extension fields for objects that need dict or member slots.
363/// Allocated as a prefix before Py when needed (prefix allocation pattern).
364/// Access via `Py::ext_ref()` using negative offset from the object pointer.
365///
366/// align(8) ensures size_of::<ObjExt>() is always a multiple of 8,
367/// so the offset from Layout::extend equals size_of::<ObjExt>() for any
368/// Py<T> alignment (important on wasm32 where pointers are 4 bytes
369/// but some payloads like PyWeak have align 8 due to i64 fields).
370#[repr(C, align(8))]
371pub(super) struct ObjExt {
372    /// Always present. The dict cell is its first field, so
373    /// `dict_member_offset` addresses that pointer. Null when the type has
374    /// no dict slot.
375    pub(super) dict: InstanceDict,
376}
377
378impl ObjExt {
379    fn new(dict: Option<PyDictRef>, has_dict: bool, inline_values: bool) -> Self {
380        Self {
381            dict: if has_dict {
382                InstanceDict::from_opt(dict, inline_values)
383            } else {
384                InstanceDict::from_opt(None, false)
385            },
386        }
387    }
388}
389
390impl fmt::Debug for ObjExt {
391    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392        write!(f, "[ObjExt]")
393    }
394}
395
396/// Precomputed offset constants for prefix allocation.
397/// `ObjExt` and `WeakRefList` are align(8) and their sizes are multiples of 8,
398/// so `Layout::extend` adds no padding between them. Slot cells are packed
399/// immediately in front of `ObjExt`; any alignment padding is before the cells.
400const EXT_OFFSET: usize = core::mem::size_of::<ObjExt>();
401
402/// Byte offset of member cell `index` from the start of `Py`.
403/// Cells live in front of the object, so the offset is negative.
404///
405/// `ObjExt` stays flush with `Py`. A subclass that adds `__weakref__`
406/// puts that list in front of the cells, so this offset does not move.
407pub(crate) fn slot_member_offset(index: usize) -> isize {
408    // Cell 0 sits directly in front of ObjExt. Higher indexes extend further
409    // forward, so a base class offset stays valid on a subclass with more slots.
410    let cell = core::mem::size_of::<PyAtomicRef<Option<PyObject>>>();
411    -((EXT_OFFSET + (index + 1) * cell) as isize)
412}
413
414fn slot_region_layout(member_count: usize) -> Option<core::alloc::Layout> {
415    if member_count == 0 {
416        return None;
417    }
418    let cell = core::mem::size_of::<PyAtomicRef<Option<PyObject>>>();
419    let bytes = member_count * cell;
420    let align = core::mem::align_of::<ObjExt>();
421    // Padding goes in front of the cells so cell 0 stays flush with ObjExt.
422    // On wasm32 a pointer is 4 bytes and ObjExt is align 8, so an odd count
423    // would otherwise leave a gap where cell 0 is supposed to be.
424    let pad = (align - bytes % align) % align;
425    Some(core::alloc::Layout::from_size_align(pad + bytes, align).unwrap())
426}
427const WEAKREF_OFFSET: usize = core::mem::size_of::<WeakRefList>();
428
429/// Distance from `Py` back to a `WeakRefList`.
430/// Layout: `[WeakRefList?][slots?][ObjExt?][Py]`.
431fn weakref_prefix_offset(has_ext: bool, member_count: usize) -> usize {
432    let ext = if has_ext { EXT_OFFSET } else { 0 };
433    let slots = slot_region_layout(member_count).map_or(0, |layout| layout.size());
434    ext + slots + WEAKREF_OFFSET
435}
436
437const _: () =
438    assert!(core::mem::size_of::<ObjExt>().is_multiple_of(core::mem::align_of::<ObjExt>()));
439const _: () = assert!(core::mem::align_of::<ObjExt>() >= core::mem::align_of::<Py<()>>());
440const _: () = assert!(
441    core::mem::size_of::<WeakRefList>().is_multiple_of(core::mem::align_of::<WeakRefList>())
442);
443const _: () = assert!(core::mem::align_of::<WeakRefList>() >= core::mem::align_of::<Py<()>>());
444
445/// This is an actual python object. It consists of a `typ` which is the
446/// python class, and carries some rust payload optionally. This rust
447/// payload can be a rust float or rust int in case of float and int objects.
448#[repr(C)]
449pub struct Py<T> {
450    pub(super) ref_count: RefCount,
451    pub(super) vtable: &'static PyObjVTable,
452    /// GC bits for free-threading (like ob_gc_bits)
453    pub(super) gc_bits: PyAtomic<u8>,
454    /// GC generation index (0-2=gen, GC_PERMANENT=permanent, GC_UNTRACKED=not tracked).
455    /// Uses PyAtomic for interior mutability (writes happen through &self under list locks).
456    pub(super) gc_generation: PyAtomic<u8>,
457    /// Interpreter that tracked this object, or `GC_NO_OWNER`. Written by
458    /// `track_object`; read to scope a collection to one interpreter.
459    /// Sits in what would otherwise be padding, so it costs no space.
460    pub(super) gc_owner: PyAtomic<GcOwner>,
461    /// The count a running collection is working with: the strong count with
462    /// the references held from inside the candidate set taken off, or
463    /// [`GC_REACHABLE`] once the object has been proved reachable. Only
464    /// meaningful while `gc_bits` has [`GcBits::COLLECTING`].
465    pub(super) gc_refs: PyAtomic<u32>,
466    /// Intrusive linked list pointers for GC generational tracking
467    pub(super) gc_pointers: Pointers<PyObject>,
468
469    pub(super) typ: PyAtomicRef<PyType>, // __class__ member
470
471    pub(crate) payload: T,
472}
473pub const SIZEOF_PYOBJECT_HEAD: usize = core::mem::size_of::<Py<()>>();
474
475/// Byte offset of the payload inside `Py<T>`.
476#[must_use]
477#[inline]
478pub const fn payload_offset<T>() -> usize {
479    core::mem::offset_of!(Py<T>, payload)
480}
481
482/// Byte offset of the instance-dict pointer from the start of `Py`.
483///
484/// The pointer is the first field of `ObjExt`, which sits immediately in front
485/// of `Py`. The cell owns the dict.
486#[must_use]
487#[inline]
488pub const fn dict_member_offset() -> isize {
489    -(core::mem::size_of::<ObjExt>() as isize)
490}
491
492// ref_count, vtable, gc_pointers (two) and typ are one word each; the gc bits,
493// generation, owner and refs take eight bytes between them. A 64-bit header had
494// those eight as the padding its alignment forces, so they cost it nothing; a
495// 32-bit header spends a word on them. Adding to that group is free only while
496// this holds.
497const _: () = assert!(SIZEOF_PYOBJECT_HEAD == 5 * core::mem::size_of::<usize>() + 8);
498
499// `Py::drop_fields` names `payload` and `typ`; it is only complete while
500// every other field stays trivially destructible.
501const _: () = assert!(
502    !core::mem::needs_drop::<RefCount>()
503        && !core::mem::needs_drop::<&'static PyObjVTable>()
504        && !core::mem::needs_drop::<PyAtomic<u8>>()
505        && !core::mem::needs_drop::<PyAtomic<u32>>()
506        && !core::mem::needs_drop::<PyAtomic<GcOwner>>()
507        && !core::mem::needs_drop::<Pointers<PyObject>>()
508);
509
510impl<T> Py<T> {
511    /// Read type flags and member_count via raw pointers to avoid Stacked Borrows
512    /// violations during bootstrap, where type objects have self-referential typ pointers.
513    #[inline(always)]
514    fn read_type_flags(&self) -> (crate::types::PyTypeFlags, usize) {
515        let typ_ptr = self.typ.load_raw();
516        let slots = unsafe { core::ptr::addr_of!((*typ_ptr).payload.slots) };
517        // SAFETY: `flags` is the live atomic word on this type object. `as_bits`
518        // is the crate accessor for that word. The type object is live. This
519        // load does not form a reference to the type object itself.
520        let bits = unsafe {
521            (*core::ptr::addr_of!((*slots).flags))
522                .as_bits()
523                .load(core::sync::atomic::Ordering::Acquire)
524        };
525        let member_count = unsafe { core::ptr::addr_of!((*slots).member_count).read() };
526        (
527            crate::types::PyTypeFlags::from_bits_truncate(bits),
528            member_count,
529        )
530    }
531
532    /// Access the ObjExt prefix at a negative offset from this Py.
533    /// Returns None if this object was allocated without dict/slots.
534    ///
535    /// Layout: [WeakRefList?][slots?][ObjExt?][Py]
536    /// `ObjExt` is always immediately in front of `Py`.
537    #[inline(always)]
538    pub(super) fn ext_ref(&self) -> Option<&ObjExt> {
539        let (flags, member_count) = self.read_type_flags();
540        let has_ext = flags.contains(&crate::types::PyTypeFlags::HAS_DICT) || member_count > 0;
541        if !has_ext {
542            return None;
543        }
544        let self_addr = (self as *const Self as *const u8).addr();
545        let ext_ptr =
546            core::ptr::with_exposed_provenance::<ObjExt>(self_addr.wrapping_sub(EXT_OFFSET));
547        Some(unsafe { &*ext_ptr })
548    }
549
550    /// Member cells are the pointer array immediately before [`ObjExt`].
551    /// Layout: `[WeakRefList?][PyAtomicRef<Option<PyObject>>; N][ObjExt?][Py]`.
552    pub(super) fn slot_cells(&self) -> &[PyAtomicRef<Option<PyObject>>] {
553        let Some(ext) = self.ext_ref() else {
554            return &[];
555        };
556        let (_, member_count) = self.read_type_flags();
557        if member_count == 0 {
558            return &[];
559        }
560        let cell = core::mem::size_of::<PyAtomicRef<Option<PyObject>>>();
561        // Index 0 is the cell adjacent to ObjExt; index i is i cells before it.
562        let first = (ext as *const ObjExt)
563            .addr()
564            .wrapping_sub(member_count * cell);
565        let ptr = core::ptr::with_exposed_provenance::<PyAtomicRef<Option<PyObject>>>(first);
566        unsafe { core::slice::from_raw_parts(ptr, member_count) }
567    }
568
569    /// Access the WeakRefList prefix at a fixed negative offset from this Py.
570    /// Returns None if the type does not support weakrefs.
571    ///
572    /// Layout: [WeakRefList?][slots?][ObjExt?][Py]
573    /// The list sits in front of the slot cells so adding it on a subclass
574    /// does not move inherited member offsets.
575    #[inline(always)]
576    pub(super) fn weakref_list_ref(&self) -> Option<&WeakRefList> {
577        let (flags, member_count) = self.read_type_flags();
578        if !flags.contains(&crate::types::PyTypeFlags::HAS_WEAKREF) {
579            return None;
580        }
581        let has_ext = flags.contains(&crate::types::PyTypeFlags::HAS_DICT) || member_count > 0;
582        let self_addr = (self as *const Self as *const u8).addr();
583        let ptr = core::ptr::with_exposed_provenance::<WeakRefList>(
584            self_addr.wrapping_sub(weakref_prefix_offset(has_ext, member_count)),
585        );
586        Some(unsafe { &*ptr })
587    }
588}
589
590unsafe impl Traverse for PyObject {
591    /// DO notice that call `trace` on `PyObject` means apply `tracer_fn` on `PyObject`'s children,
592    /// not like call `trace` on `PyObjectRef` which apply `tracer_fn` on `PyObjectRef` itself
593    fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
594        self.0.traverse(tracer_fn)
595    }
596}
597
598// === Stripe lock for weakref list protection (WEAKREF_LIST_LOCK) ===
599
600#[cfg(feature = "threading")]
601mod weakref_lock {
602    use core::sync::atomic::{AtomicU8, Ordering};
603
604    const NUM_WEAKREF_LOCKS: usize = 64;
605
606    static LOCKS: [AtomicU8; NUM_WEAKREF_LOCKS] = [const { AtomicU8::new(0) }; NUM_WEAKREF_LOCKS];
607
608    pub(super) struct WeakrefLockGuard {
609        idx: usize,
610    }
611
612    impl Drop for WeakrefLockGuard {
613        fn drop(&mut self) {
614            LOCKS[self.idx].store(0, Ordering::Release);
615        }
616    }
617
618    pub(super) fn lock(addr: usize) -> WeakrefLockGuard {
619        let idx = (addr >> 4) % NUM_WEAKREF_LOCKS;
620        loop {
621            if LOCKS[idx]
622                .compare_exchange_weak(0, 1, Ordering::Acquire, Ordering::Relaxed)
623                .is_ok()
624            {
625                return WeakrefLockGuard { idx };
626            }
627            core::hint::spin_loop();
628        }
629    }
630
631    /// Reset all weakref stripe locks after fork in child process.
632    /// Locks held by parent threads would cause infinite spin in the child.
633    #[cfg(all(unix, feature = "host_env"))]
634    pub(crate) fn reset_all_after_fork() {
635        for lock in &LOCKS {
636            lock.store(0, Ordering::Release);
637        }
638    }
639}
640
641#[cfg(not(feature = "threading"))]
642mod weakref_lock {
643    pub(super) struct WeakrefLockGuard;
644
645    impl Drop for WeakrefLockGuard {
646        fn drop(&mut self) {}
647    }
648
649    pub(super) fn lock(_addr: usize) -> WeakrefLockGuard {
650        WeakrefLockGuard
651    }
652}
653
654/// Reset weakref stripe locks after fork. Must be called before any
655/// Python code runs in the child process.
656#[cfg(all(unix, feature = "threading", feature = "host_env"))]
657pub(crate) fn reset_weakref_locks_after_fork() {
658    weakref_lock::reset_all_after_fork();
659}
660
661// === WeakRefList: inline on every object (tp_weaklist) ===
662
663#[repr(C)]
664pub(super) struct WeakRefList {
665    /// Head of the intrusive doubly-linked list of weakrefs.
666    head: PyAtomic<*mut Py<PyWeak>>,
667    /// Cached generic weakref (no callback, exact weakref type).
668    /// Matches try_reuse_basic_ref in weakrefobject.c.
669    generic: PyAtomic<*mut Py<PyWeak>>,
670}
671
672impl fmt::Debug for WeakRefList {
673    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
674        f.debug_struct("WeakRefList").finish_non_exhaustive()
675    }
676}
677
678/// Unlink a node from the weakref list. Must be called under stripe lock.
679///
680/// # Safety
681/// `node` must be a valid pointer to a node currently in the list owned by `wrl`.
682unsafe fn unlink_weakref(wrl: &WeakRefList, node: NonNull<Py<PyWeak>>) {
683    unsafe {
684        let mut ptrs = WeakLink::pointers(node);
685        let prev = ptrs.as_ref().get_prev();
686        let next = ptrs.as_ref().get_next();
687
688        if let Some(prev) = prev {
689            WeakLink::pointers(prev).as_mut().set_next(next);
690        } else {
691            // node is the head
692            wrl.head.store(
693                next.map_or(ptr::null_mut(), |p| p.as_ptr()),
694                Ordering::Relaxed,
695            );
696        }
697        if let Some(next) = next {
698            WeakLink::pointers(next).as_mut().set_prev(prev);
699        }
700
701        ptrs.as_mut().set_prev(None);
702        ptrs.as_mut().set_next(None);
703    }
704}
705
706// try_reuse_basic_ref
707unsafe fn try_reuse_weakref(ptr: *mut Py<PyWeak>) -> Option<PyRef<PyWeak>> {
708    if ptr.is_null() {
709        return None;
710    }
711    let node = unsafe { &*ptr };
712    node.ref_count
713        .safe_inc()
714        .then(|| unsafe { PyRef::from_raw(ptr) })
715}
716
717impl WeakRefList {
718    pub(super) fn new() -> Self {
719        Self {
720            head: Radium::new(ptr::null_mut()),
721            generic: Radium::new(ptr::null_mut()),
722        }
723    }
724
725    /// get_or_create_weakref
726    fn add(
727        &self,
728        obj: &PyObject,
729        cls: PyTypeRef,
730        cls_is_weakref: bool,
731        cls_is_weakproxy: bool,
732        callback: Option<PyObjectRef>,
733        dict: Option<PyDictRef>,
734    ) -> PyRef<PyWeak> {
735        let is_generic = cls_is_weakref && callback.is_none();
736        let is_generic_proxy = cls_is_weakproxy && callback.is_none();
737
738        // Try reuse under lock first (fast path, no allocation)
739        {
740            let _lock = weakref_lock::lock(obj as *const PyObject as usize);
741            let existing = if is_generic {
742                unsafe { try_reuse_weakref(self.generic.load(Ordering::Relaxed)) }
743            } else if is_generic_proxy {
744                unsafe { try_reuse_weakref(self.find_generic_proxy_ptr()) }
745            } else {
746                None
747            };
748            if let Some(existing) = existing {
749                return existing;
750            }
751        }
752
753        // Allocate OUTSIDE the stripe lock. PyRef::new_ref may trigger
754        // maybe_collect → GC → WeakRefList::clear on another object that
755        // hashes to the same stripe, which would deadlock on the spinlock.
756        let weak_payload = PyWeak {
757            pointers: Pointers::new(),
758            wr_object: Radium::new(obj as *const PyObject as *mut PyObject),
759            callback: UnsafeCell::new(callback),
760            hash: Radium::new(crate::common::hash::SENTINEL),
761        };
762        let weak = PyRef::new_ref(weak_payload, cls, dict);
763
764        // Re-acquire lock for linked list insertion
765        let _lock = weakref_lock::lock(obj as *const PyObject as usize);
766
767        // Re-check: another thread may have inserted a generic ref/proxy
768        // while we were allocating outside the lock. If so, reuse it and
769        // drop ours.
770        let existing = if is_generic {
771            unsafe { try_reuse_weakref(self.generic.load(Ordering::Relaxed)) }
772        } else if is_generic_proxy {
773            unsafe { try_reuse_weakref(self.find_generic_proxy_ptr()) }
774        } else {
775            None
776        };
777        if let Some(existing) = existing {
778            // Nullify wr_object so drop_inner won't unlink an
779            // un-inserted node (which would corrupt the list head).
780            weak.wr_object.store(ptr::null_mut(), Ordering::Relaxed);
781            return existing;
782        }
783
784        // Insert into linked list under stripe lock
785        // (insert_weakref: generic ref at head, generic proxy right after it)
786        let node_ptr = NonNull::from(&*weak);
787        let after = if is_generic {
788            None
789        } else if is_generic_proxy {
790            NonNull::new(self.generic.load(Ordering::Relaxed))
791        } else {
792            NonNull::new(self.find_generic_proxy_ptr())
793                .or_else(|| NonNull::new(self.generic.load(Ordering::Relaxed)))
794        };
795        match after {
796            Some(after) => unsafe { self.insert_after(after, node_ptr) },
797            None => unsafe { self.insert_at_head(node_ptr) },
798        }
799        if is_generic {
800            self.generic.store(node_ptr.as_ptr(), Ordering::Relaxed);
801        }
802
803        weak
804    }
805
806    unsafe fn insert_at_head(&self, node_ptr: NonNull<Py<PyWeak>>) {
807        unsafe {
808            let mut ptrs = WeakLink::pointers(node_ptr);
809            let old_head = self.head.load(Ordering::Relaxed);
810            ptrs.as_mut().set_next(NonNull::new(old_head));
811            ptrs.as_mut().set_prev(None);
812            if let Some(old_head) = NonNull::new(old_head) {
813                WeakLink::pointers(old_head)
814                    .as_mut()
815                    .set_prev(Some(node_ptr));
816            }
817            self.head.store(node_ptr.as_ptr(), Ordering::Relaxed);
818        }
819    }
820
821    unsafe fn insert_after(&self, after: NonNull<Py<PyWeak>>, node_ptr: NonNull<Py<PyWeak>>) {
822        unsafe {
823            let mut ptrs = WeakLink::pointers(node_ptr);
824            let after_next = WeakLink::pointers(after).as_ref().get_next();
825            ptrs.as_mut().set_prev(Some(after));
826            ptrs.as_mut().set_next(after_next);
827            WeakLink::pointers(after).as_mut().set_next(Some(node_ptr));
828            if let Some(next) = after_next {
829                WeakLink::pointers(next).as_mut().set_prev(Some(node_ptr));
830            }
831        }
832    }
833
834    // get_basic_refs
835    fn find_generic_proxy_ptr(&self) -> *mut Py<PyWeak> {
836        let generic_ptr = self.generic.load(Ordering::Relaxed);
837        let candidate_ptr = if let Some(generic_node) = NonNull::new(generic_ptr) {
838            unsafe { WeakLink::pointers(generic_node).as_ref().get_next() }
839                .map_or(ptr::null_mut(), |n| n.as_ptr())
840        } else {
841            self.head.load(Ordering::Relaxed)
842        };
843        match NonNull::new(candidate_ptr) {
844            Some(candidate) => {
845                let node = unsafe { candidate.as_ref() };
846                let has_callback = unsafe { (&*node.payload.callback.get()).is_some() };
847                let node_cls = node.class();
848                // PyWeakref_CheckProxy: the basic-proxy slot is reserved for
849                // the canonical proxy type; subclasses and callback-less ref
850                // subclasses must not be mistaken for it.
851                let is_proxy = node_cls.is(crate::builtins::PyWeakProxy::static_type())
852                    || node_cls.is(crate::builtins::PyWeakCallableProxy::static_type());
853                if has_callback || !is_proxy {
854                    ptr::null_mut()
855                } else {
856                    candidate_ptr
857                }
858            }
859            None => ptr::null_mut(),
860        }
861    }
862
863    /// Clear all weakrefs and call their callbacks.
864    /// Called when the owner object is being dropped.
865    // PyObject_ClearWeakRefs
866    fn clear(&self, obj: &PyObject) {
867        let obj_addr = obj as *const PyObject as usize;
868        let _lock = weakref_lock::lock(obj_addr);
869
870        // Clear generic cache
871        self.generic.store(ptr::null_mut(), Ordering::Relaxed);
872
873        // Walk the list, collecting weakrefs with callbacks
874        let mut callbacks: Vec<(PyRef<PyWeak>, PyObjectRef)> = Vec::new();
875        let mut current = NonNull::new(self.head.load(Ordering::Relaxed));
876        while let Some(node) = current {
877            let next = unsafe { WeakLink::pointers(node).as_ref().get_next() };
878
879            let wr = unsafe { node.as_ref() };
880
881            // Mark weakref as dead
882            wr.payload
883                .wr_object
884                .store(ptr::null_mut(), Ordering::Relaxed);
885
886            // Unlink from list
887            unsafe {
888                let mut ptrs = WeakLink::pointers(node);
889                ptrs.as_mut().set_prev(None);
890                ptrs.as_mut().set_next(None);
891            }
892
893            // Collect callback only if we can still acquire a strong ref.
894            if wr.ref_count.safe_inc() {
895                let wr_ref = unsafe { PyRef::from_raw(wr as *const Py<PyWeak>) };
896                let cb = unsafe { wr.payload.callback.get().replace(None) };
897                if let Some(cb) = cb {
898                    callbacks.push((wr_ref, cb));
899                }
900            }
901
902            current = next;
903        }
904        self.head.store(ptr::null_mut(), Ordering::Relaxed);
905
906        // Invoke callbacks outside the lock
907        drop(_lock);
908        for (wr, cb) in callbacks {
909            crate::vm::thread::with_vm(&cb, |vm| {
910                let _ = cb.call((wr.clone(),), vm);
911            });
912        }
913    }
914
915    /// Clear all weakrefs but DON'T call callbacks. Instead, return them for later invocation.
916    /// Used by GC to ensure ALL weakrefs are cleared BEFORE any callbacks are invoked.
917    /// handle_weakrefs() clears all weakrefs first, then invokes callbacks.
918    fn clear_for_gc_collect_callbacks(&self, obj: &PyObject) -> Vec<(PyRef<PyWeak>, PyObjectRef)> {
919        let obj_addr = obj as *const PyObject as usize;
920        let _lock = weakref_lock::lock(obj_addr);
921
922        // Clear generic cache
923        self.generic.store(ptr::null_mut(), Ordering::Relaxed);
924
925        let mut callbacks = Vec::new();
926        let mut current = NonNull::new(self.head.load(Ordering::Relaxed));
927        while let Some(node) = current {
928            let next = unsafe { WeakLink::pointers(node).as_ref().get_next() };
929
930            let wr = unsafe { node.as_ref() };
931
932            // Mark weakref as dead
933            wr.payload
934                .wr_object
935                .store(ptr::null_mut(), Ordering::Relaxed);
936
937            // Unlink from list
938            unsafe {
939                let mut ptrs = WeakLink::pointers(node);
940                ptrs.as_mut().set_prev(None);
941                ptrs.as_mut().set_next(None);
942            }
943
944            // Collect callback without invoking only if we can keep weakref alive.
945            if wr.ref_count.safe_inc() {
946                let wr_ref = unsafe { PyRef::from_raw(wr as *const Py<PyWeak>) };
947                let cb = unsafe { wr.payload.callback.get().replace(None) };
948                if let Some(cb) = cb {
949                    callbacks.push((wr_ref, cb));
950                }
951            }
952
953            current = next;
954        }
955        self.head.store(ptr::null_mut(), Ordering::Relaxed);
956
957        callbacks
958    }
959
960    fn count(&self, obj: &PyObject) -> usize {
961        let _lock = weakref_lock::lock(obj as *const PyObject as usize);
962        let mut count = 0usize;
963        let mut current = NonNull::new(self.head.load(Ordering::Relaxed));
964        while let Some(node) = current {
965            if unsafe { node.as_ref() }.ref_count.get() > 0 {
966                count += 1;
967            }
968            current = unsafe { WeakLink::pointers(node).as_ref().get_next() };
969        }
970        count
971    }
972
973    fn get_weak_references(&self, obj: &PyObject) -> Vec<PyRef<PyWeak>> {
974        let _lock = weakref_lock::lock(obj as *const PyObject as usize);
975        let mut v = Vec::new();
976        let mut current = NonNull::new(self.head.load(Ordering::Relaxed));
977        while let Some(node) = current {
978            let wr = unsafe { node.as_ref() };
979            if wr.ref_count.safe_inc() {
980                v.push(unsafe { PyRef::from_raw(wr as *const Py<PyWeak>) });
981            }
982            current = unsafe { WeakLink::pointers(node).as_ref().get_next() };
983        }
984        v
985    }
986}
987
988impl Default for WeakRefList {
989    fn default() -> Self {
990        Self::new()
991    }
992}
993
994struct WeakLink;
995unsafe impl Link for WeakLink {
996    type Handle = PyRef<PyWeak>;
997
998    type Target = Py<PyWeak>;
999
1000    #[inline(always)]
1001    fn as_raw(handle: &PyRef<PyWeak>) -> NonNull<Self::Target> {
1002        NonNull::from(&**handle)
1003    }
1004
1005    #[inline(always)]
1006    unsafe fn from_raw(ptr: NonNull<Self::Target>) -> Self::Handle {
1007        unsafe { PyRef::from_raw(ptr.as_ptr()) }
1008    }
1009
1010    #[inline(always)]
1011    unsafe fn pointers(target: NonNull<Self::Target>) -> NonNull<Pointers<Self::Target>> {
1012        // SAFETY: requirements forwarded from caller
1013        unsafe { NonNull::new_unchecked(&raw mut (*target.as_ptr()).payload.pointers) }
1014    }
1015}
1016
1017// PyWeakReference: each weakref holds a direct pointer to its referent.
1018#[pyclass(name = "ReferenceType", module = "weakref")]
1019#[derive(Debug)]
1020pub struct PyWeak {
1021    pointers: Pointers<Py<Self>>,
1022    /// Direct pointer to the referent object, null when dead.
1023    /// Equivalent to wr_object in PyWeakReference.
1024    wr_object: PyAtomic<*mut PyObject>,
1025    /// Protected by stripe lock (keyed on wr_object address).
1026    callback: UnsafeCell<Option<PyObjectRef>>,
1027    pub(crate) hash: PyAtomic<crate::common::hash::PyHash>,
1028}
1029
1030cfg_select! {
1031    feature = "threading" => {
1032        unsafe impl Send for PyWeak {}
1033        unsafe impl Sync for PyWeak {}
1034    }
1035    _ => {}
1036}
1037
1038impl PyWeak {
1039    /// _PyWeakref_GET_REF: attempt to upgrade the weakref to a strong reference.
1040    pub(crate) fn upgrade(&self) -> Option<PyObjectRef> {
1041        let obj_ptr = self.wr_object.load(Ordering::Acquire);
1042        if obj_ptr.is_null() {
1043            return None;
1044        }
1045
1046        let _lock = weakref_lock::lock(obj_ptr as usize);
1047
1048        // Double-check under lock (clear may have run between our check and lock)
1049        let obj_ptr = self.wr_object.load(Ordering::Relaxed);
1050        if obj_ptr.is_null() {
1051            return None;
1052        }
1053
1054        unsafe {
1055            if !(*obj_ptr).0.ref_count.safe_inc() {
1056                return None;
1057            }
1058            Some(PyObjectRef::from_raw(NonNull::new_unchecked(obj_ptr)))
1059        }
1060    }
1061
1062    pub(crate) fn is_dead(&self) -> bool {
1063        self.wr_object.load(Ordering::Acquire).is_null()
1064    }
1065
1066    /// Get the callback associated with this weak reference.
1067    /// Returns `None` if there is no callback or if the referent has been
1068    /// collected (at which point the callback was already consumed).
1069    pub(crate) fn get_callback(&self) -> Option<PyObjectRef> {
1070        let obj_ptr = self.wr_object.load(Ordering::Acquire);
1071        if obj_ptr.is_null() {
1072            // Dead weakref: callback was consumed during clear
1073            return None;
1074        }
1075
1076        let _lock = weakref_lock::lock(obj_ptr as usize);
1077
1078        // Double-check under lock (clear may have run between our check and lock)
1079        let obj_ptr = self.wr_object.load(Ordering::Relaxed);
1080        if obj_ptr.is_null() {
1081            return None;
1082        }
1083
1084        // Safety: we hold the stripe lock that protects the callback field
1085        let callback = unsafe { &*self.callback.get() };
1086        callback.clone()
1087    }
1088
1089    /// weakref_dealloc: remove from list if still linked.
1090    fn drop_inner(&self) {
1091        let obj_ptr = self.wr_object.load(Ordering::Acquire);
1092        if obj_ptr.is_null() {
1093            return; // Already cleared by WeakRefList::clear()
1094        }
1095
1096        let _lock = weakref_lock::lock(obj_ptr as usize);
1097
1098        // Double-check under lock
1099        let obj_ptr = self.wr_object.load(Ordering::Relaxed);
1100        if obj_ptr.is_null() {
1101            return; // Cleared between our check and lock acquisition
1102        }
1103
1104        let obj = unsafe { &*obj_ptr };
1105        // Safety: if a weakref exists pointing to this object, weakref prefix must be present
1106        let wrl = obj.0.weakref_list_ref().unwrap();
1107
1108        // Compute our Py<PyWeak> node pointer from payload address
1109        let node_ptr = unsafe { NonNull::new_unchecked(Py::from_payload_ptr(self).cast_mut()) };
1110
1111        // Unlink from list
1112        unsafe { unlink_weakref(wrl, node_ptr) };
1113
1114        // Update generic cache if this was it
1115        if wrl.generic.load(Ordering::Relaxed) == node_ptr.as_ptr() {
1116            wrl.generic.store(ptr::null_mut(), Ordering::Relaxed);
1117        }
1118
1119        // Mark as dead
1120        self.wr_object.store(ptr::null_mut(), Ordering::Relaxed);
1121    }
1122}
1123
1124impl Drop for PyWeak {
1125    #[inline(always)]
1126    fn drop(&mut self) {
1127        // we do NOT have actual exclusive access!
1128        let me: &Self = self;
1129        me.drop_inner();
1130    }
1131}
1132
1133impl Py<PyWeak> {
1134    #[inline(always)]
1135    pub fn upgrade(&self) -> Option<PyObjectRef> {
1136        PyWeak::upgrade(self)
1137    }
1138}
1139
1140/// SHARED_KEYS_MAX_SIZE: beyond this many keys, inline values are
1141/// converted to a regular heap dict.
1142pub(crate) const SHARED_KEYS_MAX_SIZE: usize = 30;
1143
1144#[derive(Debug)]
1145#[repr(C)]
1146pub(crate) struct InstanceDict {
1147    /// Owned dict pointer. First field so `dict_member_offset` addresses it.
1148    /// Null when this object has no dict.
1149    pub(crate) dict: PyAtomicRef<Option<crate::builtins::PyDict>>,
1150    /// Separate from the pointer: shared-key inline values are valid only
1151    /// while this stays true. Replacing the dict does not change it.
1152    inline_values_valid: PyAtomic<bool>,
1153}
1154
1155const _: () = assert!(core::mem::offset_of!(InstanceDict, dict) == 0);
1156const _: () = assert!(core::mem::offset_of!(ObjExt, dict) == 0);
1157
1158impl From<PyDictRef> for InstanceDict {
1159    #[inline(always)]
1160    fn from(d: PyDictRef) -> Self {
1161        Self::new(d)
1162    }
1163}
1164
1165impl InstanceDict {
1166    #[inline]
1167    pub(crate) fn new(d: PyDictRef) -> Self {
1168        Self::from_opt(Some(d), false)
1169    }
1170
1171    #[inline]
1172    pub(crate) fn from_opt(d: Option<PyDictRef>, inline_values: bool) -> Self {
1173        Self {
1174            dict: PyAtomicRef::from(d),
1175            inline_values_valid: Radium::new(inline_values),
1176        }
1177    }
1178
1179    #[inline]
1180    pub(crate) fn inline_values_valid(&self) -> bool {
1181        self.inline_values_valid.load(Ordering::Relaxed)
1182    }
1183
1184    #[inline]
1185    pub(crate) fn invalidate_inline_values(&self) {
1186        self.inline_values_valid.store(false, Ordering::Relaxed);
1187    }
1188
1189    pub(crate) fn maybe_materialize_inline_values(&self) {
1190        if !self.inline_values_valid() {
1191            return;
1192        }
1193        let overflow = self.with(|d| d.is_some_and(|d| d.__len__() > SHARED_KEYS_MAX_SIZE));
1194        if overflow {
1195            self.invalidate_inline_values();
1196        }
1197    }
1198
1199    #[inline]
1200    pub(crate) fn get(&self) -> Option<PyDictRef> {
1201        self.dict.load_owned()
1202    }
1203
1204    /// Run `f` on the current dict.
1205    ///
1206    /// The dict is held by a strong reference for the call, so a concurrent
1207    /// replace cannot free it. `f` must not re-enter `get_or_insert` on this
1208    /// cell.
1209    #[inline]
1210    pub(crate) fn with<R>(&self, f: impl FnOnce(Option<&Py<crate::builtins::PyDict>>) -> R) -> R {
1211        let owned = self.get();
1212        f(owned.as_deref())
1213    }
1214
1215    #[inline]
1216    pub(crate) fn set(&self, d: Option<PyDictRef>) {
1217        self.replace(d);
1218    }
1219
1220    #[inline]
1221    pub(crate) fn replace(&self, d: Option<PyDictRef>) -> Option<PyDictRef> {
1222        self.dict.store(d)
1223    }
1224
1225    pub(crate) fn get_or_insert(&self, vm: &VirtualMachine) -> PyDictRef {
1226        loop {
1227            if let Some(existing) = self.get() {
1228                return existing;
1229            }
1230            let dict = vm.ctx.new_dict();
1231            match self.dict.compare_exchange_empty(dict.clone()) {
1232                Ok(()) => return dict,
1233                Err(rejected) => {
1234                    drop(rejected);
1235                    drop(dict);
1236                }
1237            }
1238        }
1239    }
1240}
1241
1242impl<T: PyPayload> Py<T> {
1243    /// Run the destructors of the fields that have one, payload first.
1244    ///
1245    /// Declaration order would drop `typ` first, and `PyAtomicRef::drop`
1246    /// leaves it null. A weakref payload is still linked into the list of the
1247    /// object it points at until its own `Drop` unlinks it, and a thread
1248    /// walking that list reads the class off every node it passes, so the
1249    /// class has to outlive the payload.
1250    unsafe fn drop_fields(ptr: *mut Self) {
1251        unsafe {
1252            core::ptr::drop_in_place(&raw mut (*ptr).payload);
1253            core::ptr::drop_in_place(&raw mut (*ptr).typ);
1254        }
1255    }
1256
1257    /// Deallocate a Py, handling optional prefix(es).
1258    /// Layout: `[WeakRefList?][PyAtomicRef<Option<PyObject>>; N][ObjExt?][Py<T>]`
1259    ///
1260    /// # Safety
1261    /// `ptr` must be a valid pointer from `Py::new` and must not be used after this call.
1262    unsafe fn dealloc(ptr: *mut Self) {
1263        unsafe {
1264            let (flags, member_count) = (*ptr).read_type_flags();
1265            let has_ext = flags.contains(&crate::types::PyTypeFlags::HAS_DICT) || member_count > 0;
1266            let has_weakref = flags.contains(&crate::types::PyTypeFlags::HAS_WEAKREF);
1267            // Objects published to lock-free caches keep their memory mapped
1268            // until a QSBR grace period passes; destructors still run now.
1269            let published = (*ptr).ref_count.is_published();
1270
1271            if has_ext || has_weakref {
1272                // Reconstruct the same layout used in new()
1273                let mut layout = core::alloc::Layout::from_size_align(0, 1).unwrap();
1274
1275                if has_weakref {
1276                    layout = layout
1277                        .extend(core::alloc::Layout::new::<WeakRefList>())
1278                        .unwrap()
1279                        .0;
1280                }
1281                if let Some(region) = slot_region_layout(member_count) {
1282                    layout = layout.extend(region).unwrap().0;
1283                }
1284                if has_ext {
1285                    layout = layout
1286                        .extend(core::alloc::Layout::new::<ObjExt>())
1287                        .unwrap()
1288                        .0;
1289                }
1290                let (combined, inner_offset) =
1291                    layout.extend(core::alloc::Layout::new::<Self>()).unwrap();
1292                let combined = combined.pad_to_align();
1293
1294                let alloc_ptr = (ptr as *mut u8).sub(inner_offset);
1295
1296                Self::drop_fields(ptr);
1297
1298                // Drop member cells, then ObjExt. WeakRefList is in front of the cells.
1299                let mut cursor = alloc_ptr;
1300                if has_weakref {
1301                    cursor = cursor.add(core::mem::size_of::<WeakRefList>());
1302                }
1303                if let Some(region) = slot_region_layout(member_count) {
1304                    let cell = core::mem::size_of::<PyAtomicRef<Option<PyObject>>>();
1305                    let first = cursor.add(region.size() - member_count * cell);
1306                    let cells = first.cast::<PyAtomicRef<Option<PyObject>>>();
1307                    for i in 0..member_count {
1308                        core::ptr::drop_in_place(cells.add(i));
1309                    }
1310                    cursor = cursor.add(region.size());
1311                }
1312                if has_ext {
1313                    core::ptr::drop_in_place(cursor.cast::<ObjExt>());
1314                }
1315                // WeakRefList has no Drop (just raw pointers), no drop_in_place needed
1316
1317                if published {
1318                    crate::object::qsbr::free_delayed(alloc_ptr, combined);
1319                } else {
1320                    alloc::alloc::dealloc(alloc_ptr, combined);
1321                }
1322            } else if published {
1323                let layout = core::alloc::Layout::new::<Self>();
1324                Self::drop_fields(ptr);
1325                crate::object::qsbr::free_delayed(ptr as *mut u8, layout);
1326            } else {
1327                Self::drop_fields(ptr);
1328                // The fields are gone; the box is only here to free the memory
1329                // the matching `Box::new` in `new` allocated.
1330                drop(Box::from_raw(ptr.cast::<core::mem::MaybeUninit<Self>>()));
1331            }
1332        }
1333    }
1334}
1335
1336impl<T: PyPayload + core::fmt::Debug> Py<T> {
1337    /// Allocate a new Py, optionally with prefix(es).
1338    /// Returns a raw pointer to the Py (NOT the allocation start).
1339    /// Layout: `[WeakRefList?][PyAtomicRef<Option<PyObject>>; N][ObjExt?][Py<T>]`
1340    fn new(payload: T, typ: PyTypeRef, dict: Option<PyDictRef>) -> *mut Self {
1341        let member_count = typ.slots.member_count;
1342        let needs_ext = typ
1343            .slots
1344            .flags
1345            .has_feature(crate::types::PyTypeFlags::HAS_DICT)
1346            || member_count > 0;
1347        let needs_weakref = typ
1348            .slots
1349            .flags
1350            .has_feature(crate::types::PyTypeFlags::HAS_WEAKREF);
1351        debug_assert!(
1352            needs_ext || dict.is_none(),
1353            "dict passed to type '{}' without HAS_DICT flag",
1354            typ.name()
1355        );
1356
1357        if needs_ext || needs_weakref {
1358            // Build layout left-to-right: [WeakRefList?][slots?][ObjExt?][Py]
1359            let mut layout = core::alloc::Layout::from_size_align(0, 1).unwrap();
1360
1361            let weakref_start = if needs_weakref {
1362                let (combined, offset) = layout
1363                    .extend(core::alloc::Layout::new::<WeakRefList>())
1364                    .unwrap();
1365                layout = combined;
1366                Some(offset)
1367            } else {
1368                None
1369            };
1370
1371            let slots_start = if let Some(region) = slot_region_layout(member_count) {
1372                let (combined, offset) = layout.extend(region).unwrap();
1373                layout = combined;
1374                Some(offset)
1375            } else {
1376                None
1377            };
1378
1379            let ext_start = if needs_ext {
1380                let (combined, offset) =
1381                    layout.extend(core::alloc::Layout::new::<ObjExt>()).unwrap();
1382                layout = combined;
1383                Some(offset)
1384            } else {
1385                None
1386            };
1387
1388            let (combined, inner_offset) =
1389                layout.extend(core::alloc::Layout::new::<Self>()).unwrap();
1390            let combined = combined.pad_to_align();
1391
1392            let alloc_ptr = unsafe { alloc::alloc::alloc(combined) };
1393            if alloc_ptr.is_null() {
1394                alloc::alloc::handle_alloc_error(combined);
1395            }
1396            // Expose provenance so ext_ref()/weakref_list_ref() can reconstruct
1397            alloc_ptr.expose_provenance();
1398
1399            unsafe {
1400                if let Some(offset) = slots_start {
1401                    let region = slot_region_layout(member_count).unwrap();
1402                    let cell = core::mem::size_of::<PyAtomicRef<Option<PyObject>>>();
1403                    let first = alloc_ptr.add(offset + region.size() - member_count * cell);
1404                    let cells = first.cast::<PyAtomicRef<Option<PyObject>>>();
1405                    for i in 0..member_count {
1406                        cells
1407                            .add(i)
1408                            .write(PyAtomicRef::<Option<PyObject>>::new_empty());
1409                    }
1410                }
1411
1412                if let Some(offset) = ext_start {
1413                    let ext_ptr = alloc_ptr.add(offset) as *mut ObjExt;
1414                    let has_dict = typ
1415                        .slots
1416                        .flags
1417                        .has_feature(crate::types::PyTypeFlags::HAS_DICT);
1418                    let inline_values = typ
1419                        .slots
1420                        .flags
1421                        .has_feature(crate::types::PyTypeFlags::INLINE_VALUES);
1422                    ext_ptr.write(ObjExt::new(dict, has_dict, inline_values));
1423                }
1424
1425                if let Some(offset) = weakref_start {
1426                    let weakref_ptr = alloc_ptr.add(offset) as *mut WeakRefList;
1427                    weakref_ptr.write(WeakRefList::new());
1428                }
1429
1430                let inner_ptr = alloc_ptr.add(inner_offset) as *mut Self;
1431                inner_ptr.write(Self {
1432                    ref_count: RefCount::new(),
1433                    vtable: PyObjVTable::of::<T>(),
1434                    gc_bits: Radium::new(0),
1435                    gc_generation: Radium::new(GC_UNTRACKED),
1436                    gc_owner: Radium::new(GC_NO_OWNER),
1437                    gc_refs: Radium::new(0),
1438                    gc_pointers: Pointers::new(),
1439                    typ: PyAtomicRef::from(typ),
1440                    payload,
1441                });
1442                inner_ptr
1443            }
1444        } else {
1445            Box::into_raw(Box::new(Self {
1446                ref_count: RefCount::new(),
1447                vtable: PyObjVTable::of::<T>(),
1448                gc_bits: Radium::new(0),
1449                gc_generation: Radium::new(GC_UNTRACKED),
1450                gc_owner: Radium::new(GC_NO_OWNER),
1451                gc_refs: Radium::new(0),
1452                gc_pointers: Pointers::new(),
1453                typ: PyAtomicRef::from(typ),
1454                payload,
1455            }))
1456        }
1457    }
1458}
1459
1460/// Thread-local freelist storage for reusing object allocations.
1461///
1462/// Wraps a `Vec<*mut PyObject>`. On thread teardown, `Drop` frees raw
1463/// `Py<T>` allocations without running payload destructors to avoid
1464/// accessing already-destroyed thread-local storage (GC state, other freelists).
1465pub(crate) struct FreeList<T: PyPayload> {
1466    items: Vec<*mut PyObject>,
1467    _marker: core::marker::PhantomData<T>,
1468}
1469
1470impl<T: PyPayload> FreeList<T> {
1471    pub(crate) const fn new() -> Self {
1472        Self {
1473            items: Vec::new(),
1474            _marker: core::marker::PhantomData,
1475        }
1476    }
1477}
1478
1479impl<T: PyPayload> Default for FreeList<T> {
1480    fn default() -> Self {
1481        Self::new()
1482    }
1483}
1484
1485impl<T: PyPayload> Drop for FreeList<T> {
1486    fn drop(&mut self) {
1487        // During thread teardown, we cannot safely run destructors on cached
1488        // objects because their Drop impls may access thread-local storage
1489        // (GC state, other freelists) that is already destroyed.
1490        // Instead, free just the raw allocation. The payload's heap fields
1491        // (BigInt, PyObjectRef, etc.) are leaked, but this is bounded by
1492        // MAX_FREELIST per type per thread.
1493        for ptr in self.items.drain(..) {
1494            unsafe {
1495                alloc::alloc::dealloc(ptr as *mut u8, core::alloc::Layout::new::<Py<T>>());
1496            }
1497        }
1498    }
1499}
1500
1501impl<T: PyPayload> core::ops::Deref for FreeList<T> {
1502    type Target = Vec<*mut PyObject>;
1503    fn deref(&self) -> &Self::Target {
1504        &self.items
1505    }
1506}
1507
1508impl<T: PyPayload> core::ops::DerefMut for FreeList<T> {
1509    fn deref_mut(&mut self) -> &mut Self::Target {
1510        &mut self.items
1511    }
1512}
1513
1514/// The `PyObjectRef` is one of the most used types. It is a reference to a
1515/// python object. A single python object can have multiple references, and
1516/// this reference counting is accounted for by this type. Use the `.clone()`
1517/// method to create a new reference and increment the amount of references
1518/// to the python object by 1.
1519#[repr(transparent)]
1520pub struct PyObjectRef {
1521    ptr: NonNull<PyObject>,
1522}
1523
1524impl Clone for PyObjectRef {
1525    #[inline(always)]
1526    fn clone(&self) -> Self {
1527        (**self).to_owned()
1528    }
1529}
1530
1531cfg_select! {
1532    feature = "threading" => {
1533        unsafe impl Send for PyObjectRef {}
1534        unsafe impl Sync for PyObjectRef {}
1535    }
1536    _ => {}
1537}
1538
1539#[repr(transparent)]
1540pub struct PyObject(Py<Erased>);
1541
1542impl Deref for PyObjectRef {
1543    type Target = PyObject;
1544
1545    #[inline(always)]
1546    fn deref(&self) -> &PyObject {
1547        unsafe { self.ptr.as_ref() }
1548    }
1549}
1550
1551impl ToOwned for PyObject {
1552    type Owned = PyObjectRef;
1553
1554    #[inline(always)]
1555    fn to_owned(&self) -> Self::Owned {
1556        self.0.ref_count.inc();
1557        PyObjectRef {
1558            ptr: NonNull::from(self),
1559        }
1560    }
1561}
1562
1563impl PyObject {
1564    /// Atomically try to create a strong reference.
1565    /// Returns `None` if the strong count is already 0 (object being destroyed).
1566    /// Uses CAS to prevent the TOCTOU race between checking strong_count and
1567    /// incrementing it.
1568    #[inline]
1569    pub fn try_to_owned(&self) -> Option<PyObjectRef> {
1570        if self.0.ref_count.safe_inc() {
1571            Some(PyObjectRef {
1572                ptr: NonNull::from(self),
1573            })
1574        } else {
1575            None
1576        }
1577    }
1578
1579    /// Like [`try_to_owned`](Self::try_to_owned), but from a raw pointer.
1580    ///
1581    /// Uses `addr_of!` to access `ref_count` without forming `&PyObject`,
1582    /// minimizing the borrow scope when the pointer may be stale
1583    /// (e.g. cache-hit paths protected by version guards).
1584    ///
1585    /// # Safety
1586    /// `ptr` must point to a live (not yet deallocated) `PyObject`, or to
1587    /// memory whose `ref_count` field is still atomically readable
1588    /// (same guarantee as `_Py_TryIncRefShared`).
1589    #[inline]
1590    pub unsafe fn try_to_owned_from_ptr(ptr: *mut Self) -> Option<PyObjectRef> {
1591        let inner = ptr.cast::<Py<Erased>>();
1592        let ref_count = unsafe { &*core::ptr::addr_of!((*inner).ref_count) };
1593        if ref_count.safe_inc() {
1594            Some(PyObjectRef {
1595                ptr: unsafe { NonNull::new_unchecked(ptr) },
1596            })
1597        } else {
1598            None
1599        }
1600    }
1601
1602    /// Mark this object as published to a lock-free cache. Its memory
1603    /// reclamation is deferred through QSBR (see `object::qsbr`) so that
1604    /// concurrent try-incref readers never touch freed memory.
1605    pub(crate) fn mark_cache_published(&self) {
1606        self.0.ref_count.mark_published();
1607    }
1608}
1609
1610impl PyObjectRef {
1611    #[inline(always)]
1612    #[must_use]
1613    pub const fn into_raw(self) -> NonNull<PyObject> {
1614        let ptr = self.ptr;
1615        core::mem::forget(self);
1616        ptr
1617    }
1618
1619    /// # Safety
1620    /// The raw pointer must have been previously returned from a call to
1621    /// [`PyObjectRef::into_raw`]. The user is responsible for ensuring that the inner data is not
1622    /// dropped more than once due to mishandling the reference count by calling this function
1623    /// too many times.
1624    #[inline(always)]
1625    #[must_use]
1626    pub const unsafe fn from_raw(ptr: NonNull<PyObject>) -> Self {
1627        Self { ptr }
1628    }
1629
1630    /// Attempt to downcast this reference to a subclass.
1631    ///
1632    /// If the downcast fails, the original ref is returned in as `Err` so
1633    /// another downcast can be attempted without unnecessary cloning.
1634    #[inline(always)]
1635    pub fn downcast<T: PyPayload>(self) -> Result<PyRef<T>, Self> {
1636        if self.downcastable::<T>() {
1637            Ok(unsafe { self.downcast_unchecked() })
1638        } else {
1639            Err(self)
1640        }
1641    }
1642
1643    pub fn try_downcast<T: PyPayload>(self, vm: &VirtualMachine) -> PyResult<PyRef<T>> {
1644        T::try_downcast_from(&self, vm)?;
1645        Ok(unsafe { self.downcast_unchecked() })
1646    }
1647
1648    /// Force to downcast this reference to a subclass.
1649    ///
1650    /// # Safety
1651    /// T must be the exact payload type
1652    #[inline(always)]
1653    #[must_use]
1654    pub unsafe fn downcast_unchecked<T>(self) -> PyRef<T> {
1655        // PyRef::from_obj_unchecked(self)
1656        // manual impl to avoid assertion
1657        let obj = ManuallyDrop::new(self);
1658        PyRef {
1659            ptr: obj.ptr.cast(),
1660        }
1661    }
1662
1663    // ideally we'd be able to define these in pyobject.rs, but method visibility rules are weird
1664
1665    /// Attempt to downcast this reference to the specific class that is associated `T`.
1666    ///
1667    /// If the downcast fails, the original ref is returned in as `Err` so
1668    /// another downcast can be attempted without unnecessary cloning.
1669    #[inline]
1670    pub fn downcast_exact<T: PyPayload>(self, vm: &VirtualMachine) -> Result<PyRefExact<T>, Self> {
1671        if self.class().is(T::class(&vm.ctx)) {
1672            // TODO: is this always true?
1673            assert!(
1674                self.downcastable::<T>(),
1675                "obj.__class__ is T::class() but payload is not T"
1676            );
1677            // SAFETY: just asserted that downcastable::<T>()
1678            Ok(unsafe { PyRefExact::new_unchecked(PyRef::from_obj_unchecked(self)) })
1679        } else {
1680            Err(self)
1681        }
1682    }
1683}
1684
1685impl PyObject {
1686    /// Returns the WeakRefList if the type supports weakrefs (HAS_WEAKREF).
1687    /// The WeakRefList is stored as a separate prefix before Py,
1688    /// independent from ObjExt (dict/slots).
1689    #[inline(always)]
1690    fn weak_ref_list(&self) -> Option<&WeakRefList> {
1691        self.0.weakref_list_ref()
1692    }
1693
1694    /// Returns the first weakref in the weakref list, if any.
1695    pub(crate) fn get_weakrefs(&self) -> Option<PyObjectRef> {
1696        let wrl = self.weak_ref_list()?;
1697        let _lock = weakref_lock::lock(self as *const Self as usize);
1698        let head_ptr = wrl.head.load(Ordering::Relaxed);
1699        if head_ptr.is_null() {
1700            None
1701        } else {
1702            let head = unsafe { &*head_ptr };
1703            if head.ref_count.safe_inc() {
1704                Some(unsafe { PyRef::from_raw(head_ptr) }.into())
1705            } else {
1706                None
1707            }
1708        }
1709    }
1710
1711    pub(crate) fn downgrade_with_weakref_typ_opt(
1712        &self,
1713        callback: Option<PyObjectRef>,
1714        // a reference to weakref_type **specifically**
1715        typ: PyTypeRef,
1716    ) -> Option<PyRef<PyWeak>> {
1717        self.weak_ref_list()
1718            .map(|wrl| wrl.add(self, typ, true, false, callback, None))
1719    }
1720
1721    pub(crate) fn downgrade_with_typ(
1722        &self,
1723        callback: Option<PyObjectRef>,
1724        typ: PyTypeRef,
1725        vm: &VirtualMachine,
1726    ) -> PyResult<PyRef<PyWeak>> {
1727        // Check HAS_WEAKREF flag first
1728        if !self
1729            .class()
1730            .slots
1731            .flags
1732            .has_feature(crate::types::PyTypeFlags::HAS_WEAKREF)
1733        {
1734            return Err(vm.new_type_error(format!(
1735                "cannot create weak reference to '{}' object",
1736                self.class().name()
1737            )));
1738        }
1739        let dict = if typ
1740            .slots
1741            .flags
1742            .has_feature(crate::types::PyTypeFlags::HAS_DICT)
1743        {
1744            Some(vm.ctx.new_dict())
1745        } else {
1746            None
1747        };
1748        let cls_is_weakref = typ.is(vm.ctx.types.weakref_type);
1749        let cls_is_weakproxy =
1750            typ.is(vm.ctx.types.weakproxy_type) || typ.is(vm.ctx.types.weakcallableproxy_type);
1751        let wrl = self.weak_ref_list().ok_or_else(|| {
1752            vm.new_type_error(format!(
1753                "cannot create weak reference to '{}' object",
1754                self.class().name()
1755            ))
1756        })?;
1757        Ok(wrl.add(self, typ, cls_is_weakref, cls_is_weakproxy, callback, dict))
1758    }
1759
1760    pub fn downgrade(
1761        &self,
1762        callback: Option<PyObjectRef>,
1763        vm: &VirtualMachine,
1764    ) -> PyResult<PyRef<PyWeak>> {
1765        self.downgrade_with_typ(callback, vm.ctx.types.weakref_type.to_owned(), vm)
1766    }
1767
1768    pub fn clear_weak_refs(&self) {
1769        if let Some(wrl) = self.weak_ref_list() {
1770            wrl.clear(self);
1771        }
1772    }
1773
1774    pub fn get_weak_references(&self) -> Option<Vec<PyRef<PyWeak>>> {
1775        self.weak_ref_list()
1776            .map(|wrl| wrl.get_weak_references(self))
1777    }
1778
1779    #[deprecated(note = "use downcastable instead")]
1780    #[inline(always)]
1781    pub fn payload_is<T: PyPayload>(&self) -> bool {
1782        self.0.vtable.typeid == T::PAYLOAD_TYPE_ID
1783    }
1784
1785    /// Force to return payload as T.
1786    ///
1787    /// # Safety
1788    /// The actual payload type must be T.
1789    #[deprecated(note = "use downcast_unchecked_ref instead")]
1790    #[inline(always)]
1791    pub const unsafe fn payload_unchecked<T: PyPayload>(&self) -> &T {
1792        // we cast to a Py<T> first because we don't know T's exact offset because of
1793        // varying alignment, but once we get a Py<T> the compiler can get it for us
1794        let inner = unsafe { &*(&self.0 as *const Py<Erased> as *const Py<T>) };
1795        &inner.payload
1796    }
1797
1798    #[deprecated(note = "use downcast_ref instead")]
1799    #[inline(always)]
1800    pub fn payload<T: PyPayload>(&self) -> Option<&T> {
1801        #[allow(deprecated)]
1802        if self.payload_is::<T>() {
1803            #[allow(deprecated)]
1804            Some(unsafe { self.payload_unchecked() })
1805        } else {
1806            None
1807        }
1808    }
1809
1810    #[inline(always)]
1811    pub fn class(&self) -> &Py<PyType> {
1812        &self.0.typ
1813    }
1814
1815    pub fn set_class(&self, typ: PyTypeRef, vm: &VirtualMachine) {
1816        self.0.typ.swap_to_temporary_refs(typ, vm);
1817    }
1818
1819    #[deprecated(note = "use downcast_ref_if_exact instead")]
1820    #[inline(always)]
1821    pub fn payload_if_exact<T: PyPayload>(&self, vm: &VirtualMachine) -> Option<&T> {
1822        if self.class().is(T::class(&vm.ctx)) {
1823            #[allow(deprecated)]
1824            self.payload()
1825        } else {
1826            None
1827        }
1828    }
1829
1830    #[inline(always)]
1831    pub(crate) fn instance_dict(&self) -> Option<&InstanceDict> {
1832        let ext = self.0.ext_ref()?;
1833        let (flags, _) = self.0.read_type_flags();
1834        if flags.contains(&crate::types::PyTypeFlags::HAS_DICT) {
1835            Some(&ext.dict)
1836        } else {
1837            None
1838        }
1839    }
1840
1841    /// `_PyObject_InlineValues(obj)->valid` when the type has INLINE_VALUES.
1842    #[inline]
1843    pub fn has_inline_values(&self) -> bool {
1844        self.class()
1845            .slots
1846            .flags
1847            .has_feature(crate::types::PyTypeFlags::INLINE_VALUES)
1848            && self
1849                .instance_dict()
1850                .is_some_and(InstanceDict::inline_values_valid)
1851    }
1852
1853    #[inline(always)]
1854    pub fn dict(&self) -> Option<PyDictRef> {
1855        self.instance_dict().and_then(|d| d.get())
1856    }
1857
1858    /// Whether this object currently has an instance dict, without cloning it.
1859    ///
1860    /// `false` both for an object with no dict slot and for one whose slot is
1861    /// still empty, which is what `dict().is_none()` reports.
1862    #[inline(always)]
1863    pub fn has_instance_dict(&self) -> bool {
1864        self.instance_dict()
1865            .is_some_and(|d| d.with(|dict| dict.is_some()))
1866    }
1867
1868    /// Run `f` on the instance dict without cloning it; see [`InstanceDict::with`].
1869    #[inline(always)]
1870    pub(crate) fn with_instance_dict<R>(
1871        &self,
1872        f: impl FnOnce(Option<&Py<crate::builtins::PyDict>>) -> R,
1873    ) -> R {
1874        match self.instance_dict() {
1875            Some(d) => d.with(f),
1876            None => f(None),
1877        }
1878    }
1879
1880    /// Set the dict field. Returns `Err(dict)` if this object does not have a dict field
1881    /// in the first place.
1882    pub fn set_dict(&self, dict: Option<PyDictRef>) -> Result<(), Option<PyDictRef>> {
1883        match self.instance_dict() {
1884            Some(d) => {
1885                d.set(dict);
1886                Ok(())
1887            }
1888            None => Err(dict),
1889        }
1890    }
1891
1892    #[deprecated(note = "use downcast_ref instead")]
1893    #[inline(always)]
1894    pub fn payload_if_subclass<T: crate::PyPayload>(&self, vm: &VirtualMachine) -> Option<&T> {
1895        if self.class().fast_issubclass(T::class(&vm.ctx)) {
1896            #[allow(deprecated)]
1897            self.payload()
1898        } else {
1899            None
1900        }
1901    }
1902
1903    #[inline]
1904    pub(crate) fn typeid(&self) -> TypeId {
1905        self.0.vtable.typeid
1906    }
1907
1908    /// Check if this object can be downcast to T.
1909    #[inline(always)]
1910    pub fn downcastable<T: PyPayload>(&self) -> bool {
1911        self.typeid() == T::PAYLOAD_TYPE_ID && unsafe { T::validate_downcastable_from(self) }
1912    }
1913
1914    /// Attempt to downcast this reference to a subclass.
1915    pub fn try_downcast_ref<'a, T: PyPayload>(
1916        &'a self,
1917        vm: &VirtualMachine,
1918    ) -> PyResult<&'a Py<T>> {
1919        T::try_downcast_from(self, vm)?;
1920        Ok(unsafe { self.downcast_unchecked_ref::<T>() })
1921    }
1922
1923    /// Attempt to downcast this reference to a subclass.
1924    #[inline(always)]
1925    pub fn downcast_ref<T: PyPayload>(&self) -> Option<&Py<T>> {
1926        if self.downcastable::<T>() {
1927            // SAFETY: just checked that the payload is T, and PyRef is repr(transparent) over
1928            // PyObjectRef
1929            Some(unsafe { self.downcast_unchecked_ref::<T>() })
1930        } else {
1931            None
1932        }
1933    }
1934
1935    #[inline(always)]
1936    pub fn downcast_ref_if_exact<T: PyPayload>(&self, vm: &VirtualMachine) -> Option<&Py<T>> {
1937        self.class()
1938            .is(T::class(&vm.ctx))
1939            .then(|| unsafe { self.downcast_unchecked_ref::<T>() })
1940    }
1941
1942    /// # Safety
1943    /// T must be the exact payload type
1944    #[inline(always)]
1945    pub unsafe fn downcast_unchecked_ref<T: PyPayload>(&self) -> &Py<T> {
1946        debug_assert!(self.downcastable::<T>());
1947        // SAFETY: requirements forwarded from caller
1948        unsafe { &*(self as *const Self as *const Py<T>) }
1949    }
1950
1951    #[inline(always)]
1952    pub fn strong_count(&self) -> usize {
1953        self.0.ref_count.get()
1954    }
1955
1956    #[inline]
1957    pub fn weak_count(&self) -> Option<usize> {
1958        self.weak_ref_list().map(|wrl| wrl.count(self))
1959    }
1960
1961    #[inline(always)]
1962    pub const fn as_raw(&self) -> *const Self {
1963        self
1964    }
1965
1966    /// Check if the object has been finalized (__del__ already called).
1967    /// _PyGC_FINALIZED in Py_GIL_DISABLED mode.
1968    #[inline]
1969    pub fn gc_finalized(&self) -> bool {
1970        GcBits::from_bits_retain(self.0.gc_bits.load(Ordering::Relaxed)).contains(GcBits::FINALIZED)
1971    }
1972
1973    /// Mark the object as finalized. Should be called before __del__.
1974    /// _PyGC_SET_FINALIZED in Py_GIL_DISABLED mode.
1975    #[inline]
1976    pub(crate) fn set_gc_finalized(&self) {
1977        self.set_gc_bit(GcBits::FINALIZED);
1978    }
1979
1980    /// Set a GC bit atomically.
1981    #[inline]
1982    pub(crate) fn set_gc_bit(&self, bit: GcBits) {
1983        self.0.gc_bits.fetch_or(bit.bits(), Ordering::Relaxed);
1984    }
1985
1986    /// Get the GC generation index for this object.
1987    #[inline]
1988    pub(crate) fn gc_generation(&self) -> u8 {
1989        self.0.gc_generation.load(Ordering::Relaxed)
1990    }
1991
1992    /// Set the GC generation index for this object.
1993    /// Must only be called while holding the generation list's write lock.
1994    #[inline]
1995    pub(crate) fn set_gc_generation(&self, generation: u8) {
1996        self.0.gc_generation.store(generation, Ordering::Relaxed);
1997    }
1998
1999    /// The interpreter whose collections consider this object.
2000    #[inline]
2001    pub(crate) fn gc_owner(&self) -> GcOwner {
2002        self.0.gc_owner.load(Ordering::Relaxed)
2003    }
2004
2005    /// Set the owning interpreter. Written by `track_object` before the object
2006    /// enters a generation list, and reset to `GC_NO_OWNER` when the owning
2007    /// interpreter goes away.
2008    #[inline]
2009    pub(crate) fn set_gc_owner(&self, owner: GcOwner) {
2010        self.0.gc_owner.store(owner, Ordering::Relaxed);
2011    }
2012
2013    /// Enter the running collection's candidate set, with `strong_count` as the
2014    /// count to subtract internal references from. A count too large to hold is
2015    /// taken as reachable outright, rather than clipped to a number the
2016    /// subtraction could still walk down to zero.
2017    #[inline]
2018    pub(crate) fn start_gc_refs(&self, strong_count: usize) {
2019        let refs = if strong_count >= GC_REACHABLE as usize {
2020            GC_REACHABLE
2021        } else {
2022            strong_count as u32
2023        };
2024        self.0.gc_refs.store(refs, Ordering::Relaxed);
2025        self.set_gc_bit(GcBits::COLLECTING);
2026    }
2027
2028    /// The count the running collection is working with.
2029    #[inline]
2030    pub(crate) fn gc_refs(&self) -> u32 {
2031        self.0.gc_refs.load(Ordering::Relaxed)
2032    }
2033
2034    /// Whether this object is in the running collection's candidate set.
2035    #[inline]
2036    pub(crate) fn is_gc_collecting(&self) -> bool {
2037        GcBits::from_bits_retain(self.0.gc_bits.load(Ordering::Relaxed))
2038            .contains(GcBits::COLLECTING)
2039    }
2040
2041    /// Take off one reference held from inside the candidate set. A count that
2042    /// did not fit stands for more references than every subtraction together
2043    /// could take off, so it stays where [`Self::start_gc_refs`] put it.
2044    #[inline]
2045    pub(crate) fn subtract_gc_ref(&self) {
2046        let refs = self.0.gc_refs.load(Ordering::Relaxed);
2047        if refs == GC_REACHABLE {
2048            return;
2049        }
2050        self.0
2051            .gc_refs
2052            .store(refs.saturating_sub(1), Ordering::Relaxed);
2053    }
2054
2055    /// Mark the object reachable, answering whether this call was the one that
2056    /// did it.
2057    #[inline]
2058    pub(crate) fn mark_gc_reachable(&self) -> bool {
2059        if self.0.gc_refs.load(Ordering::Relaxed) == GC_REACHABLE {
2060            return false;
2061        }
2062        self.0.gc_refs.store(GC_REACHABLE, Ordering::Relaxed);
2063        true
2064    }
2065
2066    /// Leave the candidate set, whatever the collection concluded.
2067    #[inline]
2068    pub(crate) fn end_gc_refs(&self) {
2069        self.0
2070            .gc_bits
2071            .fetch_and(!GcBits::COLLECTING.bits(), Ordering::Relaxed);
2072    }
2073
2074    /// _PyObject_GC_TRACK
2075    #[inline]
2076    pub(crate) fn set_gc_tracked(&self) {
2077        self.set_gc_bit(GcBits::TRACKED);
2078    }
2079
2080    /// Like [`Self::set_gc_tracked`], but for an object whose `gc_bits` is
2081    /// still known to be `0` (right after allocation or a freelist pop, both
2082    /// of which zero it). Writes the tracked bit with a plain relaxed store
2083    /// instead of `set_gc_tracked`'s `fetch_or`: on the per-allocation hot
2084    /// path, a read-modify-write is measurably pricier than a store even
2085    /// with no contention.
2086    ///
2087    /// # Safety (debug-checked)
2088    /// Caller must ensure `gc_bits` is currently `0`.
2089    #[inline]
2090    pub(crate) fn init_gc_tracked_bit(&self) {
2091        debug_assert_eq!(
2092            self.0.gc_bits.load(Ordering::Relaxed),
2093            0,
2094            "init_gc_tracked_bit called on an object with non-zero gc_bits"
2095        );
2096        self.0
2097            .gc_bits
2098            .store(GcBits::TRACKED.bits(), Ordering::Relaxed);
2099    }
2100
2101    /// _PyObject_GC_UNTRACK
2102    #[inline]
2103    pub(crate) fn clear_gc_tracked(&self) {
2104        self.0
2105            .gc_bits
2106            .fetch_and(!GcBits::TRACKED.bits(), Ordering::Relaxed);
2107    }
2108
2109    #[inline(always)] // the outer function is never inlined
2110    fn drop_slow_inner(&self) -> Result<(), ()> {
2111        // __del__ is mostly not implemented
2112        #[inline(never)]
2113        #[cold]
2114        fn call_slot_del(
2115            zelf: &PyObject,
2116            slot_del: fn(&PyObject, &VirtualMachine) -> PyResult<()>,
2117        ) -> Result<(), ()> {
2118            let ret = crate::vm::thread::with_vm(zelf, |vm| {
2119                // Temporarily resurrect (0→2) so ref_count stays positive
2120                // during __del__, preventing safe_inc from seeing 0.
2121                zelf.0.ref_count.inc_by(2);
2122
2123                let del_method = zelf.get_class_attr(identifier!(vm, __del__)).unwrap();
2124                if let Err(e) = slot_del(zelf, vm) {
2125                    let msg = del_method
2126                        .repr(vm)
2127                        .ok()
2128                        .map(|r| format!("Exception ignored while calling deallocator {r}"));
2129                    vm.run_unraisable(e, msg, del_method);
2130                }
2131
2132                // Undo the temporary resurrection. Always remove both
2133                // temporary refs; the second dec returns true only when
2134                // ref_count drops to 0 (no resurrection).
2135                let _ = zelf.0.ref_count.dec();
2136                zelf.0.ref_count.dec()
2137            });
2138            match ret {
2139                // the decref set ref_count back to 0
2140                Some(true) => Ok(()),
2141                // we've been resurrected by __del__
2142                Some(false) => Err(()),
2143                None => Ok(()),
2144            }
2145        }
2146
2147        // __del__ should only be called once (like _PyGC_FINALIZED check in GIL_DISABLED)
2148        // We call __del__ BEFORE clearing weakrefs to allow the finalizer to access
2149        // the object's weak references if needed.
2150        let del = self.class().slots().del.load();
2151        if let Some(slot_del) = del
2152            && !self.gc_finalized()
2153        {
2154            // Skip the (comparatively expensive) VM attach in `call_slot_del`
2155            // when the type says its `del` is a documented no-op for this
2156            // object right now — e.g. a generator/coroutine that already
2157            // ran to completion. See `PyTypeSlots::del_needed`.
2158            let needs_del = self
2159                .class()
2160                .slots
2161                .del_needed
2162                .load()
2163                .is_none_or(|check| check(self));
2164            if needs_del {
2165                self.set_gc_finalized();
2166                call_slot_del(self, slot_del)?;
2167            }
2168        }
2169
2170        // Clear weak refs AFTER __del__.
2171        // Note: This differs from GC behavior which clears weakrefs before finalizers,
2172        // but for direct deallocation (drop_slow_inner), we need to allow the finalizer
2173        // to run without triggering use-after-free from WeakRefList operations.
2174        if let Some(wrl) = self.weak_ref_list() {
2175            wrl.clear(self);
2176        }
2177
2178        Ok(())
2179    }
2180
2181    /// _Py_Dealloc: dispatch to type's dealloc
2182    #[inline(never)]
2183    unsafe fn drop_slow(ptr: NonNull<Self>) {
2184        let dealloc = unsafe { ptr.as_ref().0.vtable.dealloc };
2185        unsafe { dealloc(ptr.as_ptr()) }
2186    }
2187
2188    /// # Safety
2189    /// This call will make the object live forever: it marks the object both
2190    /// interned and immortal (see [`Self::make_immortal`]), so no `__del__`
2191    /// and no weakref callback will ever run for it.
2192    pub(crate) unsafe fn mark_intern(&self) {
2193        self.0.ref_count.leak();
2194    }
2195
2196    pub(crate) fn is_interned(&self) -> bool {
2197        self.0.ref_count.is_leaked()
2198    }
2199
2200    /// Make this object live for the whole process (PEP 683).
2201    ///
2202    /// Every later reference operation on it becomes a relaxed load and a
2203    /// branch instead of an atomic read-modify-write — and a decref in
2204    /// particular stops paying for a `Release` store, which is the expensive
2205    /// half of the pair on a weakly ordered target. `RefCount`'s `IMMORTAL`
2206    /// documents the full invariant; the parts that bind a caller:
2207    ///
2208    /// * The object is never deallocated, so its `__del__` and its weakref
2209    ///   callbacks never run. Only grant this to something an owner already
2210    ///   keeps for the whole process — a `static_cell`, the [`Context`], the
2211    ///   string pool.
2212    /// * [`Self::strong_count`] reports a number far past any real total, so
2213    ///   the object stays out of every `strong_count() == 1` in-place-mutation
2214    ///   fast path and the cycle collector reads it as a permanent root.
2215    ///
2216    /// This is *not* interning: [`Self::is_interned`] answers from a separate
2217    /// bit and keeps meaning "this string is the pool's copy".
2218    ///
2219    /// [`Context`]: crate::vm::Context
2220    #[inline]
2221    pub fn make_immortal(&self) {
2222        self.0.ref_count.make_immortal();
2223    }
2224
2225    /// Whether this object lives for the whole process.
2226    #[inline(always)]
2227    #[must_use]
2228    pub fn is_immortal(&self) -> bool {
2229        self.0.ref_count.is_immortal()
2230    }
2231
2232    pub(crate) fn get_slot(&self, byte_offset: isize) -> Option<PyObjectRef> {
2233        self.slot_cell_at(byte_offset).load_owned()
2234    }
2235
2236    pub(crate) fn set_slot(&self, byte_offset: isize, value: Option<PyObjectRef>) {
2237        drop(self.slot_cell_at(byte_offset).store(value));
2238    }
2239
2240    fn slot_cell_at(&self, byte_offset: isize) -> &PyAtomicRef<Option<Self>> {
2241        let addr = (self as *const Self as *const u8)
2242            .addr()
2243            .wrapping_add(byte_offset as usize);
2244        let ptr = core::ptr::with_exposed_provenance::<PyAtomicRef<Option<Self>>>(addr);
2245        // SAFETY: `byte_offset` addresses an object-pointer cell. Nullable and
2246        // non-null cells share this layout; member loads go through the nullable view.
2247        unsafe { &*ptr }
2248    }
2249
2250    /// _PyObject_GC_IS_TRACKED
2251    pub fn is_gc_tracked(&self) -> bool {
2252        GcBits::from_bits_retain(self.0.gc_bits.load(Ordering::Relaxed)).contains(GcBits::TRACKED)
2253    }
2254
2255    /// Get the referents (objects directly referenced) of this object.
2256    /// Uses the full traverse including dict and slots.
2257    pub fn gc_get_referents(&self) -> Vec<PyObjectRef> {
2258        let mut result = Vec::new();
2259        self.0.traverse(&mut |child: &Self| {
2260            result.push(child.to_owned());
2261        });
2262        result
2263    }
2264
2265    /// Call __del__ if present, without triggering object deallocation.
2266    /// Used by GC to call finalizers before breaking cycles.
2267    /// This allows proper resurrection detection.
2268    /// PyObject_CallFinalizerFromDealloc
2269    pub fn try_call_finalizer(&self) {
2270        let del = self.class().slots().del.load();
2271        if let Some(slot_del) = del
2272            && !self.gc_finalized()
2273            && self
2274                .class()
2275                .slots
2276                .del_needed
2277                .load()
2278                .is_none_or(|check| check(self))
2279        {
2280            // Mark as finalized BEFORE calling __del__ to prevent double-call
2281            // This ensures drop_slow_inner() won't call __del__ again
2282            self.set_gc_finalized();
2283            let result = crate::vm::thread::with_vm(self, |vm| {
2284                if let Err(e) = slot_del(self, vm)
2285                    && let Some(del_method) = self.get_class_attr(identifier!(vm, __del__))
2286                {
2287                    let msg = del_method
2288                        .repr(vm)
2289                        .ok()
2290                        .map(|r| format!("Exception ignored while calling deallocator {r}"));
2291                    vm.run_unraisable(e, msg, del_method);
2292                }
2293            });
2294            let _ = result;
2295        }
2296    }
2297
2298    /// Clear weakrefs but collect callbacks instead of calling them.
2299    /// This is used by GC to ensure ALL weakrefs are cleared BEFORE any callbacks run.
2300    /// Returns collected callbacks as (PyRef<PyWeak>, callback) pairs.
2301    // = handle_weakrefs
2302    pub fn gc_clear_weakrefs_collect_callbacks(&self) -> Vec<(PyRef<PyWeak>, PyObjectRef)> {
2303        if let Some(wrl) = self.weak_ref_list() {
2304            wrl.clear_for_gc_collect_callbacks(self)
2305        } else {
2306            vec![]
2307        }
2308    }
2309
2310    /// Get raw pointers to referents without incrementing reference counts.
2311    /// This is used during GC to avoid reference count manipulation.
2312    /// tp_traverse visits objects without incref
2313    ///
2314    /// # Safety
2315    /// The returned pointers are only valid as long as the object is alive
2316    /// and its contents haven't been modified.
2317    pub unsafe fn gc_get_referent_ptrs(&self) -> Vec<NonNull<Self>> {
2318        let mut result = Vec::new();
2319        unsafe { self.gc_extend_referent_ptrs(&mut result) };
2320        result
2321    }
2322
2323    /// Append this object's referents to `out`, for a caller that holds many
2324    /// objects' referents in one buffer rather than one buffer each.
2325    ///
2326    /// # Safety
2327    /// Same as [`Self::gc_get_referent_ptrs`].
2328    pub unsafe fn gc_extend_referent_ptrs(&self, out: &mut Vec<NonNull<Self>>) {
2329        // Traverse the entire object including dict and slots
2330        self.0.traverse(&mut |child: &Self| {
2331            out.push(NonNull::from(child));
2332        });
2333    }
2334
2335    /// Pop edges from this object for cycle breaking.
2336    /// Returns extracted child references that were removed from this object (tp_clear).
2337    /// This is used during garbage collection to break circular references.
2338    ///
2339    /// # Safety
2340    /// - ptr must be a valid pointer to a PyObject
2341    /// - The caller must have exclusive access (no other references exist)
2342    /// - This is only safe during GC when the object is unreachable
2343    pub unsafe fn gc_clear_raw(ptr: *mut Self) -> Vec<PyObjectRef> {
2344        let mut result = Vec::new();
2345        let obj = unsafe { &*ptr };
2346
2347        // 1. Clear payload-specific references (vtable.clear / tp_clear)
2348        if let Some(clear_fn) = obj.0.vtable.clear {
2349            unsafe { clear_fn(ptr, &mut result) };
2350        }
2351
2352        // 2. Clear dict and member slots (subtype_clear)
2353        // Detach the dict via Py_CLEAR(*_PyObject_GetDictPtr(self)) — NULL
2354        // the pointer without clearing dict contents. The dict may still be
2355        // referenced by other live objects (e.g. function.__globals__).
2356        let (flags, member_count) = obj.0.read_type_flags();
2357        let has_ext = flags.contains(&crate::types::PyTypeFlags::HAS_DICT) || member_count > 0;
2358        if has_ext {
2359            let self_addr = (ptr as *const u8).addr();
2360            let ext_ptr = core::ptr::with_exposed_provenance_mut::<ObjExt>(
2361                self_addr.wrapping_sub(EXT_OFFSET),
2362            );
2363            let ext = unsafe { &mut *ext_ptr };
2364            if flags.contains(&crate::types::PyTypeFlags::HAS_DICT)
2365                && let Some(dict_ref) = ext.dict.replace(None)
2366            {
2367                result.push(dict_ref.into());
2368            }
2369            for slot in obj.0.slot_cells() {
2370                if let Some(val) = slot.store(None) {
2371                    result.push(val);
2372                }
2373            }
2374        }
2375
2376        result
2377    }
2378
2379    /// Clear this object for cycle breaking (tp_clear).
2380    /// This version takes &self but should only be called during GC
2381    /// when exclusive access is guaranteed.
2382    ///
2383    /// # Safety
2384    /// - The caller must guarantee exclusive access (no other references exist)
2385    /// - This is only safe during GC when the object is unreachable
2386    pub unsafe fn gc_clear(&self) -> Vec<PyObjectRef> {
2387        // SAFETY: During GC collection, this object is unreachable (gc_refs == 0),
2388        // meaning no other code has a reference to it. The only references are
2389        // internal cycle references which we're about to break.
2390        unsafe { Self::gc_clear_raw(self as *const _ as *mut Self) }
2391    }
2392
2393    /// Check if this object has clear capability (tp_clear)
2394    // Py_TPFLAGS_HAVE_GC types have tp_clear
2395    pub fn gc_has_clear(&self) -> bool {
2396        self.0.vtable.clear.is_some()
2397            || self
2398                .0
2399                .read_type_flags()
2400                .0
2401                .contains(&crate::types::PyTypeFlags::HAS_DICT)
2402            || self.0.read_type_flags().1 > 0
2403    }
2404}
2405
2406impl Borrow<PyObject> for PyObjectRef {
2407    #[inline(always)]
2408    fn borrow(&self) -> &PyObject {
2409        self
2410    }
2411}
2412
2413impl AsRef<PyObject> for PyObjectRef {
2414    #[inline(always)]
2415    fn as_ref(&self) -> &PyObject {
2416        self
2417    }
2418}
2419
2420impl<'a, T: PyPayload> From<&'a Py<T>> for &'a PyObject {
2421    #[inline(always)]
2422    fn from(py_ref: &'a Py<T>) -> Self {
2423        py_ref.as_object()
2424    }
2425}
2426
2427impl Drop for PyObjectRef {
2428    #[inline]
2429    fn drop(&mut self) {
2430        if self.0.ref_count.dec() {
2431            unsafe { PyObject::drop_slow(self.ptr) }
2432        }
2433    }
2434}
2435
2436impl fmt::Debug for PyObject {
2437    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2438        // SAFETY: the vtable contains functions that accept payload types that always match up
2439        // with the payload of the object
2440        unsafe { (self.0.vtable.debug)(self, f) }
2441    }
2442}
2443
2444impl fmt::Debug for PyObjectRef {
2445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2446        self.as_object().fmt(f)
2447    }
2448}
2449
2450const STACKREF_BORROW_TAG: usize = 1;
2451
2452/// A tagged stack reference to a Python object.
2453///
2454/// Uses the lowest bit of the pointer to distinguish owned vs borrowed:
2455/// - bit 0 = 0 → **owned**: refcount was incremented; Drop will decrement.
2456/// - bit 0 = 1 → **borrowed**: no refcount change; Drop is a no-op.
2457///
2458/// Same size as `PyObjectRef` (one pointer-width).  `PyObject` is at least
2459/// 8-byte aligned, so the low bit is always available for tagging.
2460///
2461/// Uses `NonZeroUsize` so that `Option<PyStackRef>` has the same size as
2462/// `PyStackRef` via niche optimization (matching `Option<PyObjectRef>`).
2463///
2464/// # The borrow invariant
2465///
2466/// A borrowed entry keeps no strong count of its own, so something else has to
2467/// keep the object alive for as long as the entry sits on the value stack.
2468/// Two producers create them, each with its own reason:
2469///
2470/// * `LOAD_SMALL_INT` and friends borrow objects the `Context` owns for the
2471///   whole life of the interpreter, so nothing can free them.
2472/// * `LOAD_FAST_BORROW` borrows the object in a fastlocals slot of the frame
2473///   that is executing. The slot holds the strong count. The codegen pass
2474///   `optimize_load_fast` (`crates/codegen/src/ir.rs`, a port of CPython's
2475///   `flowgraph.c`) only rewrites `LOAD_FAST` into `LOAD_FAST_BORROW` when it
2476///   can prove, over the basic block, that the pushed entry is consumed before
2477///   anything stores to or deletes that local, and before the entry could be
2478///   stored into the local itself.
2479///
2480/// Three rules keep the runtime side of that bargain:
2481///
2482/// 1. Anything that makes a borrowed entry outlive the block it was pushed in
2483///    must promote it first (`LocalsPlus::promote_stack`, run at every yield
2484///    point). A frame that suspends keeps its own fastlocals, so this is
2485///    belt-and-braces, but a stack that is copied out of the frame is not.
2486/// 2. The cycle collector must not count a borrowed entry as an edge; see
2487///    `Traverse for PyStackRef`.
2488/// 3. Anything that overwrites a fastlocals slot from outside the eval loop --
2489///    `frame.f_locals` write-back -- must keep the displaced value alive
2490///    (`f_overwritten_fast_locals`) rather than dropping it in place.
2491#[repr(transparent)]
2492pub struct PyStackRef {
2493    bits: NonZeroUsize,
2494}
2495
2496impl PyStackRef {
2497    /// Create an owned stack reference, consuming the `PyObjectRef`.
2498    /// Refcount is NOT incremented — ownership is transferred.
2499    #[inline(always)]
2500    #[must_use]
2501    pub fn new_owned(obj: PyObjectRef) -> Self {
2502        let ptr = obj.into_raw();
2503        let bits = ptr.as_ptr() as usize;
2504        debug_assert!(
2505            bits & STACKREF_BORROW_TAG == 0,
2506            "PyObject pointer must be aligned"
2507        );
2508        Self {
2509            // SAFETY: valid PyObject pointers are never null
2510            bits: unsafe { NonZeroUsize::new_unchecked(bits) },
2511        }
2512    }
2513
2514    /// Create a borrowed stack reference from a `&PyObject`.
2515    ///
2516    /// # Safety
2517    /// The caller must guarantee that the pointed-to object lives at least as
2518    /// long as this `PyStackRef`.  In practice the compiler guarantees that
2519    /// borrowed refs are consumed within the same basic block, before any
2520    /// `STORE_FAST`/`DELETE_FAST` could overwrite the source slot.
2521    #[inline(always)]
2522    pub unsafe fn new_borrowed(obj: &PyObject) -> Self {
2523        let bits = (obj as *const PyObject as usize) | STACKREF_BORROW_TAG;
2524        Self {
2525            // SAFETY: valid PyObject pointers are never null, and ORing with 1 keeps it non-zero
2526            bits: unsafe { NonZeroUsize::new_unchecked(bits) },
2527        }
2528    }
2529
2530    /// Whether this is a borrowed (non-owning) reference.
2531    #[inline(always)]
2532    #[must_use]
2533    pub fn is_borrowed(&self) -> bool {
2534        self.bits.get() & STACKREF_BORROW_TAG != 0
2535    }
2536
2537    /// Get a `&PyObject` reference.  Works for both owned and borrowed.
2538    #[inline(always)]
2539    #[must_use]
2540    pub fn as_object(&self) -> &PyObject {
2541        unsafe { &*((self.bits.get() & !STACKREF_BORROW_TAG) as *const PyObject) }
2542    }
2543
2544    /// Convert to an owned `PyObjectRef`.
2545    ///
2546    /// * If **borrowed** → increments refcount, forgets self.
2547    /// * If **owned** → reconstructs `PyObjectRef` from the raw pointer, forgets self.
2548    #[inline(always)]
2549    #[must_use]
2550    pub fn to_pyobj(self) -> PyObjectRef {
2551        let obj = if self.is_borrowed() {
2552            self.as_object().to_owned() // inc refcount
2553        } else {
2554            let ptr = unsafe { NonNull::new_unchecked(self.bits.get() as *mut PyObject) };
2555            unsafe { PyObjectRef::from_raw(ptr) }
2556        };
2557        core::mem::forget(self); // don't run Drop
2558        obj
2559    }
2560
2561    /// Promote a borrowed ref to owned **in place** (increments refcount,
2562    /// clears the borrow tag).  No-op if already owned.
2563    #[inline(always)]
2564    pub fn promote(&mut self) {
2565        if self.is_borrowed() {
2566            self.as_object().0.ref_count.inc();
2567            // SAFETY: clearing the low bit of a non-null pointer keeps it non-zero
2568            self.bits =
2569                unsafe { NonZeroUsize::new_unchecked(self.bits.get() & !STACKREF_BORROW_TAG) };
2570        }
2571    }
2572}
2573
2574impl Drop for PyStackRef {
2575    #[inline]
2576    fn drop(&mut self) {
2577        if !self.is_borrowed() {
2578            // Owned: decrement refcount (potentially deallocate).
2579            let ptr = unsafe { NonNull::new_unchecked(self.bits.get() as *mut PyObject) };
2580            drop(unsafe { PyObjectRef::from_raw(ptr) });
2581        }
2582        // Borrowed: nothing to do.
2583    }
2584}
2585
2586impl core::ops::Deref for PyStackRef {
2587    type Target = PyObject;
2588
2589    #[inline(always)]
2590    fn deref(&self) -> &PyObject {
2591        self.as_object()
2592    }
2593}
2594
2595impl Clone for PyStackRef {
2596    /// Cloning always produces an **owned** reference (increments refcount).
2597    #[inline(always)]
2598    fn clone(&self) -> Self {
2599        Self::new_owned(self.as_object().to_owned())
2600    }
2601}
2602
2603impl fmt::Debug for PyStackRef {
2604    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2605        if self.is_borrowed() {
2606            write!(f, "PyStackRef(borrowed, ")?;
2607        } else {
2608            write!(f, "PyStackRef(owned, ")?;
2609        }
2610        self.as_object().fmt(f)?;
2611        write!(f, ")")
2612    }
2613}
2614
2615cfg_select! {
2616    feature = "threading" => {
2617        unsafe impl Send for PyStackRef {}
2618        unsafe impl Sync for PyStackRef {}
2619    }
2620    _ => {}
2621}
2622
2623// Ensure Option<PyStackRef> uses niche optimization and matches Option<PyObjectRef> in size
2624const _: () = assert!(
2625    core::mem::size_of::<Option<PyStackRef>>() == core::mem::size_of::<Option<PyObjectRef>>()
2626);
2627const _: () =
2628    assert!(core::mem::size_of::<Option<PyStackRef>>() == core::mem::size_of::<PyStackRef>());
2629
2630impl<T: PyPayload> Py<T> {
2631    pub fn downgrade(
2632        &self,
2633        callback: Option<PyObjectRef>,
2634        vm: &VirtualMachine,
2635    ) -> PyResult<PyWeakRef<T>> {
2636        Ok(PyWeakRef {
2637            weak: self.as_object().downgrade(callback, vm)?,
2638            _marker: PhantomData,
2639        })
2640    }
2641
2642    #[inline]
2643    pub fn payload(&self) -> &T {
2644        &self.payload
2645    }
2646
2647    /// Recover the object pointer from a pointer to its `payload` field.
2648    ///
2649    /// # Safety
2650    /// `payload` must point to the `payload` of a live `Py<T>` (e.g. a `&T`
2651    /// obtained by dereferencing a `Py<T>`), and the object must outlive the
2652    /// returned pointer's use.
2653    #[inline]
2654    #[cfg_attr(not(feature = "threading"), allow(dead_code))]
2655    pub(crate) unsafe fn from_payload_ptr(payload: *const T) -> *const Self {
2656        let offset = core::mem::offset_of!(Self, payload);
2657        unsafe { (payload as *const u8).sub(offset) as *const Self }
2658    }
2659}
2660
2661impl<T> ToOwned for Py<T> {
2662    type Owned = PyRef<T>;
2663
2664    #[inline(always)]
2665    fn to_owned(&self) -> Self::Owned {
2666        self.ref_count.inc();
2667        PyRef {
2668            ptr: NonNull::from(self),
2669        }
2670    }
2671}
2672
2673impl<T> Deref for Py<T> {
2674    type Target = T;
2675
2676    #[inline(always)]
2677    fn deref(&self) -> &Self::Target {
2678        &self.payload
2679    }
2680}
2681
2682impl<T: PyPayload> Borrow<PyObject> for Py<T> {
2683    #[inline(always)]
2684    fn borrow(&self) -> &PyObject {
2685        unsafe { &*(self as *const Self as *const PyObject) }
2686    }
2687}
2688
2689impl<T> core::hash::Hash for Py<T>
2690where
2691    T: core::hash::Hash + PyPayload,
2692{
2693    #[inline]
2694    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2695        self.deref().hash(state)
2696    }
2697}
2698
2699impl<T> PartialEq for Py<T>
2700where
2701    T: PartialEq + PyPayload,
2702{
2703    #[inline]
2704    fn eq(&self, other: &Self) -> bool {
2705        self.deref().eq(&**other)
2706    }
2707}
2708
2709impl<T> Eq for Py<T> where T: Eq + PyPayload {}
2710
2711impl<T> AsRef<PyObject> for Py<T>
2712where
2713    T: PyPayload,
2714{
2715    #[inline(always)]
2716    fn as_ref(&self) -> &PyObject {
2717        self.borrow()
2718    }
2719}
2720
2721impl<T: PyPayload + core::fmt::Debug> fmt::Debug for Py<T> {
2722    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2723        (**self).fmt(f)
2724    }
2725}
2726
2727/// A reference to a Python object.
2728///
2729/// Note that a `PyRef<T>` can only deref to a shared / immutable reference.
2730/// It is the payload type's responsibility to handle (possibly concurrent)
2731/// mutability with locks or concurrent data structures if required.
2732///
2733/// A `PyRef<T>` can be directly returned from a built-in function to handle
2734/// situations (such as when implementing in-place methods such as `__iadd__`)
2735/// where a reference to the same object must be returned.
2736#[repr(transparent)]
2737pub struct PyRef<T> {
2738    ptr: NonNull<Py<T>>,
2739}
2740
2741cfg_select! {
2742    feature = "threading" => {
2743        unsafe impl<T> Send for PyRef<T> {}
2744        unsafe impl<T> Sync for PyRef<T> {}
2745    }
2746    _ => {}
2747}
2748
2749impl<T: fmt::Debug> fmt::Debug for PyRef<T> {
2750    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2751        (**self).fmt(f)
2752    }
2753}
2754
2755impl<T> Drop for PyRef<T> {
2756    #[inline]
2757    fn drop(&mut self) {
2758        if self.ref_count.dec() {
2759            unsafe { PyObject::drop_slow(self.ptr.cast::<PyObject>()) }
2760        }
2761    }
2762}
2763
2764impl<T> Clone for PyRef<T> {
2765    #[inline(always)]
2766    fn clone(&self) -> Self {
2767        (**self).to_owned()
2768    }
2769}
2770
2771impl<T: PyPayload> PyRef<T> {
2772    #[inline(always)]
2773    pub(super) const fn into_non_null(self) -> NonNull<Py<T>> {
2774        let ptr = self.ptr;
2775        core::mem::forget(self);
2776        ptr
2777    }
2778
2779    /// # Safety
2780    /// The raw pointer must point to a valid `Py<T>` object
2781    #[must_use]
2782    #[inline(always)]
2783    pub const unsafe fn from_non_null(ptr: NonNull<Py<T>>) -> Self {
2784        Self { ptr }
2785    }
2786
2787    /// # Safety
2788    /// The raw pointer must point to a valid `Py<T>` object
2789    #[inline(always)]
2790    pub const unsafe fn from_raw(raw: *const Py<T>) -> Self {
2791        unsafe { Self::from_non_null(NonNull::new_unchecked(raw as *mut _)) }
2792    }
2793
2794    /// Safety: payload type of `obj` must be `T`
2795    #[inline(always)]
2796    unsafe fn from_obj_unchecked(obj: PyObjectRef) -> Self {
2797        debug_assert!(obj.downcast_ref::<T>().is_some());
2798        let obj = ManuallyDrop::new(obj);
2799        Self {
2800            ptr: obj.ptr.cast(),
2801        }
2802    }
2803
2804    #[must_use]
2805    pub const fn leak(pyref: Self) -> &'static Py<T> {
2806        let ptr = pyref.ptr;
2807        core::mem::forget(pyref);
2808        unsafe { ptr.as_ref() }
2809    }
2810}
2811
2812impl<T: PyPayload + crate::object::MaybeTraverse + core::fmt::Debug> PyRef<T> {
2813    #[inline(always)]
2814    pub fn new_ref(payload: T, typ: crate::builtins::PyTypeRef, dict: Option<PyDictRef>) -> Self {
2815        let has_dict = dict.is_some();
2816        let is_heaptype = typ.heaptype_ext.is_some();
2817
2818        // Try to reuse from freelist (no dict, no heaptype)
2819        let cached = if !has_dict && !is_heaptype {
2820            unsafe { T::freelist_pop(&payload) }
2821        } else {
2822            None
2823        };
2824
2825        let ptr = if let Some(cached) = cached {
2826            let inner = cached.as_ptr() as *mut Py<T>;
2827            unsafe {
2828                core::ptr::write(&mut (*inner).ref_count, RefCount::new());
2829                (*inner).gc_bits.store(0, Ordering::Relaxed);
2830                core::ptr::drop_in_place(&mut (*inner).payload);
2831                core::ptr::write(&mut (*inner).payload, payload);
2832                // Freelist only stores exact base types (push-side filter),
2833                // but subtypes sharing the same Rust payload (e.g. structseq)
2834                // may pop entries. Update typ if it differs.
2835                let cached_typ: *const Py<PyType> = &*(*inner).typ;
2836                if core::ptr::eq(cached_typ, &*typ) {
2837                    drop(typ);
2838                } else {
2839                    let _old = (*inner).typ.swap(typ);
2840                }
2841            }
2842            unsafe { NonNull::new_unchecked(inner.cast::<Py<T>>()) }
2843        } else {
2844            let inner = Py::new(payload, typ, dict);
2845            unsafe { NonNull::new_unchecked(inner.cast::<Py<T>>()) }
2846        };
2847
2848        // Track object if:
2849        // - HAS_TRAVERSE is true (Rust payload implements Traverse), OR
2850        // - has instance dict (user-defined class instances), OR
2851        // - heap type (all heap type instances are GC-tracked, like Py_TPFLAGS_HAVE_GC)
2852        // unless the payload opts out via NEW_REF_UNTRACKED (e.g. call frames,
2853        // which are tracked lazily only on escape).
2854        if (<T as crate::object::MaybeTraverse>::HAS_TRAVERSE || has_dict || is_heaptype)
2855            && !T::NEW_REF_UNTRACKED
2856        {
2857            // Tracks under the interpreter running now and collects if this
2858            // allocation pushed gen0 past its threshold.
2859            unsafe {
2860                crate::gc_state::track_new_object(ptr.cast());
2861            }
2862        }
2863
2864        Self { ptr }
2865    }
2866}
2867
2868impl<T: crate::class::PySubclass + core::fmt::Debug> PyRef<T>
2869where
2870    T::Base: core::fmt::Debug,
2871{
2872    /// Converts this reference to the base type (ownership transfer).
2873    /// # Safety
2874    /// T and T::Base must have compatible layouts in size_of::<T::Base>() bytes.
2875    #[inline]
2876    #[must_use]
2877    pub fn into_base(self) -> PyRef<T::Base> {
2878        let obj: PyObjectRef = self.into();
2879        match obj.downcast() {
2880            Ok(base_ref) => base_ref,
2881            Err(_) => unsafe { core::hint::unreachable_unchecked() },
2882        }
2883    }
2884    #[inline]
2885    #[must_use]
2886    pub fn upcast<U: PyPayload + StaticType>(self) -> PyRef<U>
2887    where
2888        T: StaticType,
2889    {
2890        debug_assert!(T::static_type().is_subtype(U::static_type()));
2891        let obj: PyObjectRef = self.into();
2892        match obj.downcast::<U>() {
2893            Ok(upcast_ref) => upcast_ref,
2894            Err(_) => unsafe { core::hint::unreachable_unchecked() },
2895        }
2896    }
2897}
2898
2899impl<T: crate::class::PySubclass> Py<T> {
2900    /// Converts `&Py<T>` to `&Py<T::Base>`.
2901    #[inline]
2902    pub fn to_base(&self) -> &Py<T::Base> {
2903        debug_assert!(self.as_object().downcast_ref::<T::Base>().is_some());
2904        // SAFETY: T is #[repr(transparent)] over T::Base,
2905        // so Py<T> and Py<T::Base> have the same layout.
2906        unsafe { &*(self as *const Self as *const Py<T::Base>) }
2907    }
2908
2909    /// Converts `&Py<T>` to `&Py<U>` where U is an ancestor type.
2910    #[inline]
2911    pub fn upcast_ref<U: PyPayload + StaticType>(&self) -> &Py<U>
2912    where
2913        T: StaticType,
2914    {
2915        debug_assert!(T::static_type().is_subtype(U::static_type()));
2916        // SAFETY: T is a subtype of U, so Py<T> can be viewed as Py<U>.
2917        unsafe { &*(self as *const Self as *const Py<U>) }
2918    }
2919}
2920
2921impl<T> Borrow<PyObject> for PyRef<T>
2922where
2923    T: PyPayload,
2924{
2925    #[inline(always)]
2926    fn borrow(&self) -> &PyObject {
2927        (**self).as_object()
2928    }
2929}
2930
2931impl<T> AsRef<PyObject> for PyRef<T>
2932where
2933    T: PyPayload,
2934{
2935    #[inline(always)]
2936    fn as_ref(&self) -> &PyObject {
2937        self.borrow()
2938    }
2939}
2940
2941impl<T> From<PyRef<T>> for PyObjectRef {
2942    #[inline]
2943    fn from(value: PyRef<T>) -> Self {
2944        let me = ManuallyDrop::new(value);
2945        Self { ptr: me.ptr.cast() }
2946    }
2947}
2948
2949impl<T> Borrow<Py<T>> for PyRef<T> {
2950    #[inline(always)]
2951    fn borrow(&self) -> &Py<T> {
2952        self
2953    }
2954}
2955
2956impl<T> AsRef<Py<T>> for PyRef<T> {
2957    #[inline(always)]
2958    fn as_ref(&self) -> &Py<T> {
2959        self
2960    }
2961}
2962
2963impl<T> Deref for PyRef<T> {
2964    type Target = Py<T>;
2965
2966    #[inline(always)]
2967    fn deref(&self) -> &Py<T> {
2968        unsafe { self.ptr.as_ref() }
2969    }
2970}
2971
2972impl<T> core::hash::Hash for PyRef<T>
2973where
2974    T: core::hash::Hash + PyPayload,
2975{
2976    #[inline]
2977    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
2978        self.deref().hash(state)
2979    }
2980}
2981
2982impl<T> PartialEq for PyRef<T>
2983where
2984    T: PartialEq + PyPayload,
2985{
2986    #[inline]
2987    fn eq(&self, other: &Self) -> bool {
2988        self.deref().eq(&**other)
2989    }
2990}
2991
2992impl<T> Eq for PyRef<T> where T: Eq + PyPayload {}
2993
2994#[repr(transparent)]
2995pub struct PyWeakRef<T: PyPayload> {
2996    weak: PyRef<PyWeak>,
2997    _marker: PhantomData<T>,
2998}
2999
3000impl<T: PyPayload> PyWeakRef<T> {
3001    #[must_use]
3002    pub fn upgrade(&self) -> Option<PyRef<T>> {
3003        self.weak
3004            .upgrade()
3005            // SAFETY: PyWeakRef<T> was always created from a PyRef<T>, so the object is T
3006            .map(|obj| unsafe { PyRef::from_obj_unchecked(obj) })
3007    }
3008}
3009
3010/// Partially initialize a struct, ensuring that all fields are
3011/// either given values or explicitly left uninitialized
3012pub(crate) struct BootstrapTypeHierarchy {
3013    pub type_type: PyTypeRef,
3014    pub object_type: PyTypeRef,
3015    pub tuple_type: PyTypeRef,
3016    pub weakref_type: PyTypeRef,
3017    pub empty_tuple: PyTupleRef,
3018}
3019
3020pub(crate) fn init_type_hierarchy() -> BootstrapTypeHierarchy {
3021    use crate::{
3022        builtins::{object, tuple},
3023        class::PyClassImpl,
3024    };
3025    use core::mem::MaybeUninit;
3026
3027    static_assertions::assert_eq_size!(MaybeUninit<Py<PyType>>, Py<PyType>);
3028    static_assertions::assert_eq_align!(MaybeUninit<Py<PyType>>, Py<PyType>);
3029    static_assertions::assert_eq_size!(MaybeUninit<Py<PyTuple>>, Py<PyTuple>);
3030    static_assertions::assert_eq_align!(MaybeUninit<Py<PyTuple>>, Py<PyTuple>);
3031
3032    // All three core type objects are instances of `type`, which has HAS_DICT
3033    // and HAS_WEAKREF. Their allocations therefore need both prefixes,
3034    // with the weakref list in front of ObjExt.
3035    let alloc_type_with_prefixes = || -> *mut Py<PyType> {
3036        let inner_layout = core::alloc::Layout::new::<MaybeUninit<Py<PyType>>>();
3037        let ext_layout = core::alloc::Layout::new::<ObjExt>();
3038        let weakref_layout = core::alloc::Layout::new::<WeakRefList>();
3039
3040        // [WeakRefList][ObjExt][Py] — the list stays in front of ObjExt.
3041        let (layout, ext_offset) = weakref_layout.extend(ext_layout).unwrap();
3042        let (combined, inner_offset) = layout.extend(inner_layout).unwrap();
3043        let combined = combined.pad_to_align();
3044
3045        let alloc_ptr = unsafe { alloc::alloc::alloc(combined) };
3046        if alloc_ptr.is_null() {
3047            alloc::alloc::handle_alloc_error(combined);
3048        }
3049        alloc_ptr.expose_provenance();
3050
3051        unsafe {
3052            (alloc_ptr as *mut WeakRefList).write(WeakRefList::new());
3053            (alloc_ptr.add(ext_offset) as *mut ObjExt).write(ObjExt::new(None, true, false));
3054            alloc_ptr.add(inner_offset).cast()
3055        }
3056    };
3057
3058    let alloc_tuple =
3059        || Box::into_raw(Box::new(MaybeUninit::<Py<PyTuple>>::uninit())).cast::<Py<PyTuple>>();
3060
3061    unsafe fn init_ref_count<T>(ptr: *mut Py<T>) {
3062        unsafe { ptr::addr_of_mut!((*ptr).ref_count).write(RefCount::new()) };
3063    }
3064
3065    unsafe fn initial_ref<T: PyPayload>(ptr: *mut Py<T>) -> PyRef<T> {
3066        unsafe { PyRef::from_raw(ptr.cast()) }
3067    }
3068
3069    unsafe fn clone_raw_ref<T: PyPayload>(ptr: *mut Py<T>) -> PyRef<T> {
3070        unsafe { &*ptr::addr_of!((*ptr).ref_count) }.inc();
3071        unsafe { PyRef::from_raw(ptr.cast()) }
3072    }
3073
3074    unsafe fn into_type_tuple(tuple: PyTupleRef) -> PyTypeTupleRef {
3075        // SAFETY: PyTypeRef and PyObjectRef have the same layout, and the
3076        // bootstrap tuples contain only PyType objects.
3077        unsafe { core::mem::transmute::<PyTupleRef, PyTypeTupleRef>(tuple) }
3078    }
3079
3080    unsafe fn init_inner<T>(ptr: *mut Py<T>, typ: PyTypeRef, payload: T)
3081    where
3082        T: PyPayload + MaybeTraverse + fmt::Debug,
3083    {
3084        unsafe {
3085            ptr::addr_of_mut!((*ptr).vtable).write(PyObjVTable::of::<T>());
3086            ptr::addr_of_mut!((*ptr).gc_bits).write(Radium::new(0));
3087            ptr::addr_of_mut!((*ptr).gc_generation).write(Radium::new(GC_UNTRACKED));
3088            ptr::addr_of_mut!((*ptr).gc_owner).write(Radium::new(GC_NO_OWNER));
3089            ptr::addr_of_mut!((*ptr).gc_refs).write(Radium::new(0));
3090            ptr::addr_of_mut!((*ptr).gc_pointers).write(Pointers::new());
3091            ptr::addr_of_mut!((*ptr).typ).write(PyAtomicRef::from_ref_without_retag(typ));
3092            ptr::addr_of_mut!((*ptr).payload).write(payload);
3093        }
3094    }
3095
3096    let type_type_ptr = alloc_type_with_prefixes();
3097    let object_type_ptr = alloc_type_with_prefixes();
3098    let tuple_type_ptr = alloc_type_with_prefixes();
3099    let empty_tuple_ptr = alloc_tuple();
3100    let type_bases_ptr = alloc_tuple();
3101    let tuple_bases_ptr = alloc_tuple();
3102
3103    unsafe {
3104        init_ref_count(type_type_ptr);
3105        init_ref_count(object_type_ptr);
3106        init_ref_count(tuple_type_ptr);
3107        init_ref_count(empty_tuple_ptr);
3108        init_ref_count(type_bases_ptr);
3109        init_ref_count(tuple_bases_ptr);
3110    }
3111
3112    // Each initial reference consumes the allocation's initial strong count.
3113    // Further references are created through clone_raw_ref while the graph is
3114    // still being assembled and cannot yet be safely dereferenced.
3115    let type_type = unsafe { initial_ref(type_type_ptr) };
3116    let object_type = unsafe { initial_ref(object_type_ptr) };
3117    let tuple_type = unsafe { initial_ref(tuple_type_ptr) };
3118    let empty_tuple = unsafe { initial_ref(empty_tuple_ptr) };
3119    let type_bases = unsafe { into_type_tuple(initial_ref(type_bases_ptr)) };
3120    let tuple_bases = unsafe { into_type_tuple(initial_ref(tuple_bases_ptr)) };
3121
3122    let type_payload = PyType {
3123        base: unsafe {
3124            PyAtomicRef::from_optional_ref_without_retag(Some(clone_raw_ref(object_type_ptr)))
3125        },
3126        bases: PyRwLock::new(type_bases),
3127        mro: PyRwLock::new(vec![unsafe { clone_raw_ref(type_type_ptr) }, unsafe {
3128            clone_raw_ref(object_type_ptr)
3129        }]),
3130        subclasses: PyRwLock::default(),
3131        attributes: Default::default(),
3132        slots: PyType::make_slots(),
3133        heaptype_ext: None,
3134        tp_version_tag: core::sync::atomic::AtomicU32::new(0),
3135    };
3136    let object_payload = PyType {
3137        base: unsafe { PyAtomicRef::from_optional_ref_without_retag(None) },
3138        bases: PyRwLock::new(unsafe { into_type_tuple(clone_raw_ref(empty_tuple_ptr)) }),
3139        mro: PyRwLock::new(vec![unsafe { clone_raw_ref(object_type_ptr) }]),
3140        subclasses: PyRwLock::default(),
3141        attributes: Default::default(),
3142        slots: object::PyBaseObject::make_slots(),
3143        heaptype_ext: None,
3144        tp_version_tag: core::sync::atomic::AtomicU32::new(0),
3145    };
3146    let tuple_payload = PyType {
3147        base: unsafe {
3148            PyAtomicRef::from_optional_ref_without_retag(Some(clone_raw_ref(object_type_ptr)))
3149        },
3150        bases: PyRwLock::new(tuple_bases),
3151        mro: PyRwLock::new(vec![unsafe { clone_raw_ref(tuple_type_ptr) }, unsafe {
3152            clone_raw_ref(object_type_ptr)
3153        }]),
3154        subclasses: PyRwLock::default(),
3155        attributes: Default::default(),
3156        slots: tuple::PyTuple::make_slots(),
3157        heaptype_ext: None,
3158        tp_version_tag: core::sync::atomic::AtomicU32::new(0),
3159    };
3160
3161    let object_element =
3162        || -> PyObjectRef { unsafe { clone_raw_ref::<PyType>(object_type_ptr) }.into() };
3163    unsafe {
3164        init_inner(type_type_ptr, clone_raw_ref(type_type_ptr), type_payload);
3165        init_inner(
3166            object_type_ptr,
3167            clone_raw_ref(type_type_ptr),
3168            object_payload,
3169        );
3170        init_inner(tuple_type_ptr, clone_raw_ref(type_type_ptr), tuple_payload);
3171        init_inner(
3172            empty_tuple_ptr,
3173            clone_raw_ref(tuple_type_ptr),
3174            PyTuple::new_unchecked(Vec::new().into_boxed_slice()),
3175        );
3176        init_inner(
3177            type_bases_ptr,
3178            clone_raw_ref(tuple_type_ptr),
3179            PyTuple::new_unchecked(vec![object_element()].into_boxed_slice()),
3180        );
3181        init_inner(
3182            tuple_bases_ptr,
3183            clone_raw_ref(tuple_type_ptr),
3184            PyTuple::new_unchecked(vec![object_element()].into_boxed_slice()),
3185        );
3186    }
3187
3188    PyType::finalize_bootstrap_static(&tuple_type);
3189
3190    let weakref_bases =
3191        PyTuple::new_ref_typed_with_type(vec![object_type.clone()], tuple_type.clone());
3192    unsafe {
3193        crate::gc_state::gc_state()
3194            .untrack_object(NonNull::from(weakref_bases.as_untyped().as_object()));
3195    }
3196    weakref_bases.as_untyped().as_object().clear_gc_tracked();
3197    let weakref_payload = PyType {
3198        base: Some(object_type.clone()).into(),
3199        bases: PyRwLock::new(weakref_bases),
3200        mro: PyRwLock::new(vec![object_type.clone()]),
3201        subclasses: PyRwLock::default(),
3202        attributes: Default::default(),
3203        slots: PyWeak::make_slots(),
3204        heaptype_ext: None,
3205        tp_version_tag: core::sync::atomic::AtomicU32::new(0),
3206    };
3207    let weakref_type = PyRef::new_ref(weakref_payload, type_type.clone(), None);
3208    // Static type: untrack from GC (was tracked by new_ref because PyType has HAS_TRAVERSE)
3209    unsafe {
3210        crate::gc_state::gc_state()
3211            .untrack_object(core::ptr::NonNull::from(weakref_type.as_object()));
3212    }
3213    weakref_type.as_object().clear_gc_tracked();
3214    // weakref's mro is [weakref, object]
3215    weakref_type.mro.write().insert(0, weakref_type.clone());
3216
3217    object_type.subclasses.write().push(
3218        type_type
3219            .as_object()
3220            .downgrade_with_weakref_typ_opt(None, weakref_type.clone())
3221            .unwrap(),
3222    );
3223
3224    object_type.subclasses.write().push(
3225        tuple_type
3226            .as_object()
3227            .downgrade_with_weakref_typ_opt(None, weakref_type.clone())
3228            .unwrap(),
3229    );
3230
3231    object_type.subclasses.write().push(
3232        weakref_type
3233            .as_object()
3234            .downgrade_with_weakref_typ_opt(None, weakref_type.clone())
3235            .unwrap(),
3236    );
3237
3238    BootstrapTypeHierarchy {
3239        type_type,
3240        object_type,
3241        tuple_type,
3242        weakref_type,
3243        empty_tuple,
3244    }
3245}
3246
3247#[cfg(test)]
3248mod tests {
3249    use super::*;
3250
3251    #[test]
3252    fn native_type_basicsize_includes_payload_padding() {
3253        use crate::class::PyClassDef;
3254
3255        #[pyclass(module = false, name = "PaddedPayload")]
3256        #[derive(Debug, PyPayload)]
3257        #[repr(align(64))]
3258        struct PaddedPayload;
3259
3260        #[pyclass]
3261        impl PaddedPayload {}
3262
3263        assert_eq!(
3264            PaddedPayload::BASICSIZE,
3265            core::mem::size_of::<Py<PaddedPayload>>()
3266        );
3267    }
3268
3269    #[test]
3270    fn native_subclass_inherits_getter_with_mixed_field_sizes() {
3271        use crate::class::PyClassImpl;
3272
3273        #[pyclass(module = false, name = "LayoutBase")]
3274        #[derive(Debug, PyPayload)]
3275        // Keep the base and derived payload aligned alike on 32-bit targets too.
3276        #[repr(align(8))]
3277        struct LayoutBase {
3278            value: PyObjectRef,
3279        }
3280
3281        #[pyclass(flags(BASETYPE))]
3282        impl Py<LayoutBase> {
3283            #[pygetset]
3284            fn value(&self) -> PyObjectRef {
3285                self.value.clone()
3286            }
3287        }
3288
3289        #[pyclass(module = false, name = "LayoutDerived", base = LayoutBase)]
3290        #[derive(Debug)]
3291        struct LayoutDerived {
3292            base: LayoutBase,
3293            extra: Option<u64>,
3294        }
3295
3296        #[pyclass]
3297        impl LayoutDerived {
3298            #[pygetset]
3299            fn extra(zelf: &Py<Self>) -> Option<u64> {
3300                zelf.extra
3301            }
3302        }
3303
3304        assert_eq!(core::mem::offset_of!(LayoutDerived, base), 0);
3305        crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
3306            let _ = LayoutBase::make_static_type();
3307            let _ = LayoutDerived::make_static_type();
3308            let value: PyObjectRef = vm.ctx.new_int(42).into();
3309            let obj = vm.new_pyobj(LayoutDerived {
3310                base: LayoutBase {
3311                    value: value.clone(),
3312                },
3313                extra: Some(99),
3314            });
3315            assert!(obj.get_attr("value", vm).unwrap().is(&value));
3316            assert_eq!(
3317                obj.get_attr("extra", vm)
3318                    .unwrap()
3319                    .try_to_value::<u64>(vm)
3320                    .unwrap(),
3321                99
3322            );
3323        });
3324    }
3325
3326    #[test]
3327    fn clear_reuses_storage_and_preserves_existing_edges() {
3328        use crate::builtins::PyList;
3329
3330        crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
3331            for tuple in [false, true] {
3332                for existing in [false, true] {
3333                    let elements = vec![vm.ctx.none(), vm.ctx.none()];
3334                    let allocation = elements.as_ptr();
3335                    let mut sequence: Box<dyn Traverse> = if tuple {
3336                        Box::new(PyTuple::new_unchecked(elements.into_boxed_slice()))
3337                    } else {
3338                        Box::new(PyList::from(elements))
3339                    };
3340                    let mut out = if existing {
3341                        vec![vm.ctx.new_int(1).into()]
3342                    } else {
3343                        Vec::new()
3344                    };
3345                    sequence.clear(&mut out);
3346                    if existing {
3347                        assert_eq!(out[0].try_to_value::<i32>(vm).unwrap(), 1);
3348                    } else {
3349                        assert_eq!(out.as_ptr(), allocation);
3350                    }
3351                    assert_eq!(out.len(), 2 + usize::from(existing));
3352                    assert!(out[usize::from(existing)..].iter().all(|x| vm.is_none(x)));
3353                    sequence.traverse(&mut |_| panic!("cleared sequence still owns an edge"));
3354                }
3355            }
3356        });
3357    }
3358
3359    #[test]
3360    fn miri_test_type_initialization() {
3361        let hierarchy = init_type_hierarchy();
3362
3363        assert!(hierarchy.type_type.class().is(&hierarchy.type_type));
3364        assert!(hierarchy.object_type.class().is(&hierarchy.type_type));
3365        assert!(hierarchy.tuple_type.class().is(&hierarchy.type_type));
3366        assert!(hierarchy.weakref_type.class().is(&hierarchy.type_type));
3367
3368        let object_bases = hierarchy.object_type.bases.read();
3369        assert!(object_bases.as_slice().is_empty());
3370        assert!(object_bases.as_untyped().is(&hierarchy.empty_tuple));
3371        assert!(object_bases.as_untyped().class().is(&hierarchy.tuple_type));
3372        drop(object_bases);
3373
3374        for typ in [
3375            &hierarchy.type_type,
3376            &hierarchy.tuple_type,
3377            &hierarchy.weakref_type,
3378        ] {
3379            let bases = typ.bases.read();
3380            assert_eq!(bases.as_slice().len(), 1);
3381            assert!(bases.as_slice()[0].is(&hierarchy.object_type));
3382            assert!(bases.as_untyped().class().is(&hierarchy.tuple_type));
3383        }
3384    }
3385
3386    #[test]
3387    fn miri_test_drop() {
3388        //cspell:ignore dfghjkl
3389        let ctx = crate::Context::genesis();
3390        let obj = ctx.new_bytes(b"dfghjkl".to_vec());
3391        drop(obj);
3392    }
3393
3394    /// A weakref node stays linked into its target's list until its own
3395    /// `Drop` unlinks it, and `WeakRefList::add` reads the class off every
3396    /// node it walks looking for a proxy to reuse. A node that lost its class
3397    /// while still linked made that walk dereference a null type pointer.
3398    #[cfg(feature = "threading")]
3399    #[test]
3400    fn weakref_proxies_keep_their_class_while_linked() {
3401        const THREADS: usize = 8;
3402        const ROUNDS: usize = 20_000;
3403
3404        crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
3405            let target: PyObjectRef = vm
3406                .ctx
3407                .new_class(
3408                    None,
3409                    "WeakrefTarget",
3410                    vm.ctx.types.object_type.to_owned(),
3411                    Default::default(),
3412                )
3413                .into();
3414            let workers = (0..THREADS)
3415                .map(|_| {
3416                    let thread_vm = vm.new_thread();
3417                    let target = target.clone();
3418                    std::thread::spawn(move || {
3419                        thread_vm.run(|vm| {
3420                            let proxy_type = vm.ctx.types.weakproxy_type.to_owned();
3421                            for _ in 0..ROUNDS {
3422                                let proxy = target
3423                                    .downgrade_with_typ(None, proxy_type.clone(), vm)
3424                                    .expect("a type object takes weakrefs");
3425                                drop(proxy);
3426                                vm.check_signals().unwrap();
3427                            }
3428                        })
3429                    })
3430                })
3431                .collect::<Vec<_>>();
3432            // Detach while joining: a thread that blocks attached never
3433            // reaches a safepoint, so a collection started by a worker could
3434            // not finish.
3435            vm.allow_threads(|| {
3436                for worker in workers {
3437                    worker.join().unwrap();
3438                }
3439            });
3440        });
3441    }
3442}