@@ -4,15 +4,17 @@ use std::{
44 collections:: hash_map:: DefaultHasher ,
55 fmt:: Write ,
66 hash:: { Hash , Hasher } ,
7- mem:: { ManuallyDrop , size_of} ,
8- ptr:: addr_of,
7+ mem:: size_of,
98 vec,
109} ;
1110
1211use ahash:: AHashSet ;
1312use num_integer:: Integer ;
1413use smallvec:: SmallVec ;
1514
15+ // Re-export items moved to `heap_traits` so that `crate::heap::HeapGuard` etc. continue
16+ // to resolve (used by the `defer_drop!` macros and throughout the codebase).
17+ pub ( crate ) use crate :: heap_traits:: { ContainsHeap , DropWithHeap , HeapGuard , ImmutableHeapGuard } ;
1618use crate :: {
1719 args:: ArgValues ,
1820 asyncio:: { Coroutine , GatherFuture , GatherItem } ,
@@ -1725,300 +1727,3 @@ impl<T: ResourceTracker> Drop for Heap<T> {
17251727 }
17261728 }
17271729}
1728-
1729- /// This trait represents types that contain a `Heap`; it allows for more complex structures
1730- /// to participate in the `HeapGuard` pattern.
1731- pub ( crate ) trait ContainsHeap {
1732- type ResourceTracker : ResourceTracker ;
1733- fn heap ( & self ) -> & Heap < Self :: ResourceTracker > ;
1734- fn heap_mut ( & mut self ) -> & mut Heap < Self :: ResourceTracker > ;
1735- }
1736-
1737- impl < T : ResourceTracker > ContainsHeap for Heap < T > {
1738- type ResourceTracker = T ;
1739- fn heap ( & self ) -> & Self {
1740- self
1741- }
1742- #[ inline]
1743- fn heap_mut ( & mut self ) -> & mut Self {
1744- self
1745- }
1746- }
1747-
1748- /// Trait for types that require heap access for proper cleanup.
1749- ///
1750- /// Rust's standard `Drop` trait cannot decrement heap reference counts because it has no
1751- /// access to the `Heap`. This trait provides an explicit drop-with-heap method so that
1752- /// ref-counted values (and containers of them) can properly decrement their counts when
1753- /// they are no longer needed.
1754- ///
1755- /// **All types implementing this trait must be cleaned up on every code path** — not just
1756- /// the happy path, but also early returns, conditional branches, `continue`, etc. A missed
1757- /// call on any branch leaks reference counts. Prefer [`defer_drop!`] or [`HeapGuard`] to
1758- /// guarantee cleanup automatically rather than inserting manual calls in every branch.
1759- ///
1760- /// Implemented for `Value`, `Option<V>`, `Vec<Value>`, `ArgValues`, iterators, and other
1761- /// types that hold heap references.
1762- pub ( crate ) trait DropWithHeap : Sized {
1763- /// Consume `self` and decrement reference counts for any heap-allocated values contained within.
1764- fn drop_with_heap < H : ContainsHeap > ( self , heap : & mut H ) ;
1765- }
1766-
1767- impl DropWithHeap for Value {
1768- #[ inline]
1769- fn drop_with_heap < H : ContainsHeap > ( self , heap : & mut H ) {
1770- Self :: drop_with_heap ( self , heap) ;
1771- }
1772- }
1773-
1774- impl < U : DropWithHeap > DropWithHeap for Option < U > {
1775- #[ inline]
1776- fn drop_with_heap < H : ContainsHeap > ( self , heap : & mut H ) {
1777- if let Some ( value) = self {
1778- value. drop_with_heap ( heap) ;
1779- }
1780- }
1781- }
1782-
1783- impl < U : DropWithHeap > DropWithHeap for Vec < U > {
1784- fn drop_with_heap < H : ContainsHeap > ( self , heap : & mut H ) {
1785- for value in self {
1786- value. drop_with_heap ( heap) ;
1787- }
1788- }
1789- }
1790-
1791- impl < U : DropWithHeap > DropWithHeap for vec:: IntoIter < U > {
1792- fn drop_with_heap < H : ContainsHeap > ( self , heap : & mut H ) {
1793- for value in self {
1794- value. drop_with_heap ( heap) ;
1795- }
1796- }
1797- }
1798-
1799- impl < const N : usize > DropWithHeap for [ Value ; N ] {
1800- fn drop_with_heap < H : ContainsHeap > ( self , heap : & mut H ) {
1801- for value in self {
1802- value. drop_with_heap ( heap) ;
1803- }
1804- }
1805- }
1806-
1807- impl < U : DropWithHeap , V : DropWithHeap > DropWithHeap for ( U , V ) {
1808- fn drop_with_heap < H : ContainsHeap > ( self , heap : & mut H ) {
1809- let ( left, right) = self ;
1810- left. drop_with_heap ( heap) ;
1811- right. drop_with_heap ( heap) ;
1812- }
1813- }
1814-
1815- /// Trait for types that require only an immutable heap reference for cleanup.
1816- ///
1817- /// Unlike [`DropWithHeap`], which requires `&mut Heap`, this trait works with `&Heap`.
1818- /// This is needed for cleanup in contexts that only have shared access to the heap,
1819- /// such as `py_repr_fmt` and `py_str` formatting methods.
1820- ///
1821- /// Currently implemented for [`RecursionToken`], which decrements the recursion depth
1822- /// counter via interior mutability (`Cell`).
1823- pub ( crate ) trait DropWithImmutableHeap {
1824- /// Consume `self` and perform cleanup using an immutable heap reference.
1825- fn drop_with_immutable_heap < T : ResourceTracker > ( self , heap : & Heap < T > ) ;
1826- }
1827-
1828- impl DropWithImmutableHeap for RecursionToken {
1829- #[ inline]
1830- fn drop_with_immutable_heap < T : ResourceTracker > ( self , heap : & Heap < T > ) {
1831- heap. decr_recursion_depth ( ) ;
1832- }
1833- }
1834-
1835- /// RAII guard that ensures a [`DropWithImmutableHeap`] value is cleaned up on every code path.
1836- ///
1837- /// Like [`HeapGuard`], but holds an immutable `&Heap<T>` instead of requiring `&mut` access
1838- /// via [`ContainsHeap`]. This is useful in contexts that only have shared access to the heap,
1839- /// such as `py_repr_fmt` formatting methods.
1840- ///
1841- /// On the normal path, the guarded value can be borrowed via [`as_parts`](Self::as_parts).
1842- /// The guard's `Drop` impl calls [`DropWithImmutableHeap::drop_with_immutable_heap`]
1843- /// automatically, so cleanup happens on all exit paths.
1844- pub ( crate ) struct ImmutableHeapGuard < ' a , T : ResourceTracker , V : DropWithImmutableHeap > {
1845- value : ManuallyDrop < V > ,
1846- heap : & ' a Heap < T > ,
1847- }
1848-
1849- impl < ' a , T : ResourceTracker , V : DropWithImmutableHeap > ImmutableHeapGuard < ' a , T , V > {
1850- /// Creates a new `ImmutableHeapGuard` for the given value and immutable heap reference.
1851- #[ inline]
1852- pub fn new ( value : V , heap : & ' a Heap < T > ) -> Self {
1853- Self {
1854- value : ManuallyDrop :: new ( value) ,
1855- heap,
1856- }
1857- }
1858-
1859- /// Borrows the value (immutably) and heap (immutably) out of the guard.
1860- ///
1861- /// This is what [`defer_drop_immutable_heap!`] calls internally. The returned
1862- /// references are tied to the guard's lifetime, so the value cannot escape.
1863- #[ inline]
1864- pub fn as_parts ( & self ) -> ( & V , & ' a Heap < T > ) {
1865- ( & self . value , self . heap )
1866- }
1867- }
1868-
1869- impl < T : ResourceTracker , V : DropWithImmutableHeap > Drop for ImmutableHeapGuard < ' _ , T , V > {
1870- fn drop ( & mut self ) {
1871- // SAFETY: [DH] - value is never manually dropped until this point
1872- unsafe { ManuallyDrop :: take ( & mut self . value ) } . drop_with_immutable_heap ( self . heap ) ;
1873- }
1874- }
1875-
1876- /// RAII guard that ensures a [`DropWithHeap`] value is cleaned up on every code path.
1877- ///
1878- /// The guard's `Drop` impl calls [`DropWithHeap::drop_with_heap`] automatically, so
1879- /// cleanup happens whether the scope exits normally, via `?`, `continue`, early return,
1880- /// or any other branch. This eliminates the need to manually insert `drop_with_heap`
1881- /// calls in every branch.
1882- ///
1883- /// On the normal path, the guarded value can be borrowed via [`as_parts`](Self::as_parts) /
1884- /// [`as_parts_mut`](Self::as_parts_mut), or reclaimed via [`into_inner`](Self::into_inner) /
1885- /// [`into_parts`](Self::into_parts) (which consume the guard without dropping the value).
1886- ///
1887- /// Prefer the [`defer_drop!`] macro for the common case where you just need to ensure a
1888- /// value is dropped at scope exit. Use `HeapGuard` directly when you need to conditionally
1889- /// reclaim the value (e.g. push it back onto the stack on success) or need mutable access
1890- /// to both the value and heap through [`as_parts_mut`](Self::as_parts_mut).
1891- pub ( crate ) struct HeapGuard < ' a , H : ContainsHeap , V : DropWithHeap > {
1892- // manually dropped because it needs to be dropped by move.
1893- value : ManuallyDrop < V > ,
1894- heap : & ' a mut H ,
1895- }
1896-
1897- impl < ' a , H : ContainsHeap , V : DropWithHeap > HeapGuard < ' a , H , V > {
1898- /// Creates a new `HeapGuard` for the given value and heap.
1899- #[ inline]
1900- pub fn new ( value : V , heap : & ' a mut H ) -> Self {
1901- Self {
1902- value : ManuallyDrop :: new ( value) ,
1903- heap,
1904- }
1905- }
1906-
1907- /// Consumes the guard and returns the contained value without dropping it.
1908- ///
1909- /// Use this when the value should survive beyond the guard's scope (e.g. returning
1910- /// a computed result from a function that used the guard for error-path safety).
1911- #[ inline]
1912- pub fn into_inner ( self ) -> V {
1913- let mut this = ManuallyDrop :: new ( self ) ;
1914- // SAFETY: [DH] - `ManuallyDrop::new(self)` prevents `Drop` on self, so we can take the value out
1915- unsafe { ManuallyDrop :: take ( & mut this. value ) }
1916- }
1917-
1918- /// Borrows the value (immutably) and heap (mutably) out of the guard.
1919- ///
1920- /// This is what [`defer_drop!`] calls internally. The returned references are tied
1921- /// to the guard's lifetime, so the value cannot escape.
1922- #[ inline]
1923- pub fn as_parts ( & mut self ) -> ( & V , & mut H ) {
1924- ( & self . value , self . heap )
1925- }
1926-
1927- /// Borrows the value (mutably) and heap (mutably) out of the guard.
1928- ///
1929- /// This is what [`defer_drop_mut!`] calls internally. Use this when the value needs
1930- /// to be mutated in place (e.g. advancing an iterator, swapping during min/max).
1931- #[ inline]
1932- pub fn as_parts_mut ( & mut self ) -> ( & mut V , & mut H ) {
1933- ( & mut self . value , self . heap )
1934- }
1935-
1936- /// Consumes the guard and returns the value and heap separately, without dropping.
1937- ///
1938- /// Use this when you need to reclaim both the value *and* the heap reference — for
1939- /// example, to push the value back onto the VM stack via the heap owner.
1940- #[ inline]
1941- pub fn into_parts ( self ) -> ( V , & ' a mut H ) {
1942- let mut this = ManuallyDrop :: new ( self ) ;
1943- // SAFETY: [DH] - `ManuallyDrop` prevents `Drop` on self, so we can recover the parts
1944- unsafe { ( ManuallyDrop :: take ( & mut this. value ) , addr_of ! ( this. heap) . read ( ) ) }
1945- }
1946-
1947- /// Borrows just the heap out of the guard
1948- #[ inline]
1949- pub fn heap ( & mut self ) -> & mut H {
1950- self . heap
1951- }
1952- }
1953-
1954- impl < H : ContainsHeap , V : DropWithHeap > Drop for HeapGuard < ' _ , H , V > {
1955- fn drop ( & mut self ) {
1956- // SAFETY: [DH] - value is never manually dropped until this point
1957- unsafe { ManuallyDrop :: take ( & mut self . value ) } . drop_with_heap ( self . heap . heap_mut ( ) ) ;
1958- }
1959- }
1960-
1961- /// The preferred way to ensure a [`DropWithHeap`] value is cleaned up on every code path.
1962- ///
1963- /// Creates a [`HeapGuard`] and immediately rebinds `$value` as `&V` and `$heap` as
1964- /// `&mut H` via [`HeapGuard::as_parts`]. The original owned value is moved into the
1965- /// guard, which will call [`DropWithHeap::drop_with_heap`] when scope exits — whether
1966- /// that's normal completion, early return via `?`, `continue`, or any other branch.
1967- ///
1968- /// Beyond safety, this is often much more concise than inserting `drop_with_heap` calls
1969- /// in every branch of complex control flow. For mutable access to the value, use
1970- /// [`defer_drop_mut!`].
1971- ///
1972- /// # Limitation
1973- ///
1974- /// The macro rebinds `$heap` as a new `let` binding, so it cannot be used when `$heap`
1975- /// is `self`. In `&mut self` methods, first assign `let this = self;` and pass `this`.
1976- #[ macro_export]
1977- macro_rules! defer_drop {
1978- ( $value: ident, $heap: ident) => {
1979- let mut _guard = $crate:: heap:: HeapGuard :: new( $value, $heap) ;
1980- #[ allow(
1981- clippy:: allow_attributes,
1982- reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
1983- ) ]
1984- #[ allow( unused_variables) ]
1985- let ( $value, $heap) = _guard. as_parts( ) ;
1986- } ;
1987- }
1988-
1989- /// Like [`defer_drop!`], but rebinds `$value` as `&mut V` via [`HeapGuard::as_parts_mut`].
1990- ///
1991- /// Use this when the value needs to be mutated in place — for example, advancing an
1992- /// iterator with `for_next()`, or swapping values during a min/max comparison.
1993- #[ macro_export]
1994- macro_rules! defer_drop_mut {
1995- ( $value: ident, $heap: ident) => {
1996- let mut _guard = $crate:: heap:: HeapGuard :: new( $value, $heap) ;
1997- #[ allow(
1998- clippy:: allow_attributes,
1999- reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
2000- ) ]
2001- #[ allow( unused_variables) ]
2002- let ( $value, $heap) = _guard. as_parts_mut( ) ;
2003- } ;
2004- }
2005-
2006- /// Like [`defer_drop!`], but for [`DropWithImmutableHeap`] values that only need `&Heap`
2007- /// for cleanup.
2008- ///
2009- /// Creates an [`ImmutableHeapGuard`] and immediately rebinds `$value` as `&V` and `$heap`
2010- /// as `&Heap<T>`. The guard will call [`DropWithImmutableHeap::drop_with_immutable_heap`]
2011- /// when scope exits. Use this for values like [`RecursionToken`] in contexts that only have
2012- /// shared access to the heap (e.g., `py_repr_fmt` formatting methods).
2013- #[ macro_export]
2014- macro_rules! defer_drop_immutable_heap {
2015- ( $value: ident, $heap: ident) => {
2016- let _guard = $crate:: heap:: ImmutableHeapGuard :: new( $value, $heap) ;
2017- #[ allow(
2018- clippy:: allow_attributes,
2019- reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
2020- ) ]
2021- #[ allow( unused_variables) ]
2022- let ( $value, $heap) = _guard. as_parts( ) ;
2023- } ;
2024- }
0 commit comments