diff --git a/hugr-cli/src/validate.rs b/hugr-cli/src/validate.rs index d3ea32a423..e33ff6506b 100644 --- a/hugr-cli/src/validate.rs +++ b/hugr-cli/src/validate.rs @@ -3,7 +3,7 @@ use clap::Parser; use clap_verbosity_flag::log::Level; use hugr::package::PackageValidationError; -use hugr::Hugr; +use hugr::{Hugr, HugrView}; use crate::hugr_io::HugrInputArgs; use crate::{CliError, OtherArgs}; diff --git a/hugr-core/src/builder.rs b/hugr-core/src/builder.rs index d47e8de6e4..e5414e2e95 100644 --- a/hugr-core/src/builder.rs +++ b/hugr-core/src/builder.rs @@ -26,7 +26,7 @@ //! `CircuitBuilder`. //! //! ```rust -//! # use hugr::Hugr; +//! # use hugr::{Hugr, HugrView}; //! # use hugr::builder::{BuildError, BuildHandle, Container, DFGBuilder, Dataflow, DataflowHugr, ModuleBuilder, DataflowSubContainer, HugrBuilder}; //! use hugr::extension::prelude::bool_t; //! use hugr::std_extensions::logic::{self, LogicOp}; @@ -138,7 +138,7 @@ pub fn inout_sig(inputs: impl Into, outputs: impl Into) -> Sig pub enum BuildError { /// The constructed HUGR is invalid. #[error("The constructed HUGR is invalid: {0}.")] - InvalidHUGR(#[from] ValidationError), + InvalidHUGR(#[from] ValidationError), /// SignatureError in trying to construct a node (differs from /// [ValidationError::SignatureError] in that we could not construct a node to report about) #[error(transparent)] diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index 1a11a18beb..0d5053b955 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -161,7 +161,7 @@ pub trait Container { /// (with varying root node types) pub trait HugrBuilder: Container { /// Finish building the HUGR, perform any validation checks and return it. - fn finish_hugr(self) -> Result; + fn finish_hugr(self) -> Result>; } /// Types implementing this trait build a container graph region by borrowing a HUGR diff --git a/hugr-core/src/builder/cfg.rs b/hugr-core/src/builder/cfg.rs index c0587955db..21f6a6c0f0 100644 --- a/hugr-core/src/builder/cfg.rs +++ b/hugr-core/src/builder/cfg.rs @@ -155,7 +155,7 @@ impl CFGBuilder { } impl HugrBuilder for CFGBuilder { - fn finish_hugr(self) -> Result { + fn finish_hugr(self) -> Result> { self.base.validate()?; Ok(self.base) } diff --git a/hugr-core/src/builder/conditional.rs b/hugr-core/src/builder/conditional.rs index 9c4dfdba53..a99eb46eff 100644 --- a/hugr-core/src/builder/conditional.rs +++ b/hugr-core/src/builder/conditional.rs @@ -138,7 +138,7 @@ impl + AsRef> ConditionalBuilder { } impl HugrBuilder for ConditionalBuilder { - fn finish_hugr(self) -> Result { + fn finish_hugr(self) -> Result> { self.base.validate()?; Ok(self.base) } diff --git a/hugr-core/src/builder/dataflow.rs b/hugr-core/src/builder/dataflow.rs index f430c4a7ce..5e66fe4fc2 100644 --- a/hugr-core/src/builder/dataflow.rs +++ b/hugr-core/src/builder/dataflow.rs @@ -99,7 +99,7 @@ impl DFGBuilder { } impl HugrBuilder for DFGBuilder { - fn finish_hugr(self) -> Result { + fn finish_hugr(self) -> Result> { self.base.validate()?; Ok(self.base) } @@ -306,7 +306,7 @@ impl + AsRef, T: From>> SubContainer for } impl HugrBuilder for DFGWrapper { - fn finish_hugr(self) -> Result { + fn finish_hugr(self) -> Result> { self.0.finish_hugr() } } diff --git a/hugr-core/src/builder/module.rs b/hugr-core/src/builder/module.rs index 21f360b233..6fb4edde9c 100644 --- a/hugr-core/src/builder/module.rs +++ b/hugr-core/src/builder/module.rs @@ -50,7 +50,7 @@ impl Default for ModuleBuilder { } impl HugrBuilder for ModuleBuilder { - fn finish_hugr(self) -> Result { + fn finish_hugr(self) -> Result> { self.0.validate()?; Ok(self.0) } diff --git a/hugr-core/src/builder/tail_loop.rs b/hugr-core/src/builder/tail_loop.rs index bbe4999446..e8b97492ca 100644 --- a/hugr-core/src/builder/tail_loop.rs +++ b/hugr-core/src/builder/tail_loop.rs @@ -105,7 +105,7 @@ mod test { use super::*; #[test] fn basic_loop() -> Result<(), BuildError> { - let build_result: Result = { + let build_result: Result> = { let mut loop_b = TailLoopBuilder::new(vec![], vec![bool_t()], vec![usize_t()])?; let [i1] = loop_b.input_wires_arr(); let const_wire = loop_b.add_load_value(ConstUsize::new(1)); diff --git a/hugr-core/src/extension/resolution.rs b/hugr-core/src/extension/resolution.rs index a08cbfb380..b55aaa75ee 100644 --- a/hugr-core/src/extension/resolution.rs +++ b/hugr-core/src/extension/resolution.rs @@ -33,6 +33,7 @@ use types_mut::{ use derive_more::{Display, Error, From}; use super::{Extension, ExtensionId, ExtensionRegistry, ExtensionSet}; +use crate::core::HugrNode; use crate::ops::constant::ValueName; use crate::ops::custom::OpaqueOpError; use crate::ops::{NamedOp, OpName, OpType, Value}; @@ -78,11 +79,11 @@ pub fn resolve_value_extensions( /// Errors that can occur during extension resolution. #[derive(Debug, Display, Clone, Error, From, PartialEq)] #[non_exhaustive] -pub enum ExtensionResolutionError { +pub enum ExtensionResolutionError { /// Could not resolve an opaque operation to an extension operation. #[display("Error resolving opaque operation: {_0}")] #[from] - OpaqueOpError(OpaqueOpError), + OpaqueOpError(OpaqueOpError), /// An operation requires an extension that is not in the given registry. #[display( "{op}{} requires extension {missing_extension}, but it could not be found in the extension list used during resolution. The available extensions are: {}", @@ -91,7 +92,7 @@ pub enum ExtensionResolutionError { )] MissingOpExtension { /// The node that requires the extension. - node: Option, + node: Option, /// The operation that requires the extension. op: OpName, /// The missing extension @@ -107,7 +108,7 @@ pub enum ExtensionResolutionError { )] MissingTypeExtension { /// The node that requires the extension. - node: Option, + node: Option, /// The type that requires the extension. ty: TypeName, /// The missing extension @@ -149,10 +150,10 @@ pub enum ExtensionResolutionError { }, } -impl ExtensionResolutionError { +impl ExtensionResolutionError { /// Create a new error for missing operation extensions. pub fn missing_op_extension( - node: Option, + node: Option, op: &OpType, missing_extension: &ExtensionId, extensions: &ExtensionRegistry, @@ -167,7 +168,7 @@ impl ExtensionResolutionError { /// Create a new error for missing type extensions. pub fn missing_type_extension( - node: Option, + node: Option, ty: &TypeName, missing_extension: &ExtensionId, extensions: &WeakExtensionRegistry, @@ -184,7 +185,7 @@ impl ExtensionResolutionError { /// Errors that can occur when collecting extension requirements. #[derive(Debug, Display, Clone, Error, From, PartialEq)] #[non_exhaustive] -pub enum ExtensionCollectionError { +pub enum ExtensionCollectionError { /// An operation requires an extension that is not in the given registry. #[display( "{op}{} contains custom types for which have lost the reference to their defining extensions. Dropped extensions: {}", @@ -193,7 +194,7 @@ pub enum ExtensionCollectionError { )] DroppedOpExtensions { /// The node that is missing extensions. - node: Option, + node: Option, /// The operation that is missing extensions. op: OpName, /// The missing extensions. @@ -212,10 +213,10 @@ pub enum ExtensionCollectionError { }, } -impl ExtensionCollectionError { +impl ExtensionCollectionError { /// Create a new error when operation extensions have been dropped. pub fn dropped_op_extension( - node: Option, + node: Option, op: &OpType, missing_extension: impl IntoIterator, ) -> Self { diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 6b8b91e86d..0c0edcf5ea 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -337,7 +337,7 @@ pub enum LoadHugrError { Load(#[from] serde_json::Error), /// Validation of the loaded Hugr failed. #[error(transparent)] - Validation(#[from] ValidationError), + Validation(#[from] ValidationError), /// Error when resolving extension operations and types. #[error(transparent)] Extension(#[from] ExtensionResolutionError), diff --git a/hugr-core/src/hugr/serialize/test.rs b/hugr-core/src/hugr/serialize/test.rs index 2e868c0634..4518469f64 100644 --- a/hugr-core/src/hugr/serialize/test.rs +++ b/hugr-core/src/hugr/serialize/test.rs @@ -12,6 +12,7 @@ use crate::extension::test::SimpleOpDef; use crate::extension::ExtensionRegistry; use crate::hugr::internal::HugrMutInternals; use crate::hugr::validate::ValidationError; +use crate::hugr::views::ExtractionResult; use crate::ops::custom::{ExtensionOp, OpaqueOp, OpaqueOpError}; use crate::ops::{self, dataflow::IOTrait, Input, Module, Output, Value, DFG}; use crate::std_extensions::arithmetic::float_types::float64_type; @@ -174,10 +175,14 @@ fn ser_roundtrip_check_schema( /// equality checking. /// /// Returns the deserialized HUGR. -pub fn check_hugr_roundtrip(hugr: &Hugr, check_schema: bool) -> Hugr { - let new_hugr = ser_roundtrip_check_schema(hugr, get_schemas(check_schema)); +pub fn check_hugr_roundtrip(hugr: &impl HugrView, check_schema: bool) -> Hugr { + // Transform the whole view into a HUGR. + let (mut base, extract_map) = hugr.extract_hugr(hugr.module_root()); + base.set_entrypoint(extract_map.extracted_node(hugr.entrypoint())); - check_hugr(hugr, &new_hugr); + let new_hugr = ser_roundtrip_check_schema(&base, get_schemas(check_schema)); + + check_hugr(&base, &new_hugr); new_hugr } diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index 0dc573a9d4..ffb1b5b4d7 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -6,9 +6,9 @@ use std::iter; use itertools::Itertools; use petgraph::algo::dominators::{self, Dominators}; use petgraph::visit::{Topo, Walker}; -use portgraph::{LinkView, PortView}; use thiserror::Error; +use crate::core::HugrNode; use crate::extension::SignatureError; use crate::ops::constant::ConstTypeError; @@ -17,9 +17,9 @@ use crate::ops::validate::{ChildrenEdgeData, ChildrenValidationError, EdgeValida use crate::ops::{FuncDefn, NamedOp, OpName, OpTag, OpTrait, OpType, ValidateOp}; use crate::types::type_param::TypeParam; use crate::types::EdgeKind; -use crate::{Direction, Hugr, Node, Port}; +use crate::{Direction, Port}; -use super::internal::HugrInternals; +use super::internal::PortgraphNodeMap; use super::views::HugrView; use super::ExtensionError; @@ -28,38 +28,30 @@ use super::ExtensionError; /// /// TODO: Consider implementing updatable dominator trees and storing it in the /// Hugr to avoid recomputing it every time. -struct ValidationContext<'a> { - hugr: &'a Hugr, +pub(super) struct ValidationContext<'a, H: HugrView> { + hugr: &'a H, /// Dominator tree for each CFG region, using the container node as index. - dominators: HashMap>, + dominators: HashMap>, } -impl Hugr { - /// Check the validity of the HUGR. - pub fn validate(&self) -> Result<(), ValidationError> { - let mut validator = ValidationContext::new(self); - validator.validate() - } -} - -impl<'a> ValidationContext<'a> { +impl<'a, H: HugrView> ValidationContext<'a, H> { /// Create a new validation context. - pub fn new(hugr: &'a Hugr) -> Self { + pub fn new(hugr: &'a H) -> Self { let dominators = HashMap::new(); Self { hugr, dominators } } /// Check the validity of the HUGR. - pub fn validate(&mut self) -> Result<(), ValidationError> { + pub fn validate(&mut self) -> Result<(), ValidationError> { // Root node must be a root in the hierarchy. - if !self.hugr.hierarchy().is_root(self.hugr.module_root) { + if self.hugr.get_parent(self.hugr.module_root()).is_some() { return Err(ValidationError::RootNotRoot { node: self.hugr.module_root(), }); } // Node-specific checks - for node in self.hugr.graph.nodes_iter().map_into() { + for node in self.hugr.nodes().map_into() { self.validate_node(node)?; } @@ -85,10 +77,10 @@ impl<'a> ValidationContext<'a> { /// /// The results of this computation should be cached in `self.dominators`. /// We don't do it here to avoid mutable borrows. - fn compute_dominator(&self, parent: Node) -> Dominators { - let (region, _) = self.hugr.region_portgraph(parent); + fn compute_dominator(&self, parent: H::Node) -> Dominators { + let (region, node_map) = self.hugr.region_portgraph(parent); let entry_node = self.hugr.children(parent).next().unwrap(); - dominators::simple_fast(®ion, entry_node.into_portgraph()) + dominators::simple_fast(®ion, node_map.to_portgraph(entry_node)) } /// Check the constraints on a single node. @@ -96,7 +88,7 @@ impl<'a> ValidationContext<'a> { /// This includes: /// - Matching the number of ports with the signature /// - Dataflow ports are correct. See `validate_df_port` - fn validate_node(&self, node: Node) -> Result<(), ValidationError> { + fn validate_node(&self, node: H::Node) -> Result<(), ValidationError> { let op_type = self.hugr.get_optype(node); if let OpType::OpaqueOp(opaque) = op_type { @@ -110,7 +102,7 @@ impl<'a> ValidationContext<'a> { for dir in Direction::BOTH { // Check that we have the correct amount of ports and edges. - let num_ports = self.hugr.graph.num_ports(node.into_portgraph(), dir); + let num_ports = self.hugr.num_ports(node, dir); if num_ports != op_type.port_count(dir) { return Err(ValidationError::WrongNumberOfPorts { node, @@ -128,7 +120,7 @@ impl<'a> ValidationContext<'a> { }; let parent_optype = self.hugr.get_optype(parent); - let allowed_children = parent_optype.validity_flags().allowed_children; + let allowed_children = parent_optype.validity_flags::().allowed_children; if !allowed_children.is_superset(op_type.tag()) { return Err(ValidationError::InvalidParentOp { child: node, @@ -157,16 +149,15 @@ impl<'a> ValidationContext<'a> { /// - The linked port must have a compatible type. fn validate_port( &mut self, - node: Node, + node: H::Node, port: Port, - port_index: portgraph::PortIndex, op_type: &OpType, var_decls: &[TypeParam], - ) -> Result<(), ValidationError> { + ) -> Result<(), ValidationError> { let port_kind = op_type.port_kind(port).unwrap(); let dir = port.direction(); - let mut links = self.hugr.graph.port_links(port_index).peekable(); + let mut links = self.hugr.linked_ports(node, port).peekable(); // Linear dataflow values must be used, and control must have somewhere to flow. let outgoing_is_linear = port_kind.is_linear() || port_kind == EdgeKind::ControlFlow; let must_be_connected = match dir { @@ -200,7 +191,7 @@ impl<'a> ValidationContext<'a> { })?; let mut link_cnt = 0; - for (_, link) in links { + for (other_node, other_offset) in links { link_cnt += 1; if outgoing_is_linear && link_cnt > 1 { return Err(ValidationError::TooManyConnections { @@ -210,14 +201,10 @@ impl<'a> ValidationContext<'a> { }); } - let other_node: Node = self.hugr.graph.port_node(link).unwrap().into(); - let other_offset = self.hugr.graph.port_offset(link).unwrap().into(); - let other_op = self.hugr.get_optype(other_node); let Some(other_kind) = other_op.port_kind(other_offset) else { panic!("The number of ports in {other_node} does not match the operation definition. This should have been caught by `validate_node`."); }; - // TODO: We will require some "unifiable" comparison instead of strict equality, to allow for pre-type inference hugrs. if other_kind != port_kind { return Err(ValidationError::IncompatiblePorts { from: node, @@ -253,10 +240,14 @@ impl<'a> ValidationContext<'a> { /// Check operation-specific constraints. /// /// These are flags defined for each operation type as an [`OpValidityFlags`] object. - fn validate_children(&self, node: Node, op_type: &OpType) -> Result<(), ValidationError> { + fn validate_children( + &self, + node: H::Node, + op_type: &OpType, + ) -> Result<(), ValidationError> { let flags = op_type.validity_flags(); - if self.hugr.hierarchy().child_count(node.into_portgraph()) > 0 { + if self.hugr.children(node).count() > 0 { if flags.allowed_children.is_empty() { return Err(ValidationError::NonContainerWithChildren { node, @@ -292,8 +283,7 @@ impl<'a> ValidationContext<'a> { } } // Additional validations running over the full list of children optypes - let children_optypes = - all_children.map(|c| (c.into_portgraph(), self.hugr.get_optype(c))); + let children_optypes = all_children.map(|c| (c, self.hugr.get_optype(c))); if let Err(source) = op_type.validate_op_children(children_optypes) { return Err(ValidationError::InvalidChildren { parent: node, @@ -304,21 +294,20 @@ impl<'a> ValidationContext<'a> { // Additional validations running over the edges of the contained graph if let Some(edge_check) = flags.edge_check { - for source in self.hugr.hierarchy().children(node.into_portgraph()) { - for target in self.hugr.graph.output_neighbours(source) { - if self.hugr.hierarchy.parent(target) != Some(node.into_portgraph()) { + for source in self.hugr.children(node) { + for target in self.hugr.output_neighbours(source) { + if self.hugr.get_parent(target) != Some(node) { continue; } - let source_op = self.hugr.get_optype(source.into()); - let target_op = self.hugr.get_optype(target.into()); - for (source_port, target_port) in - self.hugr.graph.get_connections(source, target) + let source_op = self.hugr.get_optype(source); + let target_op = self.hugr.get_optype(target); + for [source_port, target_port] in self.hugr.node_connections(source, target) { let edge_data = ChildrenEdgeData { source, target, - source_port: self.hugr.graph.port_offset(source_port).unwrap(), - target_port: self.hugr.graph.port_offset(target_port).unwrap(), + source_port, + target_port, source_op: source_op.clone(), target_op: target_op.clone(), }; @@ -351,8 +340,12 @@ impl<'a> ValidationContext<'a> { /// /// Inter-graph edges are ignored. Only internal dataflow, constant, or /// state order edges are considered. - fn validate_children_dag(&self, parent: Node, op_type: &OpType) -> Result<(), ValidationError> { - if !self.hugr.hierarchy.has_children(parent.into_portgraph()) { + fn validate_children_dag( + &self, + parent: H::Node, + op_type: &OpType, + ) -> Result<(), ValidationError> { + if self.hugr.children(parent).next().is_none() { // No children, nothing to do return Ok(()); }; @@ -361,7 +354,7 @@ impl<'a> ValidationContext<'a> { let postorder = Topo::new(®ion); let nodes_visited = postorder .iter(®ion) - .filter(|n| *n != parent.into_portgraph()) + .filter(|n| *n != self.hugr.to_portgraph_node(parent)) .count(); let node_count = self.hugr.children(parent).count(); if nodes_visited != node_count { @@ -383,12 +376,12 @@ impl<'a> ValidationContext<'a> { /// a descendant of a post-dominating sibling of the BasicBlock. fn validate_edge( &mut self, - from: Node, + from: H::Node, from_offset: Port, from_optype: &OpType, - to: Node, + to: H::Node, to_offset: Port, - ) -> Result<(), InterGraphEdgeError> { + ) -> Result<(), InterGraphEdgeError> { let from_parent = self .hugr .get_parent(from) @@ -440,12 +433,8 @@ impl<'a> ValidationContext<'a> { if !is_static { // Must have an order edge. self.hugr - .graph - .get_connections(from.into_portgraph(), ancestor.into_portgraph()) - .find(|&(p, _)| { - let offset = self.hugr.graph.port_offset(p).unwrap(); - from_optype.port_kind(offset) == Some(EdgeKind::StateOrder) - }) + .node_connections(from, ancestor) + .find(|&[p, _]| from_optype.port_kind(p) == Some(EdgeKind::StateOrder)) .ok_or(InterGraphEdgeError::MissingOrderEdge { from, from_offset, @@ -478,8 +467,8 @@ impl<'a> ValidationContext<'a> { } }; if !dominator_tree - .dominators(ancestor.into_portgraph()) - .is_some_and(|mut ds| ds.any(|n| n == from_parent.into_portgraph())) + .dominators(self.hugr.to_portgraph_node(ancestor)) + .is_some_and(|mut ds| ds.any(|n| n == self.hugr.to_portgraph_node(from_parent))) { return Err(InterGraphEdgeError::NonDominatedAncestor { from, @@ -507,13 +496,13 @@ impl<'a> ValidationContext<'a> { /// only refer to type variables declared by the closest enclosing FuncDefn. fn validate_subtree( &mut self, - node: Node, + node: H::Node, var_decls: &[TypeParam], - ) -> Result<(), ValidationError> { + ) -> Result<(), ValidationError> { let op_type = self.hugr.get_optype(node); // The op_type must be defined only in terms of type variables defined outside the node - let validate_ext = |ext_op: &ExtensionOp| -> Result<(), ValidationError> { + let validate_ext = |ext_op: &ExtensionOp| -> Result<(), ValidationError> { // Check TypeArgs are valid, and if we can, fit the declared TypeParams ext_op .def() @@ -557,14 +546,8 @@ impl<'a> ValidationContext<'a> { // Root nodes are ignored, as they cannot have connected edges. if node != self.hugr.entrypoint() { for dir in Direction::BOTH { - for (i, port_index) in self - .hugr - .graph - .ports(node.into_portgraph(), dir) - .enumerate() - { - let port = Port::new(dir, i); - self.validate_port(node, port, port_index, op_type, var_decls)?; + for port in self.hugr.node_ports(node, dir) { + self.validate_port(node, port, op_type, var_decls)?; } } } @@ -589,61 +572,59 @@ impl<'a> ValidationContext<'a> { #[derive(Debug, Clone, PartialEq, Error)] #[allow(missing_docs)] #[non_exhaustive] -pub enum ValidationError { +pub enum ValidationError { /// The root node of the Hugr is not a root in the hierarchy. - #[error("The root node of the Hugr {node} is not a root in the hierarchy.")] - RootNotRoot { node: Node }, + #[error("The root node of the Hugr ({node}) is not a root in the hierarchy.")] + RootNotRoot { node: N }, /// The node ports do not match the operation signature. - #[error("The node {node} has an invalid number of ports. The operation {optype} cannot have {actual} {dir:?} ports. Expected {expected}.")] + #[error("{node} has an invalid number of ports. The operation {optype} cannot have {actual} {dir:?} ports. Expected {expected}.")] WrongNumberOfPorts { - node: Node, + node: N, optype: OpType, actual: usize, expected: usize, dir: Direction, }, /// A dataflow port is not connected. - #[error("The node {node} has an unconnected port {port} of type {port_kind}.")] + #[error("{node} has an unconnected port {port} of type {port_kind}.")] UnconnectedPort { - node: Node, + node: N, port: Port, port_kind: EdgeKind, }, /// A linear port is connected to more than one thing. - #[error( - "The node {node} has a port {port} of type {port_kind} with more than one connection." - )] + #[error("{node} has a port {port} of type {port_kind} with more than one connection.")] TooManyConnections { - node: Node, + node: N, port: Port, port_kind: EdgeKind, }, /// Connected ports have different types, or non-unifiable types. - #[error("Connected ports {from_port} in node {from} and {to_port} in node {to} have incompatible kinds. Cannot connect {from_kind} to {to_kind}.")] + #[error("Connected ports {from_port} in {from} and {to_port} in {to} have incompatible kinds. Cannot connect {from_kind} to {to_kind}.")] IncompatiblePorts { - from: Node, + from: N, from_port: Port, from_kind: EdgeKind, - to: Node, + to: N, to_port: Port, to_kind: EdgeKind, }, /// The non-root node has no parent. - #[error("The node {node} has no parent.")] - NoParent { node: Node }, + #[error("{node} has no parent.")] + NoParent { node: N }, /// The parent node is not compatible with the child node. - #[error("The operation {parent_optype} cannot contain a {child_optype} as a child. Allowed children: {}. In node {child} with parent {parent}.", allowed_children.description())] + #[error("The operation {parent_optype} cannot contain a {child_optype} as a child. Allowed children: {}. In {child} with parent {parent}.", allowed_children.description())] InvalidParentOp { - child: Node, + child: N, child_optype: OpType, - parent: Node, + parent: N, parent_optype: OpType, allowed_children: OpTag, }, /// Invalid first/second child. - #[error("A {optype} operation cannot be the {position} child of a {parent_optype}. Expected {expected}. In parent node {parent}")] + #[error("A {optype} operation cannot be the {position} child of a {parent_optype}. Expected {expected}. In parent {parent}")] InvalidInitialChild { - parent: Node, + parent: N, parent_optype: OpType, optype: OpType, expected: OpTag, @@ -651,13 +632,13 @@ pub enum ValidationError { }, /// The children list has invalid elements. #[error( - "An operation {parent_optype} contains invalid children: {source}. In parent {parent}, child Node({child})", - child=source.child().index(), + "An operation {parent_optype} contains invalid children: {source}. In parent {parent}, child {child}", + child=source.child(), )] InvalidChildren { - parent: Node, + parent: N, parent_optype: OpType, - source: ChildrenValidationError, + source: ChildrenValidationError, }, /// The children graph has invalid edges. #[error( @@ -668,32 +649,34 @@ pub enum ValidationError { to_port=source.edge().target_port, )] InvalidEdges { - parent: Node, + parent: N, parent_optype: OpType, - source: EdgeValidationError, + source: EdgeValidationError, }, /// The node operation is not a container, but has children. - #[error("The node {node} with optype {optype} is not a container, but has children.")] - NonContainerWithChildren { node: Node, optype: OpType }, + #[error("{node} with optype {optype} is not a container, but has children.")] + NonContainerWithChildren { node: N, optype: OpType }, /// The node must have children, but has none. - #[error("The node {node} with optype {optype} must have children, but has none.")] - ContainerWithoutChildren { node: Node, optype: OpType }, + #[error("{node} with optype {optype} must have children, but has none.")] + ContainerWithoutChildren { node: N, optype: OpType }, /// The children of a node do not form a DAG. - #[error("The children of an operation {optype} must form a DAG. Loops are not allowed. In node {node}.")] - NotADag { node: Node, optype: OpType }, + #[error( + "The children of an operation {optype} must form a DAG. Loops are not allowed. In {node}." + )] + NotADag { node: N, optype: OpType }, /// There are invalid inter-graph edges. #[error(transparent)] - InterGraphEdgeError(#[from] InterGraphEdgeError), + InterGraphEdgeError(#[from] InterGraphEdgeError), /// There are errors in the extension deltas. #[error(transparent)] ExtensionError(#[from] ExtensionError), /// A node claims to still be awaiting extension inference. Perhaps it is not acted upon by inference. - #[error("Node {node} needs a concrete ExtensionSet - inference will provide this for Case/CFG/Conditional/DataflowBlock/DFG/TailLoop only")] - ExtensionsNotInferred { node: Node }, + #[error("{node} needs a concrete ExtensionSet - inference will provide this for Case/CFG/Conditional/DataflowBlock/DFG/TailLoop only")] + ExtensionsNotInferred { node: N }, /// Error in a node signature #[error("Error in signature of operation {op} at {node}: {cause}")] SignatureError { - node: Node, + node: N, op: OpName, #[source] cause: SignatureError, @@ -703,7 +686,7 @@ pub enum ValidationError { /// [ExtensionOp]: crate::ops::ExtensionOp /// [Opaque]: crate::ops::OpaqueOp #[error(transparent)] - OpaqueOpError(#[from] OpaqueOpError), + OpaqueOpError(#[from] OpaqueOpError), /// A [Const] contained a [Value] of unexpected [Type]. /// /// [Const]: crate::ops::Const @@ -717,60 +700,60 @@ pub enum ValidationError { #[derive(Debug, Clone, PartialEq, Error)] #[allow(missing_docs)] #[non_exhaustive] -pub enum InterGraphEdgeError { +pub enum InterGraphEdgeError { /// Inter-Graph edges can only carry copyable data. #[error("Inter-graph edges can only carry copyable data. In an inter-graph edge from {from} ({from_offset}) to {to} ({to_offset}) with type {ty}.")] NonCopyableData { - from: Node, + from: N, from_offset: Port, - to: Node, + to: N, to_offset: Port, ty: EdgeKind, }, /// Inter-Graph edges may not enter into FuncDefns unless they are static #[error("Inter-graph Value edges cannot enter into FuncDefns. Inter-graph edge from {from} ({from_offset}) to {to} ({to_offset} enters FuncDefn {func}")] ValueEdgeIntoFunc { - from: Node, + from: N, from_offset: Port, - to: Node, + to: N, to_offset: Port, - func: Node, + func: N, }, /// The grandparent of a dominator inter-graph edge must be a CFG container. #[error("The grandparent of a dominator inter-graph edge must be a CFG container. Found operation {ancestor_parent_op}. In a dominator inter-graph edge from {from} ({from_offset}) to {to} ({to_offset}).")] NonCFGAncestor { - from: Node, + from: N, from_offset: Port, - to: Node, + to: N, to_offset: Port, ancestor_parent_op: OpType, }, /// The sibling ancestors of the external inter-graph edge endpoints must be have an order edge between them. #[error("Missing state order between the external inter-graph source {from} and the ancestor of the target {to_ancestor}. In an external inter-graph edge from {from} ({from_offset}) to {to} ({to_offset}).")] MissingOrderEdge { - from: Node, + from: N, from_offset: Port, - to: Node, + to: N, to_offset: Port, - to_ancestor: Node, + to_ancestor: N, }, /// The ancestors of an inter-graph edge are not related. #[error("The ancestors of an inter-graph edge are not related. In an inter-graph edge from {from} ({from_offset}) to {to} ({to_offset}).")] NoRelation { - from: Node, + from: N, from_offset: Port, - to: Node, + to: N, to_offset: Port, }, /// The basic block containing the source node does not dominate the basic block containing the target node. - #[error(" The basic block containing the source node does not dominate the basic block containing the target node in the CFG. Expected node {from_parent} to dominate {ancestor}. In a dominator inter-graph edge from {from} ({from_offset}) to {to} ({to_offset}).")] + #[error(" The basic block containing the source node does not dominate the basic block containing the target node in the CFG. Expected {from_parent} to dominate {ancestor}. In a dominator inter-graph edge from {from} ({from_offset}) to {to} ({to_offset}).")] NonDominatedAncestor { - from: Node, + from: N, from_offset: Port, - to: Node, + to: N, to_offset: Port, - from_parent: Node, - ancestor: Node, + from_parent: N, + ancestor: N, }, } diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 21d26b1292..d1dd95e6dd 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -25,7 +25,7 @@ use crate::types::{ CustomType, FuncValueType, PolyFuncType, PolyFuncTypeRV, Signature, Type, TypeBound, TypeRV, TypeRow, }; -use crate::{const_extension_ids, test_file, type_row, Direction, IncomingPort, Node}; +use crate::{const_extension_ids, test_file, type_row, Direction, Hugr, IncomingPort, Node}; /// Creates a hugr with a single function definition that copies a bit `copies` times. /// @@ -87,7 +87,7 @@ fn invalid_root() { b.set_parent(root, module); assert_matches!( b.validate(), - Err(ValidationError::RootNotRoot { node }) => assert_eq!(node, root) + Err(ValidationError::NoParent { node }) => assert_eq!(node, module) ); // Fix the root @@ -189,7 +189,7 @@ fn df_children_restrictions() { assert_matches!( b.validate(), Err(ValidationError::InvalidChildren { parent, source: ChildrenValidationError::IOSignatureMismatch { child, .. }, .. }) - => {assert_eq!(parent, def); assert_eq!(child, output.into_portgraph())} + => {assert_eq!(parent, def); assert_eq!(child, output)} ); b.replace_op(output, ops::Output::new(vec![bool_t(), bool_t()])); @@ -198,7 +198,7 @@ fn df_children_restrictions() { assert_matches!( b.validate(), Err(ValidationError::InvalidChildren { parent, source: ChildrenValidationError::InternalIOChildren { child, .. }, .. }) - => {assert_eq!(parent, def); assert_eq!(child, copy.into_portgraph())} + => {assert_eq!(parent, def); assert_eq!(child, copy)} ); } @@ -807,7 +807,7 @@ fn cfg_children_restrictions() { assert_matches!( b.validate(), Err(ValidationError::InvalidChildren { parent, source: ChildrenValidationError::InternalExitChildren { child, .. }, .. }) - => {assert_eq!(parent, cfg); assert_eq!(child, exit2.into_portgraph())} + => {assert_eq!(parent, cfg); assert_eq!(child, exit2)} ); b.remove_node(exit2); diff --git a/hugr-core/src/hugr/views.rs b/hugr-core/src/hugr/views.rs index 7be454f42e..511aa1c99a 100644 --- a/hugr-core/src/hugr/views.rs +++ b/hugr-core/src/hugr/views.rs @@ -24,6 +24,7 @@ use portgraph::render::{DotFormat, MermaidFormat}; use portgraph::{LinkView, PortView}; use super::internal::{HugrInternals, HugrMutInternals}; +use super::validate::ValidationContext; use super::{Hugr, HugrMut, Node, NodeMetadata, ValidationError}; use crate::core::HugrNode; use crate::extension::ExtensionRegistry; @@ -451,9 +452,12 @@ pub trait HugrView: HugrInternals { fn extensions(&self) -> &ExtensionRegistry; /// Check the validity of the underlying HUGR. - fn validate(&self) -> Result<(), ValidationError> { - #[allow(deprecated)] - self.base_hugr().validate() + fn validate(&self) -> Result<(), ValidationError> + where + Self: Sized, + { + let mut validator = ValidationContext::new(self); + validator.validate() } /// Extracts a HUGR containing the parent node and all its descendants. diff --git a/hugr-core/src/hugr/views/impls.rs b/hugr-core/src/hugr/views/impls.rs index adf4c03e65..5d321dbb5a 100644 --- a/hugr-core/src/hugr/views/impls.rs +++ b/hugr-core/src/hugr/views/impls.rs @@ -70,7 +70,7 @@ macro_rules! hugr_view_methods { fn static_targets(&self, node: Self::Node) -> Option>; fn value_types(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator; fn extensions(&self) -> &crate::extension::ExtensionRegistry; - fn validate(&self) -> Result<(), crate::hugr::ValidationError>; + fn validate(&self) -> Result<(), crate::hugr::ValidationError>; fn extract_hugr(&self, parent: Self::Node) -> (crate::Hugr, impl crate::hugr::views::ExtractionResult + 'static); } } diff --git a/hugr-core/src/hugr/views/rerooted.rs b/hugr-core/src/hugr/views/rerooted.rs index 9fde68f991..e76476688c 100644 --- a/hugr-core/src/hugr/views/rerooted.rs +++ b/hugr-core/src/hugr/views/rerooted.rs @@ -114,7 +114,7 @@ impl HugrView for Rerooted { fn static_targets(&self, node: Self::Node) -> Option>; fn value_types(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator; fn extensions(&self) -> &crate::extension::ExtensionRegistry; - fn validate(&self) -> Result<(), crate::hugr::ValidationError>; + fn validate(&self) -> Result<(), crate::hugr::ValidationError>; fn extract_hugr(&self, parent: Self::Node) -> (crate::Hugr, impl crate::hugr::views::ExtractionResult + 'static); } } diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index 39409a180a..d6cf8e75fb 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -18,12 +18,11 @@ use std::borrow::Cow; use crate::extension::simple_op::MakeExtensionOp; use crate::extension::{ExtensionId, ExtensionRegistry}; use crate::types::{EdgeKind, Signature, Substitution}; -use crate::{Direction, OutgoingPort, Port}; +use crate::{Direction, Node, OutgoingPort, Port}; use crate::{IncomingPort, PortIndex}; use derive_more::Display; use handle::NodeHandle; use paste::paste; -use portgraph::NodeIndex; use enum_dispatch::enum_dispatch; @@ -298,7 +297,7 @@ impl OpType { /// Checks whether the operation can contain children nodes. #[inline] pub fn is_container(&self) -> bool { - self.validity_flags().allowed_children != OpTag::None + self.validity_flags::().allowed_children != OpTag::None } /// Cast to an extension operation. @@ -491,16 +490,16 @@ impl OpParent for ExitBlock {} pub trait ValidateOp { /// Returns a set of flags describing the validity predicates for this operation. #[inline] - fn validity_flags(&self) -> validate::OpValidityFlags { + fn validity_flags(&self) -> validate::OpValidityFlags { Default::default() } /// Validate the ordered list of children. #[inline] - fn validate_op_children<'a>( + fn validate_op_children<'a, N: HugrNode>( &self, - _children: impl DoubleEndedIterator, - ) -> Result<(), validate::ChildrenValidationError> { + _children: impl DoubleEndedIterator, + ) -> Result<(), validate::ChildrenValidationError> { Ok(()) } } diff --git a/hugr-core/src/ops/custom.rs b/hugr-core/src/ops/custom.rs index 0e81d07a83..9fffdf6be5 100644 --- a/hugr-core/src/ops/custom.rs +++ b/hugr-core/src/ops/custom.rs @@ -12,10 +12,11 @@ use { ::proptest_derive::Arbitrary, }; +use crate::core::HugrNode; use crate::extension::simple_op::MakeExtensionOp; use crate::extension::{ConstFoldResult, ExtensionId, OpDef, SignatureError}; use crate::types::{type_param::TypeArg, Signature}; -use crate::{ops, IncomingPort, Node}; +use crate::{ops, IncomingPort}; use super::dataflow::DataflowOpTrait; use super::tag::OpTag; @@ -316,14 +317,14 @@ impl DataflowOpTrait for OpaqueOp { /// when trying to resolve the serialized names against a registry of known Extensions. #[derive(Clone, Debug, Error, PartialEq)] #[non_exhaustive] -pub enum OpaqueOpError { +pub enum OpaqueOpError { /// The Extension was found but did not contain the expected OpDef #[error("Operation '{op}' in {node} not found in Extension {extension}. Available operations: {}", available_ops.iter().join(", ") )] OpNotFoundInExtension { /// The node where the error occurred. - node: Node, + node: N, /// The missing operation. op: OpName, /// The extension where the operation was expected. @@ -335,7 +336,7 @@ pub enum OpaqueOpError { #[error("Conflicting signature: resolved {op} in extension {extension} to a concrete implementation which computed {computed} but stored signature was {stored}")] #[allow(missing_docs)] SignatureMismatch { - node: Node, + node: N, extension: ExtensionId, op: OpName, stored: Signature, @@ -345,14 +346,14 @@ pub enum OpaqueOpError { #[error("Error in signature of operation '{name}' in {node}: {cause}")] #[allow(missing_docs)] SignatureError { - node: Node, + node: N, name: OpName, #[source] cause: SignatureError, }, /// Unresolved operation encountered during validation. #[error("Unexpected unresolved opaque operation '{1}' in {0}, from Extension {2}.")] - UnresolvedOp(Node, OpName, ExtensionId), + UnresolvedOp(N, OpName, ExtensionId), /// Error updating the extension registry in the Hugr while resolving opaque ops. #[error("Error updating extension registry: {0}")] ExtensionRegistryError(#[from] crate::extension::ExtensionRegistryError), @@ -367,6 +368,7 @@ mod test { use crate::extension::ExtensionRegistry; use crate::std_extensions::arithmetic::conversions::{self}; use crate::std_extensions::STD_REG; + use crate::Node; use crate::{ extension::{ prelude::{bool_t, qb_t, usize_t}, diff --git a/hugr-core/src/ops/validate.rs b/hugr-core/src/ops/validate.rs index ab8a4c7611..984db04e2c 100644 --- a/hugr-core/src/ops/validate.rs +++ b/hugr-core/src/ops/validate.rs @@ -7,17 +7,23 @@ //! require traversing the children. use itertools::Itertools; -use portgraph::{NodeIndex, PortOffset}; use thiserror::Error; +use crate::core::HugrNode; use crate::types::TypeRow; +use crate::{Port, PortIndex}; use super::dataflow::{DataflowOpTrait, DataflowParent}; use super::{impl_validate_op, BasicBlock, ExitBlock, OpTag, OpTrait, OpType, ValidateOp}; +/// A function that checks the edges between children of a node. +/// +/// Part of the [`OpValidityFlags`] struct. +pub type EdgeCheck = fn(ChildrenEdgeData) -> Result<(), EdgeValidationError>; + /// A set of property flags required for an operation. #[non_exhaustive] -pub struct OpValidityFlags { +pub struct OpValidityFlags { /// The set of valid children operation types. pub allowed_children: OpTag, /// Additional restrictions on the first child operation. @@ -35,10 +41,10 @@ pub struct OpValidityFlags { /// A validation check for edges between children /// // Enclosed in an `Option` to avoid iterating over the edges if not needed. - pub edge_check: Option Result<(), EdgeValidationError>>, + pub edge_check: Option>, } -impl Default for OpValidityFlags { +impl Default for OpValidityFlags { fn default() -> Self { // Defaults to flags valid for non-container operations Self { @@ -53,7 +59,7 @@ impl Default for OpValidityFlags { } impl ValidateOp for super::Module { - fn validity_flags(&self) -> OpValidityFlags { + fn validity_flags(&self) -> OpValidityFlags { OpValidityFlags { allowed_children: OpTag::ModuleOp, requires_children: false, @@ -63,7 +69,7 @@ impl ValidateOp for super::Module { } impl ValidateOp for super::Conditional { - fn validity_flags(&self) -> OpValidityFlags { + fn validity_flags(&self) -> OpValidityFlags { OpValidityFlags { allowed_children: OpTag::Case, requires_children: true, @@ -72,10 +78,10 @@ impl ValidateOp for super::Conditional { } } - fn validate_op_children<'a>( + fn validate_op_children<'a, N: HugrNode>( &self, - children: impl DoubleEndedIterator, - ) -> Result<(), ChildrenValidationError> { + children: impl DoubleEndedIterator, + ) -> Result<(), ChildrenValidationError> { let children = children.collect_vec(); // The first input to the ɣ-node is a value of Sum type, // whose arity matches the number of children of the ɣ-node. @@ -107,7 +113,7 @@ impl ValidateOp for super::Conditional { } impl ValidateOp for super::CFG { - fn validity_flags(&self) -> OpValidityFlags { + fn validity_flags(&self) -> OpValidityFlags { OpValidityFlags { allowed_children: OpTag::ControlFlowChild, allowed_first_child: OpTag::DataflowBlock, @@ -119,10 +125,10 @@ impl ValidateOp for super::CFG { } } - fn validate_op_children<'a>( + fn validate_op_children<'a, N: HugrNode>( &self, - mut children: impl Iterator, - ) -> Result<(), ChildrenValidationError> { + mut children: impl Iterator, + ) -> Result<(), ChildrenValidationError> { let (entry, entry_op) = children.next().unwrap(); let (exit, exit_op) = children.next().unwrap(); let entry_op = entry_op @@ -163,21 +169,21 @@ impl ValidateOp for super::CFG { #[derive(Debug, Clone, PartialEq, Error)] #[allow(missing_docs)] #[non_exhaustive] -pub enum ChildrenValidationError { +pub enum ChildrenValidationError { /// An CFG graph has an exit operation as a non-second child. #[error("Exit basic blocks are only allowed as the second child in a CFG graph")] - InternalExitChildren { child: NodeIndex }, + InternalExitChildren { child: N }, /// An operation only allowed as the first/second child was found as an intermediate child. #[error("A {optype} operation is only allowed as a {expected_position} child")] InternalIOChildren { - child: NodeIndex, + child: N, optype: OpType, expected_position: &'static str, }, /// The signature of the contained dataflow graph does not match the one of the container. #[error("The {node_desc} node of a {container_desc} has a signature of {actual}, which differs from the expected type row {expected}")] IOSignatureMismatch { - child: NodeIndex, + child: N, actual: TypeRow, expected: TypeRow, node_desc: &'static str, @@ -185,20 +191,20 @@ pub enum ChildrenValidationError { }, /// The signature of a child case in a conditional operation does not match the container's signature. #[error("A conditional case has optype {sig}, which differs from the signature of Conditional container", sig=optype.dataflow_signature().unwrap_or_default())] - ConditionalCaseSignature { child: NodeIndex, optype: OpType }, + ConditionalCaseSignature { child: N, optype: OpType }, /// The conditional container's branching value does not match the number of children. #[error("The conditional container's branch Sum input should be a sum with {expected_count} elements, but it had {} elements. Sum rows: {actual_sum_rows:?}", actual_sum_rows.len())] InvalidConditionalSum { - child: NodeIndex, + child: N, expected_count: usize, actual_sum_rows: Vec, }, } -impl ChildrenValidationError { +impl ChildrenValidationError { /// Returns the node index of the child that caused the error. - pub fn child(&self) -> NodeIndex { + pub fn child(&self) -> N { match self { ChildrenValidationError::InternalIOChildren { child, .. } => *child, ChildrenValidationError::InternalExitChildren { child, .. } => *child, @@ -213,21 +219,21 @@ impl ChildrenValidationError { #[derive(Debug, Clone, PartialEq, Error)] #[allow(missing_docs)] #[non_exhaustive] -pub enum EdgeValidationError { +pub enum EdgeValidationError { /// The dataflow signature of two connected basic blocks does not match. #[error("The dataflow signature of two connected basic blocks does not match. The source type was {source_ty} but the target had type {target_types}", source_ty = source_types.clone().unwrap_or_default(), )] CFGEdgeSignatureMismatch { - edge: ChildrenEdgeData, + edge: ChildrenEdgeData, source_types: Option, target_types: TypeRow, }, } -impl EdgeValidationError { +impl EdgeValidationError { /// Returns information on the edge that caused the error. - pub fn edge(&self) -> &ChildrenEdgeData { + pub fn edge(&self) -> &ChildrenEdgeData { match self { EdgeValidationError::CFGEdgeSignatureMismatch { edge, .. } => edge, } @@ -236,24 +242,24 @@ impl EdgeValidationError { /// Auxiliary structure passed as data to [`OpValidityFlags::edge_check`]. #[derive(Debug, Clone, PartialEq)] -pub struct ChildrenEdgeData { +pub struct ChildrenEdgeData { /// Source child. - pub source: NodeIndex, + pub source: N, /// Target child. - pub target: NodeIndex, + pub target: N, /// Operation type of the source child. pub source_op: OpType, /// Operation type of the target child. pub target_op: OpType, /// Source port. - pub source_port: PortOffset, + pub source_port: Port, /// Target port. - pub target_port: PortOffset, + pub target_port: Port, } impl ValidateOp for T { /// Returns the set of allowed parent operation types. - fn validity_flags(&self) -> OpValidityFlags { + fn validity_flags(&self) -> OpValidityFlags { OpValidityFlags { allowed_children: OpTag::DataflowChild, allowed_first_child: OpTag::Input, @@ -265,10 +271,10 @@ impl ValidateOp for T { } /// Validate the ordered list of children. - fn validate_op_children<'a>( + fn validate_op_children<'a, N: HugrNode>( &self, - children: impl DoubleEndedIterator, - ) -> Result<(), ChildrenValidationError> { + children: impl DoubleEndedIterator, + ) -> Result<(), ChildrenValidationError> { let sig = self.inner_signature(); validate_io_nodes(&sig.input, &sig.output, "DataflowParent", children) } @@ -277,12 +283,12 @@ impl ValidateOp for T { /// Checks a that the list of children nodes does not contain Input and Output /// nodes outside of the first and second elements respectively, and that those /// have the correct signature. -fn validate_io_nodes<'a>( +fn validate_io_nodes<'a, N: HugrNode>( expected_input: &TypeRow, expected_output: &TypeRow, container_desc: &'static str, - mut children: impl Iterator, -) -> Result<(), ChildrenValidationError> { + mut children: impl Iterator, +) -> Result<(), ChildrenValidationError> { // Check that the signature matches with the Input and Output rows. let (first, first_optype) = children.next().unwrap(); let (second, second_optype) = children.next().unwrap(); @@ -333,7 +339,7 @@ fn validate_io_nodes<'a>( } /// Validate an edge between two basic blocks in a CFG sibling graph. -fn validate_cfg_edge(edge: ChildrenEdgeData) -> Result<(), EdgeValidationError> { +fn validate_cfg_edge(edge: ChildrenEdgeData) -> Result<(), EdgeValidationError> { let source = &edge .source_op .as_dataflow_block() @@ -361,9 +367,10 @@ fn validate_cfg_edge(edge: ChildrenEdgeData) -> Result<(), EdgeValidationError> #[cfg(test)] mod test { use crate::extension::prelude::{usize_t, Noop}; - use crate::ops; use crate::ops::dataflow::IOTrait; + use crate::{ops, Node, NodeIndex as _}; use cool_asserts::assert_matches; + use portgraph::NodeIndex; use super::*; @@ -412,8 +419,10 @@ mod test { fn make_iter<'a>( children: &'a [(usize, &OpType)], - ) -> impl DoubleEndedIterator { - children.iter().map(|(n, op)| (NodeIndex::new(*n), *op)) + ) -> impl DoubleEndedIterator { + children + .iter() + .map(|(n, op)| (NodeIndex::new(*n).into(), *op)) } } diff --git a/hugr-core/src/package.rs b/hugr-core/src/package.rs index d5d0c45923..ea59454823 100644 --- a/hugr-core/src/package.rs +++ b/hugr-core/src/package.rs @@ -9,7 +9,7 @@ use crate::envelope::{read_envelope, write_envelope, EnvelopeConfig, EnvelopeErr use crate::extension::resolution::ExtensionResolutionError; use crate::extension::{ExtensionId, ExtensionRegistry, PRELUDE_REGISTRY}; use crate::hugr::{ExtensionError, HugrView, ValidationError}; -use crate::{Extension, Hugr}; +use crate::{Extension, Hugr, Node}; #[derive(Debug, Default, Clone, PartialEq, serde::Serialize, serde::Deserialize)] /// Package of module HUGRs. @@ -291,7 +291,7 @@ pub enum PackageValidationError { available: Vec, }, /// Error raised while validating the package hugrs. - Validation(ValidationError), + Validation(ValidationError), } #[cfg(test)] diff --git a/hugr-core/src/std_extensions/ptr.rs b/hugr-core/src/std_extensions/ptr.rs index 9115bef747..707bd067d1 100644 --- a/hugr-core/src/std_extensions/ptr.rs +++ b/hugr-core/src/std_extensions/ptr.rs @@ -225,6 +225,7 @@ pub(crate) mod test { use crate::builder::DFGBuilder; use crate::extension::prelude::bool_t; use crate::ops::ExtensionOp; + use crate::HugrView; use crate::{ builder::{Dataflow, DataflowHugr}, std_extensions::arithmetic::int_types::INT_TYPES, diff --git a/hugr-passes/src/composable.rs b/hugr-passes/src/composable.rs index 887d0bb63c..0ed8c3deda 100644 --- a/hugr-passes/src/composable.rs +++ b/hugr-passes/src/composable.rs @@ -113,17 +113,20 @@ impl E> ComposablePass for ErrMa /// Error from a [ValidatingPass] #[derive(thiserror::Error, Debug)] -pub enum ValidatePassError { +pub enum ValidatePassError +where + N: HugrNode + 'static, +{ #[error("Failed to validate input HUGR: {err}\n{pretty_hugr}")] Input { #[source] - err: ValidationError, + err: ValidationError, pretty_hugr: String, }, #[error("Failed to validate output HUGR: {err}\n{pretty_hugr}")] Output { #[source] - err: ValidationError, + err: ValidationError, pretty_hugr: String, }, #[error(transparent)] @@ -141,17 +144,20 @@ impl ValidatingPass

{ fn validation_impl( &self, - hugr: &impl HugrView, - mk_err: impl FnOnce(ValidationError, String) -> ValidatePassError, - ) -> Result<(), ValidatePassError> { + hugr: &impl HugrView, + mk_err: impl FnOnce(ValidationError, String) -> ValidatePassError, + ) -> Result<(), ValidatePassError> { hugr.validate() .map_err(|err| mk_err(err, hugr.mermaid_string())) } } -impl ComposablePass for ValidatingPass

{ +impl ComposablePass for ValidatingPass

+where + P::Node: 'static, +{ type Node = P::Node; - type Error = ValidatePassError; + type Error = ValidatePassError; type Result = P::Result; fn run(&self, hugr: &mut impl HugrMut) -> Result { @@ -207,7 +213,7 @@ impl< pub(crate) fn validate_if_test( pass: P, hugr: &mut impl HugrMut, -) -> Result> { +) -> Result> { if cfg!(test) { ValidatingPass::new(pass).run(hugr) } else { diff --git a/hugr-passes/src/dead_funcs.rs b/hugr-passes/src/dead_funcs.rs index c1be33f44d..7d28fd2eac 100644 --- a/hugr-passes/src/dead_funcs.rs +++ b/hugr-passes/src/dead_funcs.rs @@ -128,7 +128,7 @@ impl ComposablePass for RemoveDeadFuncsPass { pub fn remove_dead_funcs( h: &mut impl HugrMut, entry_points: impl IntoIterator, -) -> Result<(), ValidatePassError> { +) -> Result<(), ValidatePassError> { validate_if_test( RemoveDeadFuncsPass::default().with_module_entry_points(entry_points), h, diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 66de521fc1..1ab3ce391b 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -34,7 +34,7 @@ use crate::ComposablePass; /// whenever the names of their parents are unique, but this is not guaranteed. pub fn monomorphize( hugr: &mut impl HugrMut, -) -> Result<(), ValidatePassError> { +) -> Result<(), ValidatePassError> { validate_if_test(MonomorphizePass, hugr) }