|
1 | 1 | //! HUGR invariant checks.
|
2 | 2 |
|
| 3 | +use std::collections::hash_map::Entry; |
3 | 4 | use std::collections::HashMap;
|
4 | 5 | use std::iter;
|
5 | 6 |
|
@@ -299,6 +300,39 @@ impl<'a, H: HugrView> ValidationContext<'a, H> {
|
299 | 300 | });
|
300 | 301 | }
|
301 | 302 | }
|
| 303 | + |
| 304 | + // Linking info |
| 305 | + let mut node_and_imp_sig = HashMap::new(); |
| 306 | + for c in all_children.clone() { |
| 307 | + let (link_name, imp_sig) = match self.hugr.get_optype(c) { |
| 308 | + OpType::FuncDecl(fd) => (&fd.name, Some(&fd.signature)), |
| 309 | + OpType::FuncDefn(fd) => match fd.link_name.as_ref() { |
| 310 | + Some(ln) => (ln, None), |
| 311 | + None => continue, |
| 312 | + }, |
| 313 | + _ => continue, |
| 314 | + }; |
| 315 | + if node != self.hugr.module_root() { |
| 316 | + return Err(ValidationError::LinkNameNotAtTopLevel { node: c }); |
| 317 | + } |
| 318 | + match node_and_imp_sig.entry(link_name) { |
| 319 | + Entry::Vacant(ve) => { |
| 320 | + ve.insert((c, imp_sig)); |
| 321 | + } |
| 322 | + Entry::Occupied(oe) => { |
| 323 | + // Two nodes. Iff both import the same sig then we're OK |
| 324 | + let (prev_c, prev_imp_sig) = oe.get(); |
| 325 | + if prev_imp_sig != &imp_sig || prev_imp_sig.is_none() { |
| 326 | + // Either they are different (import<->export, or import signature), or both are exports |
| 327 | + return Err(ValidationError::DuplicateExternalNames { |
| 328 | + link_name: link_name.clone(), |
| 329 | + children: [*prev_c, c], |
| 330 | + }); |
| 331 | + }; |
| 332 | + } |
| 333 | + } |
| 334 | + } |
| 335 | + |
302 | 336 | // Additional validations running over the full list of children optypes
|
303 | 337 | let children_optypes = all_children.map(|c| (c, self.hugr.get_optype(c)));
|
304 | 338 | if let Err(source) = op_type.validate_op_children(children_optypes) {
|
@@ -657,6 +691,21 @@ pub enum ValidationError<N: HugrNode> {
|
657 | 691 | parent_optype: OpType,
|
658 | 692 | source: ChildrenValidationError<N>,
|
659 | 693 | },
|
| 694 | + /// Multiple nodes were exported using the same name from a [Module](super::Module) |
| 695 | + #[error("FuncDefn is exported under same name {link_name} as earlier node {:?}", children[0])] |
| 696 | + DuplicateExternalNames { |
| 697 | + /// The link_name of a [FuncDecl] or [FuncDefn](super::FuncDefn) |
| 698 | + link_name: String, |
| 699 | + /// Two nodes node exported under that name |
| 700 | + children: [N; 2], |
| 701 | + }, |
| 702 | + /// A [FuncDecl], or `FuncDefn` with a [link_name](super::FuncDefn::link_name), |
| 703 | + /// was neither root nor child of a [Module] root |
| 704 | + #[error("Node {node} has a link_name but is neither root nor child of Module root")] |
| 705 | + LinkNameNotAtTopLevel { |
| 706 | + // The node exporting/importing the name |
| 707 | + node: N, |
| 708 | + }, |
660 | 709 | /// The children graph has invalid edges.
|
661 | 710 | #[error(
|
662 | 711 | "An operation {parent_optype} contains invalid edges between its children: {source}. In parent {parent}, edge from {from:?} port {from_port:?} to {to:?} port {to_port:?}",
|
|
0 commit comments