mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Flip lend-parameter nodes onto record wires and keep the clone adapter plain
This commit is contained in:
@@ -590,15 +590,16 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_flipped_ref_parameter_gets_its_producer_lend_spliced() {
|
||||
fn a_flipped_ref_parameter_reads_the_borrow_from_its_record_wire() {
|
||||
let raster_list = TaggedValue::from_type(&core_types::concrete!(graphene_std::list::List<graphene_std::raster_types::Raster<graphene_std::raster_types::CPU>>)).unwrap();
|
||||
let network = ProtoNetwork {
|
||||
inputs: vec![],
|
||||
output: NodeId(2),
|
||||
output: NodeId(3),
|
||||
nodes: vec![
|
||||
(NodeId(0), ProtoNode::value(ConstructionArgs::Value(raster_list.into()), vec![])),
|
||||
(NodeId(1), ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(4).into()), vec![])),
|
||||
(NodeId(2), proto_node("raster_nodes::image_color_palette::ImageColorPaletteNode", vec![NodeId(0), NodeId(1)])),
|
||||
(NodeId(3), proto_node("core_types::record::RecordExtractNode", vec![NodeId(2)])),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -607,8 +608,8 @@ mod test {
|
||||
let generations = [];
|
||||
let scope = EvalScope::new(None, None, None, &generations, &arena);
|
||||
let ctx = ContextImpl::root(&scope);
|
||||
let result: Option<GPoll<graphene_std::list::List<graphene_std::raster::color::Color>>> = executor.tree().eval(NodeId(2), &ctx);
|
||||
assert!(matches!(result, Some(GPoll::Final(_))), "the palette must evaluate through the spliced lend, got {result:?}");
|
||||
let result: Option<GPoll<graphene_std::list::List<graphene_std::raster::color::Color>>> = executor.tree().eval(NodeId(3), &ctx);
|
||||
assert!(matches!(result, Some(GPoll::Final(_))), "the palette must evaluate through its record wires, got {result:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -787,7 +787,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
}
|
||||
})
|
||||
});
|
||||
let introduced_lend_lifetime = (has_lend && declared_arena_lifetime.is_none()).then(|| Lifetime::new("'__lend", proc_macro2::Span::call_site()));
|
||||
let introduced_lend_lifetime = (has_lend && !flip && declared_arena_lifetime.is_none()).then(|| Lifetime::new("'__lend", proc_macro2::Span::call_site()));
|
||||
let lend_lifetime = declared_arena_lifetime.or_else(|| introduced_lend_lifetime.clone());
|
||||
if let Some(lifetime) = &introduced_lend_lifetime {
|
||||
ctx_bounds.push(quote!(#core_types::context::ExtractArena<ArenaRef = &#lifetime #core_types::arena::Arena>));
|
||||
@@ -961,6 +961,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
|
||||
let record_value_ty: Type = syn::parse_quote!(#core_types::record::RecordValue<'__record>);
|
||||
let node_bounds = regular_fields.iter().enumerate().zip(&node_generics).map(|((index, field), node_generic)| match &field.ty {
|
||||
ParsedFieldType::Regular(_) if flip => quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = #record_value_ty>),
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, lend: Some(_), .. }) => {
|
||||
let lifetime = lend_lifetime.as_ref().expect("lend fields imply the lend lifetime");
|
||||
quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = &#lifetime #ty>)
|
||||
@@ -971,7 +972,6 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, .. }) if routing_source(ty) => {
|
||||
quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = #record_value_ty>)
|
||||
}
|
||||
ParsedFieldType::Regular(_) if flip => quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = #record_value_ty>),
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, .. }) => quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = #ty>),
|
||||
ParsedFieldType::Node(NodeParsedField { output_type, .. }) if routing_source(output_type) => match derives {
|
||||
true => quote!(#node_generic: for<'__derived> #core_types::record::DerivedRecordEdge<'__derived, #core_types::context::Derived<'__derived, #ctx_ident>>),
|
||||
@@ -989,7 +989,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
let mut lend_outlives: Vec<TokenStream2> = regular_fields
|
||||
.iter()
|
||||
.filter_map(|field| match &field.ty {
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, lend: Some(_), .. }) => {
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, lend: Some(_), .. }) if !flip => {
|
||||
let lifetime = lend_lifetime.as_ref().expect("lend fields imply the lend lifetime");
|
||||
Some(quote!(#ty: #lifetime))
|
||||
}
|
||||
@@ -1037,6 +1037,17 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
let name = &field.pat_ident.ident;
|
||||
match &field.ty {
|
||||
ParsedFieldType::Regular(_) if record.is_some() && !skips_carrier && index == 0 => quote!(),
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, lend: Some(_), .. }) if flip => {
|
||||
let slot = format_ident!("__in_{index}");
|
||||
let record_local = format_ident!("__record_{index}");
|
||||
quote! {
|
||||
let #record_local = match __cell.eval_input(#index, &self.#name, __input) {
|
||||
Ok(value) => value,
|
||||
Err(interrupt) => return interrupt.into(),
|
||||
};
|
||||
let #name: &#ty = unsafe { #core_types::record::borrow_element(self.#slot.rec(&#record_local)) };
|
||||
}
|
||||
}
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, .. }) if flip => {
|
||||
let slot = format_ident!("__in_{index}");
|
||||
quote! {
|
||||
@@ -1436,6 +1447,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
let mut bounds: Vec<TokenStream2> = regular_fields
|
||||
.iter()
|
||||
.filter_map(|field| match &field.ty {
|
||||
ParsedFieldType::Regular(RegularParsedField { lend: Some(_), .. }) => None,
|
||||
ParsedFieldType::Regular(RegularParsedField { ty, .. }) => Some(quote!(#ty: ::core::clone::Clone)),
|
||||
_ => None,
|
||||
})
|
||||
@@ -1813,7 +1825,7 @@ pub(crate) fn record_flip(parsed: &ParsedNodeFn) -> bool {
|
||||
if parsed.is_async || is_source_kernel(&parsed.output_type) {
|
||||
return false;
|
||||
}
|
||||
if parsed.attributes.batch.is_some() || parsed.attributes.shader_node.is_some() {
|
||||
if parsed.attributes.batch.is_some() || parsed.attributes.shader_node.is_some() || parsed.attributes.plain {
|
||||
return false;
|
||||
}
|
||||
if matches!(kernel_kind(&parsed.output_type), KernelKind::Poll(_)) {
|
||||
@@ -1842,10 +1854,7 @@ pub(crate) fn record_flip(parsed: &ParsedNodeFn) -> bool {
|
||||
GenericParam::Lifetime(_) | GenericParam::Const(_) => return false,
|
||||
}
|
||||
}
|
||||
parsed.fields.iter().all(|field| match &field.ty {
|
||||
ParsedFieldType::Regular(RegularParsedField { lend, .. }) => lend.is_none(),
|
||||
ParsedFieldType::Node(_) => false,
|
||||
})
|
||||
parsed.fields.iter().all(|field| matches!(&field.ty, ParsedFieldType::Regular(_)))
|
||||
}
|
||||
|
||||
pub(crate) fn routing_io(parsed: &ParsedNodeFn) -> Option<RoutingIo> {
|
||||
|
||||
@@ -113,6 +113,8 @@ pub(crate) struct NodeFnAttributes {
|
||||
pub(crate) batch: Option<Path>,
|
||||
/// Whether partial upstream values are mapped to `Pending` instead of flowing into this node
|
||||
pub(crate) no_partial: bool,
|
||||
/// Whether this node keeps the plain-wire lowering during the record transition
|
||||
pub(crate) plain: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
@@ -375,6 +377,7 @@ impl Parse for NodeFnAttributes {
|
||||
let mut extent = None;
|
||||
let mut batch = None;
|
||||
let mut no_partial = false;
|
||||
let mut plain = false;
|
||||
|
||||
let content = input;
|
||||
// let content;
|
||||
@@ -560,6 +563,13 @@ impl Parse for NodeFnAttributes {
|
||||
//
|
||||
// Example usage:
|
||||
// #[node_macro::node(..., no_partial, ...)]
|
||||
"plain" => {
|
||||
let path = meta.require_path_only()?;
|
||||
if plain {
|
||||
return Err(Error::new_spanned(path, "Multiple 'plain' attributes are not allowed"));
|
||||
}
|
||||
plain = true;
|
||||
}
|
||||
"no_partial" => {
|
||||
let path = meta.require_path_only()?;
|
||||
if no_partial {
|
||||
@@ -611,6 +621,7 @@ impl Parse for NodeFnAttributes {
|
||||
extent,
|
||||
batch,
|
||||
no_partial,
|
||||
plain,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1278,6 +1289,7 @@ mod tests {
|
||||
extent: None,
|
||||
batch: None,
|
||||
no_partial: false,
|
||||
plain: false,
|
||||
},
|
||||
fn_name: Ident::new("add", Span::call_site()),
|
||||
struct_name: Ident::new("Add", Span::call_site()),
|
||||
@@ -1354,6 +1366,7 @@ mod tests {
|
||||
extent: None,
|
||||
batch: None,
|
||||
no_partial: false,
|
||||
plain: false,
|
||||
},
|
||||
fn_name: Ident::new("transform", Span::call_site()),
|
||||
struct_name: Ident::new("Transform", Span::call_site()),
|
||||
@@ -1444,6 +1457,7 @@ mod tests {
|
||||
extent: None,
|
||||
batch: None,
|
||||
no_partial: false,
|
||||
plain: false,
|
||||
},
|
||||
fn_name: Ident::new("circle", Span::call_site()),
|
||||
struct_name: Ident::new("Circle", Span::call_site()),
|
||||
@@ -1516,6 +1530,7 @@ mod tests {
|
||||
extent: None,
|
||||
batch: None,
|
||||
no_partial: false,
|
||||
plain: false,
|
||||
},
|
||||
fn_name: Ident::new("levels", Span::call_site()),
|
||||
struct_name: Ident::new("Levels", Span::call_site()),
|
||||
@@ -1600,6 +1615,7 @@ mod tests {
|
||||
extent: None,
|
||||
batch: None,
|
||||
no_partial: false,
|
||||
plain: false,
|
||||
},
|
||||
fn_name: Ident::new("add", Span::call_site()),
|
||||
struct_name: Ident::new("Add", Span::call_site()),
|
||||
@@ -1687,6 +1703,7 @@ mod tests {
|
||||
extent: None,
|
||||
batch: None,
|
||||
no_partial: false,
|
||||
plain: false,
|
||||
},
|
||||
fn_name: Ident::new("load_image", Span::call_site()),
|
||||
struct_name: Ident::new("LoadImage", Span::call_site()),
|
||||
@@ -1759,6 +1776,7 @@ mod tests {
|
||||
extent: None,
|
||||
batch: None,
|
||||
no_partial: false,
|
||||
plain: false,
|
||||
},
|
||||
fn_name: Ident::new("custom_node", Span::call_site()),
|
||||
struct_name: Ident::new("CustomNode", Span::call_site()),
|
||||
|
||||
@@ -29,8 +29,9 @@ fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<
|
||||
input.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Clones the value borrowed from a lending edge. Doubles as the checker-inserted clone-out adapter.
|
||||
#[node_macro::node(category("Debug"))]
|
||||
/// Clones the value borrowed from a lending edge. Doubles as the checker-inserted clone-out
|
||||
/// adapter, so it keeps the plain lowering while the record transition runs.
|
||||
#[node_macro::node(category("Debug"), plain)]
|
||||
fn clone<T: Clone>(_: impl Ctx, #[implementations(List<Raster<CPU>>)] value: &T) -> T {
|
||||
value.clone()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user