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()
    }
}