diff --git a/node-graph/libraries/core-types/src/record.rs b/node-graph/libraries/core-types/src/record.rs index 3747f1e0d0..da6ea42297 100644 --- a/node-graph/libraries/core-types/src/record.rs +++ b/node-graph/libraries/core-types/src/record.rs @@ -1345,9 +1345,10 @@ where } } -/// Law-test scaffolding: a plain probe over a record wire, cloning the -/// element out of the parked reference when it carries drop glue. No -/// production path constructs one. +/// A plain probe over a record wire, cloning the element out of the parked +/// reference when it carries drop glue. Registry constructors wrap a record +/// edge in one to feed a node's plain value input, keeping the wire kind +/// uniform. pub struct RecordExtract { edge: N, layout: Layout, diff --git a/node-graph/node-macro/src/codegen/entries.rs b/node-graph/node-macro/src/codegen/entries.rs index 99253bb24d..853ba56ce8 100644 --- a/node-graph/node-macro/src/codegen/entries.rs +++ b/node-graph/node-macro/src/codegen/entries.rs @@ -186,6 +186,8 @@ enum SlotKind { BaseConcrete(Type), /// A concrete record edge read for its layout only. Value(Type), + /// A record edge whose element extracts to the node's plain value input. + Extracted(Type), /// A plain value edge. Plain(Type), /// A lazy node edge. @@ -226,6 +228,9 @@ fn single_row_entries(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fields ParsedFieldType::Node(NodeParsedField { output_type, .. }) => SlotKind::Lazy(output_type.clone()), ParsedFieldType::Regular(RegularParsedField { ty, .. }) => match ir::value_binding(&node, index) { ir::ValueBinding::ReadingSecondary | ir::ValueBinding::RecordElement => SlotKind::Value(ty.clone()), + // One wire kind: a record node's plain value still rides a + // record edge, extracted to its element at construction. + _ if matches!(ir::node_kind(&node), ir::NodeKind::RecordIo) => SlotKind::Extracted(ty.clone()), _ => SlotKind::Plain(ty.clone()), }, } @@ -235,7 +240,7 @@ fn single_row_entries(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fields // Every non-base value/plain/lazy input must be concrete. let values_concrete = regular_fields.iter().zip(&slots).all(|(field, slot)| match slot { SlotKind::BaseGeneric(_) | SlotKind::BaseConcrete(_) => true, - SlotKind::Value(ty) | SlotKind::Plain(ty) | SlotKind::Lazy(ty) => !contains_open_generic(parsed, ty) && (lend(field) || !type_disqualifies(ty)), + SlotKind::Value(ty) | SlotKind::Extracted(ty) | SlotKind::Plain(ty) | SlotKind::Lazy(ty) => !contains_open_generic(parsed, ty) && (lend(field) || !type_disqualifies(ty)), }); if !values_concrete { return quote!(); @@ -247,7 +252,7 @@ fn single_row_entries(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fields let input_types = slots.iter().map(|slot| match slot { SlotKind::BaseGeneric(name) => quote!(gcore::registry::generic_record_edge_type(#name)), - SlotKind::BaseConcrete(ty) | SlotKind::Value(ty) => quote!(gcore::registry::record_edge_type::<#ty>()), + SlotKind::BaseConcrete(ty) | SlotKind::Value(ty) | SlotKind::Extracted(ty) => quote!(gcore::registry::record_edge_type::<#ty>()), SlotKind::Plain(ty) | SlotKind::Lazy(ty) => quote!(gcore::registry::edge_type::<#ty>()), }); @@ -267,6 +272,11 @@ fn single_row_entries(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fields let #layout = #handle.layout().clone(); let #name = #handle.downcast_record::<#value_ty>()?; }, + SlotKind::Extracted(value_ty) => quote! { + let #handle = inputs.next().unwrap(); + let #layout = #handle.layout().clone(); + let #name = gcore::record::RecordExtract::<#value_ty, _>::new(#handle.downcast_record::<#value_ty>()?, &#layout); + }, SlotKind::Plain(value_ty) | SlotKind::Lazy(value_ty) => quote!(let #name = inputs.next().unwrap().downcast::<#value_ty>()?;), } });