From ab93e1f071b6d10f8f4bff003ed60da7bd7e9915 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sat, 5 Sep 2026 11:52:42 +0000 Subject: [PATCH] Add the owned attribute crossing over the deep field glue --- .../libraries/core-types/src/attribute.rs | 60 +++++++++++++++++++ node-graph/libraries/core-types/src/record.rs | 34 ++++++----- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/node-graph/libraries/core-types/src/attribute.rs b/node-graph/libraries/core-types/src/attribute.rs index c8831fc92f..69d578b5fe 100644 --- a/node-graph/libraries/core-types/src/attribute.rs +++ b/node-graph/libraries/core-types/src/attribute.rs @@ -79,6 +79,45 @@ impl<'e, A: Attribute> std::fmt::Debug for Attr<'e, A> { } } +/// An attribute write that outlives the evaluation producing it: an async +/// source's slot persists across generations, so a reference value cannot +/// cross as itself. The value crosses deep-copied through the field glue and +/// parks into the serving arena at every lift, which is why the copy is paid +/// once per invocation rather than once per evaluation. +pub struct OwnedAttr(Box, PhantomData A>); + +impl OwnedAttr { + /// Deep-copies `value` out of the evaluation that produced it. + pub fn new(value: A::Value<'_>) -> Self { + // SAFETY: the read addresses a live local of the marker's value type. + let erased = unsafe { A::read_erased((&raw const value).cast()) }; + OwnedAttr(crate::record::deepen_field_value(erased), PhantomData) + } + + /// Parks the copy into `arena` for one evaluation; `None` reports arena + /// exhaustion. + pub fn park<'e>(&self, arena: &'e crate::arena::Arena) -> Option> { + let resident = crate::record::replay_field_value(&*self.0, arena)?; + let mut value = A::default(); + // SAFETY: the slot is a live field of the marker's value type, and the + // stored value is the copy `new` took at that same type. + unsafe { write_stored::(resident.as_deref().unwrap_or(&*self.0), (&raw mut value).cast(), arena) }?; + Some(value) + } +} + +impl Clone for OwnedAttr { + fn clone(&self) -> Self { + OwnedAttr(self.0.clone(), PhantomData) + } +} + +impl std::fmt::Debug for OwnedAttr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple(A::NAME).field(&self.0.display_string()).finish() + } +} + /// A deletion of `A` in a node's return tuple: the name leaves the output /// layout, so downstream reads yield the declared default again. Functionally /// a write of the default; the value carries nothing. @@ -410,6 +449,27 @@ mod tests { assert_eq!(row.size, size_of::<&str>()); } + #[test] + fn an_owned_reference_crossing_parks_into_the_serving_arena() { + let owned = OwnedAttr::::new("crossing"); + let arena = crate::arena::Arena::new(1024).unwrap(); + assert_eq!(owned.park(&arena).unwrap(), "crossing"); + assert_eq!(owned.clone().park(&arena).unwrap(), "crossing", "the crossing parks again on every evaluation"); + } + + #[test] + fn an_owned_plain_crossing_rides_its_bytes() { + let arena = crate::arena::Arena::new(64).unwrap(); + assert_eq!(OwnedAttr::::new(0.25).park(&arena).unwrap(), 0.25); + } + + #[test] + fn an_exhausted_arena_refuses_an_owned_reference_crossing() { + let owned = OwnedAttr::::new("too long for this arena"); + let arena = crate::arena::Arena::new(8).unwrap(); + assert!(owned.park(&arena).is_none()); + } + #[test] fn reregistration_at_the_same_type_is_idempotent() { register::(); diff --git a/node-graph/libraries/core-types/src/record.rs b/node-graph/libraries/core-types/src/record.rs index be4f841040..952d7eb864 100644 --- a/node-graph/libraries/core-types/src/record.rs +++ b/node-graph/libraries/core-types/src/record.rs @@ -1426,13 +1426,25 @@ fn deep_field_glue(type_id: std::any::TypeId) -> Option { DEEP_FIELD_VALUES.lock().unwrap().get(&type_id).copied() } -fn deepen_field_value(value: Box) -> Box { +/// The copy-out half over an erased field value: the owned form a value takes +/// when it crosses out of the evaluation whose arena its content borrows. A +/// value with no registered glue already owns everything and passes through. +pub fn deepen_field_value(value: Box) -> Box { match deep_field_glue(value.as_any().type_id()) { Some(glue) => (glue.copy_out)(&*value).unwrap_or(value), None => value, } } +/// The replay half over an erased field value: `Some(None)` where the value +/// already owns its content, `None` on arena exhaustion. +pub fn replay_field_value(value: &dyn crate::list::AnyAttributeValue, arena: &crate::arena::Arena) -> Option>> { + match deep_field_glue(value.as_any().type_id()) { + Some(glue) => (glue.replay)(value, arena), + None => Some(None), + } +} + /// The element slot a record wire of `T` carries, its erased glue bound at /// the statically-known type. pub fn element_write() -> ElementWrite @@ -1918,14 +1930,9 @@ unsafe fn promote_record(layout: &Layout, dst: *mut u8, promotion: &Promotion<'_ } // SAFETY: the slot images a parked field of this descriptor. let value = deepen_field_value(unsafe { (field.read_erased)(slot.cast_const()) }); - match deep_field_glue(value.as_any().type_id()) { - Some(glue) => match (glue.replay)(&*value, promotion.persistent)? { - // SAFETY: the replay produced this field's own value type. - Some(resident) => unsafe { repark(&*resident, slot, promotion.persistent) }?, - None => unsafe { repark(&*value, slot, promotion.persistent) }?, - }, - None => unsafe { repark(&*value, slot, promotion.persistent) }?, - } + let resident = replay_field_value(&*value, promotion.persistent)?; + // SAFETY: the replay produced this field's own value type. + unsafe { repark(resident.as_deref().unwrap_or(&*value), slot, promotion.persistent) }?; } Some(()) } @@ -2203,13 +2210,8 @@ impl OwnedRecord { for (index, value) in &self.fields { let field = &layout.fields[*index]; let repark = field.repark.expect("copied fields carry re-park glue"); - match deep_field_glue(value.as_any().type_id()) { - Some(glue) => match (glue.replay)(&**value, arena)? { - Some(resident) => unsafe { repark(&*resident, dst.add(field.offset), arena) }?, - None => unsafe { repark(&**value, dst.add(field.offset), arena) }?, - }, - None => unsafe { repark(&**value, dst.add(field.offset), arena) }?, - } + let resident = replay_field_value(&**value, arena)?; + unsafe { repark(resident.as_deref().unwrap_or(&**value), dst.add(field.offset), arena) }?; } Some(()) }