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}