| # Stack references (`_PyStackRef`) |
| |
| Stack references are the interpreter's tagged representation of values on the evaluation stack. |
| They carry metadata to track ownership and support optimizations such as tagged small ints. |
| |
| ## Shape and tagging |
| |
| - A `_PyStackRef` is a tagged pointer-sized value (see `Include/internal/pycore_stackref.h`). |
| - Tag bits distinguish three cases: |
| - `Py_TAG_REFCNT` unset - reference count lives on the pointed-to object. |
| - `Py_TAG_REFCNT` set - ownership is "borrowed" (no refcount to drop on close) or the object is immortal. |
| - `Py_INT_TAG` set - tagged small integer stored directly in the stackref (no heap allocation). |
| - Special constants: `PyStackRef_NULL`, `PyStackRef_ERROR`, and embedded `None`/`True`/`False`. |
| |
| In GIL builds, most objects carry their refcount; tagged borrowed refs skip decref on close. In free |
| threading builds, the tag is also used to mark deferred refcounted objects so the GC can see them and |
| to avoid refcount contention on commonly shared objects. |
| |
| ## Converting to and from PyObject* |
| |
| Three conversions control ownership: |
| |
| - `PyStackRef_FromPyObjectNew(obj)` - create a new reference (INCREF if mortal). |
| - `PyStackRef_FromPyObjectSteal(obj)` - take over ownership without changing the count unless the |
| object is immortal. |
| - `PyStackRef_FromPyObjectBorrow(obj)` - create a borrowed stackref (never decref on close). |
| |
| The `obj` argument must not be `NULL`. |
| |
| Going back to `PyObject*` mirrors this: |
| |
| - `PyStackRef_AsPyObjectBorrow(ref)` - borrow the underlying pointer |
| - `PyStackRef_AsPyObjectSteal(ref)` - transfer ownership from the stackref; if ref is borrowed or |
| deferred, this creates a new owning `PyObject*` reference. |
| - `PyStackRef_AsPyObjectNew(ref)` - create a new owning reference |
| |
| Only `PyStackRef_AsPyObjectBorrow` allows ref to be `PyStackRef_NULL`. |
| |
| ## Operations on stackrefs |
| |
| The interpreter treats `_PyStackRef` as the unit of stack storage. Ownership must be managed with |
| the stackref primitives: |
| |
| - `PyStackRef_DUP` - like `Py_NewRef` for stackrefs; preserves the original. |
| - `PyStackRef_Borrow` - create a borrowed stackref from another stackref. |
| - `PyStackRef_CLOSE` / `PyStackRef_XCLOSE` - like `Py_DECREF`; invalidates the stackref. |
| - `PyStackRef_CLEAR` - like `Py_CLEAR`; closes and sets the stackref to `PyStackRef_NULL` |
| - `PyStackRef_MakeHeapSafe` - converts borrowed reference to owning reference |
| |
| Borrow tracking (for debug builds with `Py_STACKREF_DEBUG`) records who you borrowed from and reports |
| double-close, leaked borrows, or use-after-close via fatal errors. |
| |
| ## Borrow-friendly opcodes |
| |
| The interpreter can push borrowed references directly. For example, `LOAD_FAST_BORROW` loads a local |
| variable as a borrowed `_PyStackRef`, avoiding both INCREF and DECREF for the temporary lifetime on |
| the evaluation stack. |
| |
| ## Tagged integers on the stack |
| |
| Small ints can be stored inline with `Py_INT_TAG`, so no heap object is involved. Helpers like |
| `PyStackRef_TagInt`, `PyStackRef_UntagInt`, and `PyStackRef_IncrementTaggedIntNoOverflow` operate on |
| these values. Type checks use `PyStackRef_IsTaggedInt` and `PyStackRef_LongCheck`. |
| |
| ## Free threading considerations |
| |
| Objects that support deferred reference counting can be pushed to the evaluation |
| stack and stored in local variables without directly incrementing the reference |
| count because they are only freed during cyclic garbage collection. This avoids |
| reference count contention on commonly shared objects such as methods and types. The GC |
| scans each thread's locals and evaluation stack to keep objects that use |
| deferred reference counting alive. |
| |
| ## Debugging support |
| |
| `Py_STACKREF_DEBUG` builds replace the inline tags with table-backed IDs so the runtime can track |
| creation sites, borrows, closes, and leaks. Enabling `Py_STACKREF_CLOSE_DEBUG` additionally records |
| double closes. The tables live on `PyInterpreterState` and are initialized in `pystate.c`; helper |
| routines reside in `Python/stackrefs.c`. |