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