hugr_core/builder/
build_traits.rs

1use crate::extension::prelude::MakeTuple;
2use crate::hugr::hugrmut::InsertionResult;
3use crate::hugr::views::HugrView;
4use crate::hugr::{NodeMetadata, ValidationError};
5use crate::ops::{self, OpTag, OpTrait, OpType, Tag, TailLoop};
6use crate::utils::collect_array;
7use crate::{Extension, IncomingPort, Node, OutgoingPort};
8
9use std::iter;
10use std::sync::Arc;
11
12use super::{BuilderWiringError, ModuleBuilder};
13use super::{
14    CircuitBuilder,
15    handle::{BuildHandle, Outputs},
16};
17
18use crate::{
19    ops::handle::{ConstID, DataflowOpID, FuncID, NodeHandle},
20    types::EdgeKind,
21};
22
23use crate::extension::ExtensionRegistry;
24use crate::types::{Signature, Type, TypeArg, TypeRow};
25
26use itertools::Itertools;
27
28use super::{
29    BuildError, Wire, cfg::CFGBuilder, conditional::ConditionalBuilder, dataflow::DFGBuilder,
30    tail_loop::TailLoopBuilder,
31};
32
33use crate::Hugr;
34
35use crate::hugr::HugrMut;
36
37/// Trait for HUGR container builders.
38/// Containers are nodes that are parents of sibling graphs.
39/// Implementations of this trait allow the child sibling graph to be added to
40/// the HUGR.
41pub trait Container {
42    /// The container node.
43    fn container_node(&self) -> Node;
44    /// The underlying [`Hugr`] being built
45    fn hugr_mut(&mut self) -> &mut Hugr;
46    /// Immutable reference to HUGR being built
47    fn hugr(&self) -> &Hugr;
48    /// Add an [`OpType`] as the final child of the container.
49    ///
50    /// Adds the extensions required by the op to the HUGR, if they are not already present.
51    fn add_child_node(&mut self, node: impl Into<OpType>) -> Node {
52        let node: OpType = node.into();
53
54        // Add the extension the operation is defined in to the HUGR.
55        let used_extensions = node
56            .used_extensions()
57            .unwrap_or_else(|e| panic!("Build-time signatures should have valid extensions. {e}"));
58        self.use_extensions(used_extensions);
59
60        let parent = self.container_node();
61        self.hugr_mut().add_node_with_parent(parent, node)
62    }
63
64    /// Adds a non-dataflow edge between two nodes. The kind is given by the operation's [`other_inputs`] or  [`other_outputs`]
65    ///
66    /// [`other_inputs`]: crate::ops::OpTrait::other_input
67    /// [`other_outputs`]: crate::ops::OpTrait::other_output
68    fn add_other_wire(&mut self, src: Node, dst: Node) -> Wire {
69        let (src_port, _) = self.hugr_mut().add_other_edge(src, dst);
70        Wire::new(src, src_port)
71    }
72
73    /// Add a constant value to the container and return a handle to it.
74    ///
75    /// Adds the extensions required by the op to the HUGR, if they are not already present.
76    ///
77    /// # Errors
78    ///
79    /// This function will return an error if there is an error in adding the
80    /// [`OpType::Const`] node.
81    fn add_constant(&mut self, constant: impl Into<ops::Const>) -> ConstID {
82        self.add_child_node(constant.into()).into()
83    }
84
85    /// Insert a HUGR's entrypoint region as a child of the container.
86    ///
87    /// To insert an arbitrary region of a HUGR, use [`Container::add_hugr_region`].
88    fn add_hugr(&mut self, child: Hugr) -> InsertionResult {
89        let region = child.entrypoint();
90        self.add_hugr_region(child, region)
91    }
92
93    /// Insert a HUGR region as a child of the container.
94    ///
95    /// To insert the entrypoint region of a HUGR, use [`Container::add_hugr`].
96    fn add_hugr_region(&mut self, child: Hugr, region: Node) -> InsertionResult {
97        let parent = self.container_node();
98        self.hugr_mut().insert_region(parent, child, region)
99    }
100
101    /// Insert a copy of a HUGR as a child of the container.
102    ///
103    /// Only the portion below the entrypoint will be inserted, with any incoming
104    /// edges broken.
105    fn add_hugr_view<H: HugrView>(&mut self, child: &H) -> InsertionResult<H::Node, Node> {
106        let parent = self.container_node();
107        self.hugr_mut().insert_from_view(parent, child)
108    }
109
110    /// Add metadata to the container node.
111    fn set_metadata(&mut self, key: impl AsRef<str>, meta: impl Into<NodeMetadata>) {
112        let parent = self.container_node();
113        // Implementor's container_node() should be a valid node
114        self.hugr_mut().set_metadata(parent, key, meta);
115    }
116
117    /// Add metadata to a child node.
118    ///
119    /// Returns an error if the specified `child` is not a child of this container
120    fn set_child_metadata(
121        &mut self,
122        child: Node,
123        key: impl AsRef<str>,
124        meta: impl Into<NodeMetadata>,
125    ) {
126        self.hugr_mut().set_metadata(child, key, meta);
127    }
128
129    /// Add an extension to the set of extensions used by the hugr.
130    fn use_extension(&mut self, ext: impl Into<Arc<Extension>>) {
131        self.hugr_mut().use_extension(ext);
132    }
133
134    /// Extend the set of extensions used by the hugr with the extensions in the registry.
135    fn use_extensions<Reg>(&mut self, registry: impl IntoIterator<Item = Reg>)
136    where
137        ExtensionRegistry: Extend<Reg>,
138    {
139        self.hugr_mut().use_extensions(registry);
140    }
141}
142
143/// Types implementing this trait can be used to build complete HUGRs
144/// (with varying entrypoint node types)
145pub trait HugrBuilder: Container {
146    /// Allows adding definitions to the module root of which
147    /// this builder is building a part
148    fn module_root_builder(&mut self) -> ModuleBuilder<&mut Hugr> {
149        debug_assert!(
150            self.hugr()
151                .get_optype(self.hugr().module_root())
152                .is_module()
153        );
154        ModuleBuilder(self.hugr_mut())
155    }
156
157    /// Finish building the HUGR, perform any validation checks and return it.
158    fn finish_hugr(self) -> Result<Hugr, ValidationError<Node>>;
159}
160
161/// Types implementing this trait build a container graph region by borrowing a HUGR
162pub trait SubContainer: Container {
163    /// A handle to the finished container node, typically returned when the
164    /// child graph has been finished.
165    type ContainerHandle;
166    /// Consume the container builder and return the handle, may perform some
167    /// checks before finishing.
168    fn finish_sub_container(self) -> Result<Self::ContainerHandle, BuildError>;
169}
170/// Trait for building dataflow regions of a HUGR.
171pub trait Dataflow: Container {
172    /// Return the number of inputs to the dataflow sibling graph.
173    fn num_inputs(&self) -> usize;
174    /// Return indices of input and output nodes.
175    fn io(&self) -> [Node; 2] {
176        self.hugr()
177            .children(self.container_node())
178            .take(2)
179            .collect_vec()
180            .try_into()
181            .expect("First two children should be IO")
182    }
183    /// Handle to input node.
184    fn input(&self) -> BuildHandle<DataflowOpID> {
185        (self.io()[0], self.num_inputs()).into()
186    }
187    /// Handle to output node.
188    fn output(&self) -> DataflowOpID {
189        self.io()[1].into()
190    }
191    /// Return iterator over all input Value wires.
192    fn input_wires(&self) -> Outputs {
193        self.input().outputs()
194    }
195    /// Add a dataflow [`OpType`] to the sibling graph, wiring up the `input_wires` to the
196    /// incoming ports of the resulting node.
197    ///
198    /// Adds the extensions required by the op to the HUGR, if they are not already present.
199    ///
200    /// # Errors
201    ///
202    /// Returns a [`BuildError::OperationWiring`] error if the `input_wires` cannot be connected.
203    fn add_dataflow_op(
204        &mut self,
205        nodetype: impl Into<OpType>,
206        input_wires: impl IntoIterator<Item = Wire>,
207    ) -> Result<BuildHandle<DataflowOpID>, BuildError> {
208        let outs = add_node_with_wires(self, nodetype, input_wires)?;
209
210        Ok(outs.into())
211    }
212
213    /// Insert a hugr-defined op to the sibling graph, wiring up the
214    /// `input_wires` to the incoming ports of the resulting root node.
215    ///
216    /// Inserts everything from the entrypoint region of the HUGR.
217    /// See [`Dataflow::add_hugr_region_with_wires`] for a generic version that allows
218    /// inserting a region other than the entrypoint.
219    ///
220    /// # Errors
221    ///
222    /// This function will return an error if there is an error when adding the
223    /// node.
224    fn add_hugr_with_wires(
225        &mut self,
226        hugr: Hugr,
227        input_wires: impl IntoIterator<Item = Wire>,
228    ) -> Result<BuildHandle<DataflowOpID>, BuildError> {
229        let region = hugr.entrypoint();
230        self.add_hugr_region_with_wires(hugr, region, input_wires)
231    }
232
233    /// Insert a hugr-defined op to the sibling graph, wiring up the
234    /// `input_wires` to the incoming ports of the resulting root node.
235    ///
236    /// `region` must be a node in the `hugr`. See [`Dataflow::add_hugr_with_wires`]
237    /// for a helper that inserts the entrypoint region to the HUGR.
238    ///
239    /// # Errors
240    ///
241    /// This function will return an error if there is an error when adding the
242    /// node.
243    fn add_hugr_region_with_wires(
244        &mut self,
245        hugr: Hugr,
246        region: Node,
247        input_wires: impl IntoIterator<Item = Wire>,
248    ) -> Result<BuildHandle<DataflowOpID>, BuildError> {
249        let optype = hugr.get_optype(region).clone();
250        let num_outputs = optype.value_output_count();
251        let node = self.add_hugr_region(hugr, region).inserted_entrypoint;
252
253        wire_up_inputs(input_wires, node, self).map_err(|error| BuildError::OperationWiring {
254            op: Box::new(optype),
255            error,
256        })?;
257
258        Ok((node, num_outputs).into())
259    }
260
261    /// Copy a hugr-defined op into the sibling graph, wiring up the
262    /// `input_wires` to the incoming ports of the node that was the entrypoint.
263    /// (Note, any part of `hugr` outside the entrypoint is not copied;
264    /// this may lead to new input ports being disconnected.)
265    ///
266    /// # Errors
267    ///
268    /// This function will return an error if there is an error when adding the
269    /// node.
270    fn add_hugr_view_with_wires(
271        &mut self,
272        hugr: &impl HugrView,
273        input_wires: impl IntoIterator<Item = Wire>,
274    ) -> Result<BuildHandle<DataflowOpID>, BuildError> {
275        let node = self.add_hugr_view(hugr).inserted_entrypoint;
276        let optype = hugr.get_optype(hugr.entrypoint()).clone();
277        let num_outputs = optype.value_output_count();
278
279        wire_up_inputs(input_wires, node, self).map_err(|error| BuildError::OperationWiring {
280            op: Box::new(optype),
281            error,
282        })?;
283
284        Ok((node, num_outputs).into())
285    }
286
287    /// Wire up the `output_wires` to the input ports of the Output node.
288    ///
289    /// # Errors
290    ///
291    /// This function will return an error if there is an error when wiring up.
292    fn set_outputs(
293        &mut self,
294        output_wires: impl IntoIterator<Item = Wire>,
295    ) -> Result<(), BuildError> {
296        let [_, out] = self.io();
297        wire_up_inputs(output_wires.into_iter().collect_vec(), out, self).map_err(|error| {
298            BuildError::OutputWiring {
299                container_op: Box::new(self.hugr().get_optype(self.container_node()).clone()),
300                container_node: self.container_node(),
301                error,
302            }
303        })
304    }
305
306    /// Return an array of the input wires.
307    ///
308    /// # Panics
309    ///
310    /// Panics if the number of input Wires does not match the size of the array.
311    #[track_caller]
312    fn input_wires_arr<const N: usize>(&self) -> [Wire; N] {
313        collect_array(self.input_wires())
314    }
315
316    /// Return a builder for a [`crate::ops::DFG`] node, i.e. a nested dataflow subgraph,
317    /// given a signature describing its input and output types and extension delta,
318    /// and the input wires (which must match the input types)
319    ///
320    /// # Errors
321    ///
322    /// This function will return an error if there is an error when building
323    /// the DFG node.
324    // TODO: Should this be one function, or should there be a temporary "op" one like with the others?
325    fn dfg_builder(
326        &mut self,
327        signature: Signature,
328        input_wires: impl IntoIterator<Item = Wire>,
329    ) -> Result<DFGBuilder<&mut Hugr>, BuildError> {
330        let op = ops::DFG {
331            signature: signature.clone(),
332        };
333        let (dfg_n, _) = add_node_with_wires(self, op, input_wires)?;
334
335        DFGBuilder::create_with_io(self.hugr_mut(), dfg_n, signature)
336    }
337
338    /// Return a builder for a [`crate::ops::DFG`] node, i.e. a nested dataflow subgraph,
339    /// that is endomorphic (the output types are the same as the input types).
340    /// The `inputs` must be an iterable over pairs of the type of the input and
341    /// the corresponding wire.
342    fn dfg_builder_endo(
343        &mut self,
344        inputs: impl IntoIterator<Item = (Type, Wire)>,
345    ) -> Result<DFGBuilder<&mut Hugr>, BuildError> {
346        let (types, input_wires): (Vec<Type>, Vec<Wire>) = inputs.into_iter().unzip();
347        self.dfg_builder(Signature::new_endo(types), input_wires)
348    }
349
350    /// Return a builder for a [`crate::ops::CFG`] node,
351    /// i.e. a nested controlflow subgraph.
352    /// The `inputs` must be an iterable over pairs of the type of the input and
353    /// the corresponding wire.
354    /// The `output_types` are the types of the outputs.
355    ///
356    /// # Errors
357    ///
358    /// This function will return an error if there is an error when building
359    /// the CFG node.
360    fn cfg_builder(
361        &mut self,
362        inputs: impl IntoIterator<Item = (Type, Wire)>,
363        output_types: TypeRow,
364    ) -> Result<CFGBuilder<&mut Hugr>, BuildError> {
365        let (input_types, input_wires): (Vec<Type>, Vec<Wire>) = inputs.into_iter().unzip();
366
367        let inputs: TypeRow = input_types.into();
368
369        let (cfg_node, _) = add_node_with_wires(
370            self,
371            ops::CFG {
372                signature: Signature::new(inputs.clone(), output_types.clone()),
373            },
374            input_wires,
375        )?;
376        CFGBuilder::create(self.hugr_mut(), cfg_node, inputs, output_types)
377    }
378
379    /// Load a static constant and return the local dataflow wire for that constant.
380    /// Adds a [`OpType::LoadConstant`] node.
381    fn load_const(&mut self, cid: &ConstID) -> Wire {
382        let const_node = cid.node();
383        let nodetype = self.hugr().get_optype(const_node);
384        let op: ops::Const = nodetype
385            .clone()
386            .try_into()
387            .expect("ConstID does not refer to Const op.");
388
389        let load_n = self
390            .add_dataflow_op(
391                ops::LoadConstant {
392                    datatype: op.get_type().clone(),
393                },
394                // Constant wire from the constant value node
395                vec![Wire::new(const_node, OutgoingPort::from(0))],
396            )
397            .expect("The constant type should match the LoadConstant type.");
398
399        load_n.out_wire(0)
400    }
401
402    /// Load a static constant and return the local dataflow wire for that constant.
403    /// Adds a [`ops::Const`] and a [`ops::LoadConstant`] node.
404    fn add_load_const(&mut self, constant: impl Into<ops::Const>) -> Wire {
405        let cid = self.add_constant(constant);
406        self.load_const(&cid)
407    }
408
409    /// Load a [`ops::Value`] and return the local dataflow wire for that constant.
410    /// Adds a [`ops::Const`] and a [`ops::LoadConstant`] node.
411    fn add_load_value(&mut self, constant: impl Into<ops::Value>) -> Wire {
412        self.add_load_const(constant.into())
413    }
414
415    /// Load a static function and return the local dataflow wire for that function.
416    /// Adds a [`OpType::LoadFunction`] node.
417    ///
418    /// The `DEF` const generic is used to indicate whether the function is defined
419    /// or just declared.
420    fn load_func<const DEFINED: bool>(
421        &mut self,
422        fid: &FuncID<DEFINED>,
423        type_args: &[TypeArg],
424    ) -> Result<Wire, BuildError> {
425        let func_node = fid.node();
426        let func_op = self.hugr().get_optype(func_node);
427        let func_sig = match func_op {
428            OpType::FuncDefn(fd) => fd.signature().clone(),
429            OpType::FuncDecl(fd) => fd.signature().clone(),
430            _ => {
431                return Err(BuildError::UnexpectedType {
432                    node: func_node,
433                    op_desc: "FuncDecl/FuncDefn",
434                });
435            }
436        };
437
438        let load_n = self.add_dataflow_op(
439            ops::LoadFunction::try_new(func_sig, type_args)?,
440            // Static wire from the function node
441            vec![Wire::new(func_node, func_op.static_output_port().unwrap())],
442        )?;
443
444        Ok(load_n.out_wire(0))
445    }
446
447    /// Return a builder for a [`crate::ops::TailLoop`] node.
448    /// The `inputs` must be an iterable over pairs of the type of the input and
449    /// the corresponding wire.
450    /// The `output_types` are the types of the outputs.
451    ///
452    /// # Errors
453    ///
454    /// This function will return an error if there is an error when building
455    /// the [`ops::TailLoop`] node.
456    ///
457    fn tail_loop_builder(
458        &mut self,
459        just_inputs: impl IntoIterator<Item = (Type, Wire)>,
460        inputs_outputs: impl IntoIterator<Item = (Type, Wire)>,
461        just_out_types: TypeRow,
462    ) -> Result<TailLoopBuilder<&mut Hugr>, BuildError> {
463        let (input_types, mut input_wires): (Vec<Type>, Vec<Wire>) =
464            just_inputs.into_iter().unzip();
465        let (rest_types, rest_input_wires): (Vec<Type>, Vec<Wire>) =
466            inputs_outputs.into_iter().unzip();
467        input_wires.extend(rest_input_wires);
468
469        let tail_loop = ops::TailLoop {
470            just_inputs: input_types.into(),
471            just_outputs: just_out_types,
472            rest: rest_types.into(),
473        };
474        // TODO: Make input extensions a parameter
475        let (loop_node, _) = add_node_with_wires(self, tail_loop.clone(), input_wires)?;
476
477        TailLoopBuilder::create_with_io(self.hugr_mut(), loop_node, &tail_loop)
478    }
479
480    /// Return a builder for a [`crate::ops::Conditional`] node.
481    /// `sum_input` is a tuple of the type of the Sum
482    /// variants and the corresponding wire.
483    ///
484    /// The `other_inputs` must be an iterable over pairs of the type of the input and
485    /// the corresponding wire.
486    /// The `output_types` are the types of the outputs.
487    ///
488    /// # Errors
489    ///
490    /// This function will return an error if there is an error when building
491    /// the Conditional node.
492    fn conditional_builder(
493        &mut self,
494        (sum_rows, sum_wire): (impl IntoIterator<Item = TypeRow>, Wire),
495        other_inputs: impl IntoIterator<Item = (Type, Wire)>,
496        output_types: TypeRow,
497    ) -> Result<ConditionalBuilder<&mut Hugr>, BuildError> {
498        let mut input_wires = vec![sum_wire];
499        let (input_types, rest_input_wires): (Vec<Type>, Vec<Wire>) =
500            other_inputs.into_iter().unzip();
501
502        input_wires.extend(rest_input_wires);
503        let inputs: TypeRow = input_types.into();
504        let sum_rows: Vec<_> = sum_rows.into_iter().collect();
505        let n_cases = sum_rows.len();
506        let n_out_wires = output_types.len();
507
508        let conditional_id = self.add_dataflow_op(
509            ops::Conditional {
510                sum_rows,
511                other_inputs: inputs,
512                outputs: output_types,
513            },
514            input_wires,
515        )?;
516
517        Ok(ConditionalBuilder {
518            base: self.hugr_mut(),
519            conditional_node: conditional_id.node(),
520            n_out_wires,
521            case_nodes: vec![None; n_cases],
522        })
523    }
524
525    /// Add an order edge from `before` to `after`. Assumes any additional edges
526    /// to both nodes will be Order kind.
527    fn set_order(&mut self, before: &impl NodeHandle, after: &impl NodeHandle) {
528        self.add_other_wire(before.node(), after.node());
529    }
530
531    /// Get the type of a Value [`Wire`]. If not valid port or of Value kind, returns None.
532    fn get_wire_type(&self, wire: Wire) -> Result<Type, BuildError> {
533        let kind = self.hugr().get_optype(wire.node()).port_kind(wire.source());
534
535        if let Some(EdgeKind::Value(typ)) = kind {
536            Ok(typ)
537        } else {
538            Err(BuildError::WireNotFound(wire))
539        }
540    }
541
542    /// Add a [`MakeTuple`] node and wire in the `values` Wires,
543    /// returning the Wire corresponding to the tuple.
544    ///
545    /// # Errors
546    ///
547    /// This function will return an error if there is an error adding the
548    /// [`MakeTuple`] node.
549    fn make_tuple(&mut self, values: impl IntoIterator<Item = Wire>) -> Result<Wire, BuildError> {
550        let values = values.into_iter().collect_vec();
551        let types: Result<Vec<Type>, _> = values
552            .iter()
553            .map(|&wire| self.get_wire_type(wire))
554            .collect();
555        let types = types?.into();
556        let make_op = self.add_dataflow_op(MakeTuple(types), values)?;
557        Ok(make_op.out_wire(0))
558    }
559
560    /// Add a [`Tag`] node and wire in the `value` Wire,
561    /// to make a value with Sum type, with `tag` and possible types described
562    /// by `variants`.
563    /// Returns the Wire corresponding to the Sum value.
564    ///
565    /// # Errors
566    ///
567    /// This function will return an error if there is an error adding the
568    /// Tag node.
569    fn make_sum(
570        &mut self,
571        tag: usize,
572        variants: impl IntoIterator<Item = TypeRow>,
573        values: impl IntoIterator<Item = Wire>,
574    ) -> Result<Wire, BuildError> {
575        let make_op = self.add_dataflow_op(
576            Tag {
577                tag,
578                variants: variants.into_iter().collect_vec(),
579            },
580            values.into_iter().collect_vec(),
581        )?;
582        Ok(make_op.out_wire(0))
583    }
584
585    /// Use the wires in `values` to return a wire corresponding to the
586    /// "Continue" variant of a [`ops::TailLoop`] with `loop_signature`.
587    ///
588    /// Packs the values in to a tuple and tags appropriately to generate a
589    /// value of Sum type.
590    ///
591    /// # Errors
592    ///
593    /// This function will return an error if there is an error in adding the nodes.
594    fn make_continue(
595        &mut self,
596        tail_loop: ops::TailLoop,
597        values: impl IntoIterator<Item = Wire>,
598    ) -> Result<Wire, BuildError> {
599        self.make_sum(
600            TailLoop::CONTINUE_TAG,
601            [tail_loop.just_inputs, tail_loop.just_outputs],
602            values,
603        )
604    }
605
606    /// Use the wires in `values` to return a wire corresponding to the
607    /// "Break" variant of a [`ops::TailLoop`] with `loop_signature`.
608    ///
609    /// Packs the values in to a tuple and tags appropriately to generate a
610    /// value of Sum type.
611    ///
612    /// # Errors
613    ///
614    /// This function will return an error if there is an error in adding the nodes.
615    fn make_break(
616        &mut self,
617        loop_op: ops::TailLoop,
618        values: impl IntoIterator<Item = Wire>,
619    ) -> Result<Wire, BuildError> {
620        self.make_sum(
621            TailLoop::BREAK_TAG,
622            [loop_op.just_inputs, loop_op.just_outputs],
623            values,
624        )
625    }
626
627    /// Add a [`ops::Call`] node, calling `function`, with inputs
628    /// specified by `input_wires`. Returns a handle to the corresponding Call node.
629    ///
630    /// # Errors
631    ///
632    /// This function will return an error if there is an error adding the Call
633    /// node, or if `function` does not refer to a [`ops::FuncDecl`] or
634    /// [`ops::FuncDefn`] node.
635    fn call<const DEFINED: bool>(
636        &mut self,
637        function: &FuncID<DEFINED>,
638        type_args: &[TypeArg],
639        input_wires: impl IntoIterator<Item = Wire>,
640    ) -> Result<BuildHandle<DataflowOpID>, BuildError> {
641        let hugr = self.hugr();
642        let def_op = hugr.get_optype(function.node());
643        let type_scheme = match def_op {
644            OpType::FuncDefn(fd) => fd.signature().clone(),
645            OpType::FuncDecl(fd) => fd.signature().clone(),
646            _ => {
647                return Err(BuildError::UnexpectedType {
648                    node: function.node(),
649                    op_desc: "FuncDecl/FuncDefn",
650                });
651            }
652        };
653        let op: OpType = ops::Call::try_new(type_scheme, type_args)?.into();
654        let const_in_port = op.static_input_port().unwrap();
655        let op_id = self.add_dataflow_op(op, input_wires)?;
656        let src_port = self.hugr_mut().num_outputs(function.node()) - 1;
657
658        self.hugr_mut()
659            .connect(function.node(), src_port, op_id.node(), const_in_port);
660        Ok(op_id)
661    }
662
663    /// For the vector of `wires`, produce a `CircuitBuilder` where ops can be
664    /// added using indices in to the vector.
665    fn as_circuit(&mut self, wires: impl IntoIterator<Item = Wire>) -> CircuitBuilder<'_, Self> {
666        CircuitBuilder::new(wires, self)
667    }
668
669    /// Add a [Barrier] to a set of wires and return them in the same order.
670    ///
671    /// [Barrier]: crate::extension::prelude::Barrier
672    ///
673    /// # Errors
674    ///
675    /// This function will return an error if there is an error adding the Barrier node
676    /// or retrieving the type of the incoming wires.
677    fn add_barrier(
678        &mut self,
679        wires: impl IntoIterator<Item = Wire>,
680    ) -> Result<BuildHandle<DataflowOpID>, BuildError> {
681        let wires = wires.into_iter().collect_vec();
682        let types: Result<Vec<Type>, _> =
683            wires.iter().map(|&wire| self.get_wire_type(wire)).collect();
684        let types = types?;
685        let barrier_op =
686            self.add_dataflow_op(crate::extension::prelude::Barrier::new(types), wires)?;
687        Ok(barrier_op)
688    }
689}
690
691/// Add a node to the graph, wiring up the `inputs` to the input ports of the resulting node.
692///
693/// Adds the extensions required by the op to the HUGR, if they are not already present.
694///
695/// # Errors
696///
697/// Returns a [`BuildError::OperationWiring`] if any of the connections produces an
698/// invalid edge.
699fn add_node_with_wires<T: Dataflow + ?Sized>(
700    data_builder: &mut T,
701    nodetype: impl Into<OpType>,
702    inputs: impl IntoIterator<Item = Wire>,
703) -> Result<(Node, usize), BuildError> {
704    let op: OpType = nodetype.into();
705    let num_outputs = op.value_output_count();
706    let op_node = data_builder.add_child_node(op.clone());
707
708    wire_up_inputs(inputs, op_node, data_builder).map_err(|error| BuildError::OperationWiring {
709        op: Box::new(op),
710        error,
711    })?;
712
713    Ok((op_node, num_outputs))
714}
715
716/// Connect each of the `inputs` wires sequentially to the input ports of
717/// `op_node`.
718///
719/// # Errors
720///
721/// Returns a [`BuilderWiringError`] if any of the connections produces an
722/// invalid edge.
723fn wire_up_inputs<T: Dataflow + ?Sized>(
724    inputs: impl IntoIterator<Item = Wire>,
725    op_node: Node,
726    data_builder: &mut T,
727) -> Result<(), BuilderWiringError> {
728    for (dst_port, wire) in inputs.into_iter().enumerate() {
729        wire_up(data_builder, wire.node(), wire.source(), op_node, dst_port)?;
730    }
731    Ok(())
732}
733
734/// Add edge from src to dst.
735///
736/// # Errors
737///
738/// Returns a [`BuilderWiringError`] if the edge is invalid.
739fn wire_up<T: Dataflow + ?Sized>(
740    data_builder: &mut T,
741    src: Node,
742    src_port: impl Into<OutgoingPort>,
743    dst: Node,
744    dst_port: impl Into<IncomingPort>,
745) -> Result<bool, BuilderWiringError> {
746    let src_port = src_port.into();
747    let dst_port = dst_port.into();
748    let base = data_builder.hugr_mut();
749
750    let src_parent = base.get_parent(src);
751    let src_parent_parent = src_parent.and_then(|src| base.get_parent(src));
752    let dst_parent = base.get_parent(dst);
753    let local_source = src_parent == dst_parent;
754    if let EdgeKind::Value(typ) = base.get_optype(src).port_kind(src_port).unwrap() {
755        if !local_source {
756            // Non-local value sources require a state edge to an ancestor of dst
757            if !typ.copyable() {
758                return Err(BuilderWiringError::NonCopyableIntergraph {
759                    src,
760                    src_offset: src_port.into(),
761                    dst,
762                    dst_offset: dst_port.into(),
763                    typ: Box::new(typ),
764                });
765            }
766
767            let src_parent = src_parent.expect("Node has no parent");
768            let Some(src_sibling) = iter::successors(dst_parent, |&p| base.get_parent(p))
769                .tuple_windows()
770                .find_map(|(ancestor, ancestor_parent)| {
771                    (ancestor_parent == src_parent ||
772                        // Dom edge - in CFGs
773                        Some(ancestor_parent) == src_parent_parent)
774                        .then_some(ancestor)
775                })
776            else {
777                return Err(BuilderWiringError::NoRelationIntergraph {
778                    src,
779                    src_offset: src_port.into(),
780                    dst,
781                    dst_offset: dst_port.into(),
782                });
783            };
784
785            if !OpTag::ControlFlowChild.is_superset(base.get_optype(src).tag())
786                && !OpTag::ControlFlowChild.is_superset(base.get_optype(src_sibling).tag())
787            {
788                // Add a state order constraint unless one of the nodes is a CFG BasicBlock
789                base.add_other_edge(src, src_sibling);
790            }
791        } else if !typ.copyable() & base.linked_ports(src, src_port).next().is_some() {
792            // Don't copy linear edges.
793            return Err(BuilderWiringError::NoCopyLinear {
794                typ: Box::new(typ),
795                src,
796                src_offset: src_port.into(),
797            });
798        }
799    }
800
801    data_builder
802        .hugr_mut()
803        .connect(src, src_port, dst, dst_port);
804    Ok(local_source
805        && matches!(
806            data_builder
807                .hugr_mut()
808                .get_optype(dst)
809                .port_kind(dst_port)
810                .unwrap(),
811            EdgeKind::Value(_)
812        ))
813}
814
815/// Trait implemented by builders of Dataflow Hugrs
816pub trait DataflowHugr: HugrBuilder + Dataflow {
817    /// Set outputs of dataflow HUGR and return validated HUGR
818    /// # Errors
819    ///
820    /// * if there is an error when setting outputs
821    /// * if the Hugr does not validate
822    fn finish_hugr_with_outputs(
823        mut self,
824        outputs: impl IntoIterator<Item = Wire>,
825    ) -> Result<Hugr, BuildError>
826    where
827        Self: Sized,
828    {
829        self.set_outputs(outputs)?;
830        Ok(self.finish_hugr()?)
831    }
832}
833
834/// Trait implemented by builders of Dataflow container regions of a HUGR
835pub trait DataflowSubContainer: SubContainer + Dataflow {
836    /// Set the outputs of the graph and consume the builder, while returning a
837    /// handle to the parent.
838    ///
839    /// # Errors
840    ///
841    /// This function will return an error if there is an error when setting outputs.
842    fn finish_with_outputs(
843        mut self,
844        outputs: impl IntoIterator<Item = Wire>,
845    ) -> Result<Self::ContainerHandle, BuildError>
846    where
847        Self: Sized,
848    {
849        self.set_outputs(outputs)?;
850        self.finish_sub_container()
851    }
852}
853
854impl<T: HugrBuilder + Dataflow> DataflowHugr for T {}
855impl<T: SubContainer + Dataflow> DataflowSubContainer for T {}