hugr_core/hugr/rewrite.rs
1//! Rewrite operations on the HUGR - replacement, outlining, etc.
2
3pub mod consts;
4pub mod inline_call;
5pub mod inline_dfg;
6pub mod insert_identity;
7pub mod outline_cfg;
8mod port_types;
9pub mod replace;
10pub mod simple_replace;
11
12use crate::{Hugr, HugrView, Node};
13pub use port_types::{BoundaryPort, HostPort, ReplacementPort};
14pub use simple_replace::{SimpleReplacement, SimpleReplacementError};
15
16use super::HugrMut;
17
18/// An operation that can be applied to mutate a Hugr
19pub trait Rewrite {
20 /// The type of Error with which this Rewrite may fail
21 type Error: std::error::Error;
22 /// The type returned on successful application of the rewrite.
23 type ApplyResult;
24
25 /// If `true`, [self.apply]'s of this rewrite guarantee that they do not mutate the Hugr when they return an Err.
26 /// If `false`, there is no guarantee; the Hugr should be assumed invalid when Err is returned.
27 const UNCHANGED_ON_FAILURE: bool;
28
29 /// Checks whether the rewrite would succeed on the specified Hugr.
30 /// If this call succeeds, [self.apply] should also succeed on the same `h`
31 /// If this calls fails, [self.apply] would fail with the same error.
32 fn verify(&self, h: &impl HugrView<Node = Node>) -> Result<(), Self::Error>;
33
34 /// Mutate the specified Hugr, or fail with an error.
35 /// Returns [`Self::ApplyResult`] if successful.
36 /// If [self.unchanged_on_failure] is true, then `h` must be unchanged if Err is returned.
37 /// See also [self.verify]
38 /// # Panics
39 /// May panic if-and-only-if `h` would have failed [Hugr::validate]; that is,
40 /// implementations may begin with `assert!(h.validate())`, with `debug_assert!(h.validate())`
41 /// being preferred.
42 fn apply(self, h: &mut impl HugrMut) -> Result<Self::ApplyResult, Self::Error>;
43
44 /// Returns a set of nodes referenced by the rewrite. Modifying any of these
45 /// nodes will invalidate it.
46 ///
47 /// Two `impl Rewrite`s can be composed if their invalidation sets are
48 /// disjoint.
49 fn invalidation_set(&self) -> impl Iterator<Item = Node>;
50}
51
52/// Wraps any rewrite into a transaction (i.e. that has no effect upon failure)
53pub struct Transactional<R> {
54 underlying: R,
55}
56
57// Note we might like to constrain R to Rewrite<unchanged_on_failure=false> but this
58// is not yet supported, https://github.com/rust-lang/rust/issues/92827
59impl<R: Rewrite> Rewrite for Transactional<R> {
60 type Error = R::Error;
61 type ApplyResult = R::ApplyResult;
62 const UNCHANGED_ON_FAILURE: bool = true;
63
64 fn verify(&self, h: &impl HugrView<Node = Node>) -> Result<(), Self::Error> {
65 self.underlying.verify(h)
66 }
67
68 fn apply(self, h: &mut impl HugrMut) -> Result<Self::ApplyResult, Self::Error> {
69 if R::UNCHANGED_ON_FAILURE {
70 return self.underlying.apply(h);
71 }
72 // Try to backup just the contents of this HugrMut.
73 let mut backup = Hugr::new(h.root_type().clone());
74 backup.insert_from_view(backup.root(), h);
75 let r = self.underlying.apply(h);
76 if r.is_err() {
77 // Try to restore backup.
78 h.replace_op(h.root(), backup.root_type().clone())
79 .expect("The root replacement should always match the old root type");
80 while let Some(child) = h.first_child(h.root()) {
81 h.remove_node(child);
82 }
83 h.insert_from_view(h.root(), &backup);
84 }
85 r
86 }
87
88 #[inline]
89 fn invalidation_set(&self) -> impl Iterator<Item = Node> {
90 self.underlying.invalidation_set()
91 }
92}