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