diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 80b087becc..40e12a4d70 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -11,7 +11,7 @@ use core_types::node::Node; use core_types::registry::{EdgeHandle, edge_type}; use core_types::transform::Footprint; use core_types::uuid::NodeId; -use core_types::value::value_edge; +use core_types::value::record_value_edge; use core_types::{CacheHash, Color, ContextModification, MemoHash, Type, TypeDescriptor}; use dyn_any::DynAny; pub use dyn_any::StaticType; @@ -290,13 +290,13 @@ macro_rules! tagged_value { // =============== // MANUAL VARIANTS // =============== - Self::None => Ok(value_edge(())), + Self::None => Ok(record_value_edge(())), Self::TypeDefault(td) => { // Same direct-construction path as `to_dynany` for the same reason as in `to_dynany`. let name = td.name.as_ref(); macro_rules! check { ($type_default:ty) => { - if name == std::any::type_name::<$type_default>() { return Ok(value_edge(<$type_default>::default())); } + if name == std::any::type_name::<$type_default>() { return Ok(record_value_edge(<$type_default>::default())); } }; } for_each_type_default!(check); @@ -304,33 +304,33 @@ macro_rules! tagged_value { } Self::F64Array(values) => { let list: List = values.into_iter().map(core_types::list::Item::new_from_element).collect(); - Ok(value_edge(list)) + Ok(record_value_edge(list)) } Self::Color(color) => { let list: List = color.into_iter().map(core_types::list::Item::new_from_element).collect(); - Ok(value_edge(list)) + Ok(record_value_edge(list)) } - Self::Gradient(stops) => Ok(value_edge(List::::new_from_element(stops))), + Self::Gradient(stops) => Ok(record_value_edge(List::::new_from_element(stops))), Self::BrushStrokes(strokes) => { let list: List = strokes.into_iter().map(core_types::list::Item::new_from_element).collect(); - Ok(value_edge(list)) + Ok(record_value_edge(list)) } // ======================= // AUTO-GENERATED VARIANTS // ======================= - $( Self::$identifier(x) => Ok(value_edge(x)), )* + $( Self::$identifier(x) => Ok(record_value_edge(x)), )* // ======================= // NON-SERIALIZED VARIANTS // ======================= - Self::RenderOutput(x) => Ok(value_edge(x)), + Self::RenderOutput(x) => Ok(record_value_edge(x)), Self::NodeIdPath(path) => { let list: List = path.into_iter().map(core_types::list::Item::new_from_element).collect(); - Ok(value_edge(list)) + Ok(record_value_edge(list)) } - Self::DocumentNode(node) => Ok(value_edge(node)), - Self::ContextModification(modification) => Ok(value_edge(modification)), - Self::EditorApi(x) => Ok(value_edge(x)), - Self::ResourceHash(x) => Ok(value_edge(x)), + Self::DocumentNode(node) => Ok(record_value_edge(node)), + Self::ContextModification(modification) => Ok(record_value_edge(modification)), + Self::EditorApi(x) => Ok(record_value_edge(x)), + Self::ResourceHash(x) => Ok(record_value_edge(x)), } } diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index fcdaf84ef6..8f2f2bcd70 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -752,10 +752,10 @@ impl TypingContext { } let inputs = match node.construction_args { - // If the node has a value input we can infer the return type from it + // A value node is a native record source, so it types as a record + // of its value. ConstructionArgs::Value(ref v) => { - // TODO: This should return a reference to the value - let types = NodeIOTypes::new(concrete!(Context), v.ty(), vec![]); + let types = NodeIOTypes::new(concrete!(Context), Type::Record(Box::new(v.ty())), vec![]); self.inferred.insert(node_id, types.clone()); return Ok(types); } @@ -1327,9 +1327,9 @@ mod ref_adapter_test { ], ), ( - ProtoNodeIdentifier::new("graphene_core::memo::LendNode"), + ProtoNodeIdentifier::new("core_types::record::RecordExtractLendNode"), vec![RegistryEntry { - io: NodeIOTypes::new(concrete!(Context), ref_type::(), vec![edge_type::()]), + io: NodeIOTypes::new(concrete!(Context), ref_type::(), vec![record_edge_type::()]), constructor: unused, }], ), @@ -1366,7 +1366,7 @@ mod ref_adapter_test { assert_eq!(network.nodes.len(), 3); let (adapter_id, adapter) = &network.nodes[1]; - assert_eq!(adapter.identifier.as_str(), "graphene_core::memo::LendNode"); + assert_eq!(adapter.identifier.as_str(), "core_types::record::RecordExtractLendNode"); assert_eq!(adapter.unwrap_construction_nodes(), vec![NodeId(0)]); assert_eq!(network.nodes[2].1.unwrap_construction_nodes(), vec![*adapter_id]); assert_eq!(typing.type_of(*adapter_id).unwrap().return_value, ref_type::()); @@ -1392,7 +1392,7 @@ mod ref_adapter_test { let adapters: Vec = network .nodes .iter() - .filter(|(_, node)| node.identifier.as_str() == "graphene_core::memo::LendNode") + .filter(|(_, node)| node.identifier.as_str() == "core_types::record::RecordExtractLendNode") .map(|(id, _)| *id) .collect(); let [adapter_id] = adapters.as_slice() else { diff --git a/node-graph/interpreted-executor/src/dynamic_executor.rs b/node-graph/interpreted-executor/src/dynamic_executor.rs index dd3628e794..0996256567 100644 --- a/node-graph/interpreted-executor/src/dynamic_executor.rs +++ b/node-graph/interpreted-executor/src/dynamic_executor.rs @@ -555,14 +555,18 @@ mod test { let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32).into()), vec![]); let context = TypingContext::default(); tree.push_node(NodeId(0), val_1_protonode, &context).unwrap(); - let _node = tree.get(NodeId(0)).unwrap(); + let handle = tree.get(NodeId(0)).unwrap(); + let layout = handle.layout().unwrap().clone(); + let edge = handle.duplicate().downcast_record::().unwrap(); let arena = Arena::new(64).unwrap(); let generations = []; let scope = EvalScope::new(None, None, None, &generations, &arena); let ctx = ContextImpl::root(&scope); - let result: Option> = tree.eval(NodeId(0), &ctx); - assert_eq!(result, Some(GPoll::Final(2))); + let GPoll::Final(value) = edge.eval(&ctx) else { + panic!("expected a final record"); + }; + assert_eq!(unsafe { core_types::record::read_element::(layout.rec(&value)) }, 2); } fn proto_node(identifier: &'static str, args: Vec) -> ProtoNode { diff --git a/node-graph/libraries/core-types/src/value.rs b/node-graph/libraries/core-types/src/value.rs index 7fc7852415..191e9f8092 100644 --- a/node-graph/libraries/core-types/src/value.rs +++ b/node-graph/libraries/core-types/src/value.rs @@ -13,6 +13,13 @@ pub fn value_edge( crate::registry::EdgeHandle::new(std::sync::Arc::new(ClonedNode(value)) as std::sync::Arc>) } +/// The native record edge of a constant: the element lifts onto the record +/// wire per evaluation, so value sources need no spliced adapter. +pub fn record_value_edge(value: T) -> crate::registry::EdgeHandle { + let node = crate::record::RecordLift::::new(ClonedNode(value)); + crate::registry::EdgeHandle::new_record::(std::sync::Arc::new(node) as std::sync::Arc) +} + impl ClonedNode { pub const fn new(value: T) -> ClonedNode { ClonedNode(value)