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}