From 0e9f41e08d94f12adf9bc69a8a3a44ba898609f9 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sat, 5 Sep 2026 12:10:20 +0000 Subject: [PATCH] Key the element park path and its replay to the element type --- node-graph/libraries/core-types/src/record.rs | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/node-graph/libraries/core-types/src/record.rs b/node-graph/libraries/core-types/src/record.rs index be4f841040..852fb83f0e 100644 --- a/node-graph/libraries/core-types/src/record.rs +++ b/node-graph/libraries/core-types/src/record.rs @@ -1313,7 +1313,7 @@ pub unsafe fn write_field(dst: *mut u8, offset: usize, value: T) { /// `dst` must be the claimed frame (or inline scratch when `frame_bytes` is /// 0) of a record whose element is `T` and whose frame size is `frame_bytes`, /// with every carried field already written. -pub(crate) unsafe fn lift_poll_into<'e, T: Send + Sync>(poll: GPoll, dst: *mut u8, frame_bytes: usize, arena: &'e crate::arena::Arena) -> GPoll> { +pub(crate) unsafe fn lift_poll_into<'e, T: Send + Sync + dyn_any::StaticTypeSized>(poll: GPoll, dst: *mut u8, frame_bytes: usize, arena: &'e crate::arena::Arena) -> GPoll> { let build = |element: T| { let written = unsafe { write_element(dst, element, arena) }; written.map(|()| match frame_bytes { @@ -1459,7 +1459,8 @@ where } let retained = retained_measure(std::any::TypeId::of::()).map_or(0, |measure| measure(value)); let value = value.downcast_ref::().expect("an element replays at its own type"); - unsafe { write_element_sized(dst, value.clone(), arena, retained) } + // SAFETY: the caller's contract, keyed as this element's own parks are. + unsafe { write_element_keyed(dst, value.clone(), arena, retained, std::any::TypeId::of::()) } } /// Declines for a type carrying deep glue, whose value may hold interiors /// the evaluation's arena owns and which a moved header may not reference. @@ -1476,7 +1477,7 @@ where let retained = retained_measure(std::any::TypeId::of::()).map_or(0, |measure| measure(value)); // SAFETY: as above, and the decline above establishes that the payload // owns all of its content. - unsafe { promotion.move_park::(parked, retained) }.map(<*const T::Static>::cast) + unsafe { promotion.move_park::(parked, retained) }.map(<*const T::Static>::cast) } let (size, align) = element_dims::(); ElementWrite { @@ -1563,7 +1564,7 @@ pub unsafe fn read_element(rec: Rec<'_>) -> T { /// # Safety /// `dst` must be fresh element storage of a record whose element is `T`. /// `None` reports arena exhaustion for a parked element. -pub unsafe fn write_element(dst: *mut u8, value: T, arena: &crate::arena::Arena) -> Option<()> { +pub unsafe fn write_element(dst: *mut u8, value: T, arena: &crate::arena::Arena) -> Option<()> { unsafe { write_element_sized(dst, value, arena, 0) } } @@ -1571,10 +1572,21 @@ pub unsafe fn write_element(dst: *mut u8, value: T, arena: &crat /// /// # Safety /// As [`write_element`]. -pub unsafe fn write_element_sized(dst: *mut u8, value: T, arena: &crate::arena::Arena, retained: usize) -> Option<()> { +pub unsafe fn write_element_sized(dst: *mut u8, value: T, arena: &crate::arena::Arena, retained: usize) -> Option<()> { + // SAFETY: the caller's contract; `T::Static` is the element type's own key. + unsafe { write_element_keyed(dst, value, arena, retained, std::any::TypeId::of::()) } +} + +/// [`write_element_sized`] for replay glue already holding the element's +/// static form, whose key the element type it replays into supplies. +/// +/// # Safety +/// As [`write_element`], and `type_of` must be that element type's key, since +/// a park carrying it is what [`Promotion::move_park`] moves at. +pub(crate) unsafe fn write_element_keyed(dst: *mut u8, value: T, arena: &crate::arena::Arena, retained: usize, type_of: std::any::TypeId) -> Option<()> { match element_parked::() { true => { - let (parked, _) = arena.alloc_sized(value, retained)?; + let (parked, _) = arena.alloc_sized_as(value, retained, type_of)?; unsafe { dst.cast::<&T>().write(parked) }; Some(()) } @@ -1855,8 +1867,9 @@ impl<'a> Promotion<'a> { /// Moves a transient payload's header into the persistent region instead of /// cloning the heap it owns: the heap travels with the drop obligation and /// is freed at the persistent flush, never at the transient reset. `None` - /// where the header is not the transient arena's own park of `T`'s size or - /// the region refused it, which leaves the caller its clone path. + /// where the header is not the transient arena's own park keyed to `T`'s + /// static type or the region refused it, which leaves the caller its clone + /// path. /// /// The forwarding is the evaluation's, so a payload two records share moves /// once and both reach the one persistent header. The source header stays @@ -1864,10 +1877,14 @@ impl<'a> Promotion<'a> { /// the move keeps sound: no read of it may outlive the persistent flush. /// /// # Safety - /// `parked` must address a live `T`, and a transient park at that address - /// and size must be the `T` itself. `T` must own all of its content, a - /// persistent header being allowed to reference no transient storage. - pub unsafe fn move_park(&self, parked: *const u8, retained: usize) -> Option<*const T> { + /// `parked` must address a live `T`, and `T` must own all of its content, a + /// persistent header being allowed to reference no transient storage, which + /// is also what lets it be republished at `T::Static`. The park's key + /// settles the type, so the caller owes no identity argument. + pub unsafe fn move_park(&self, parked: *const u8, retained: usize) -> Option<*const T::Static> + where + T::Static: Send + Sync, + { // SAFETY: the caller's contract, forwarded to the parking arena. unsafe { self.transient.move_park::(parked, self.persistent, retained) } }