Skip to content

Commit cc3bd6a

Browse files
authored
Rewrites work on any HugrMut (#282)
Rewrite::verify takes &impl HugrView (not &Hugr); Rewrite::apply takes &mut impl HugrMut (not &mut Hugr). TransactionalRewrite backs up the portion inside the HugrMut and then restores - this feels a little awkward but may be ok, still not tested though.
1 parent 1f17a25 commit cc3bd6a

File tree

4 files changed

+38
-21
lines changed

4 files changed

+38
-21
lines changed

src/hugr/rewrite.rs

+19-9
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
pub mod insert_identity;
44
pub mod outline_cfg;
55
pub mod simple_replace;
6-
use std::mem;
76

8-
use crate::Hugr;
7+
use crate::{Hugr, HugrView};
98
pub use simple_replace::{SimpleReplacement, SimpleReplacementError};
109

10+
use super::HugrMut;
11+
1112
/// An operation that can be applied to mutate a Hugr
1213
pub trait Rewrite {
1314
/// The type of Error with which this Rewrite may fail
@@ -21,7 +22,7 @@ pub trait Rewrite {
2122
/// Checks whether the rewrite would succeed on the specified Hugr.
2223
/// If this call succeeds, [self.apply] should also succeed on the same `h`
2324
/// If this calls fails, [self.apply] would fail with the same error.
24-
fn verify(&self, h: &Hugr) -> Result<(), Self::Error>;
25+
fn verify(&self, h: &impl HugrView) -> Result<(), Self::Error>;
2526

2627
/// Mutate the specified Hugr, or fail with an error.
2728
/// Returns [`Self::ApplyResult`] if successful.
@@ -31,7 +32,7 @@ pub trait Rewrite {
3132
/// May panic if-and-only-if `h` would have failed [Hugr::validate]; that is,
3233
/// implementations may begin with `assert!(h.validate())`, with `debug_assert!(h.validate())`
3334
/// being preferred.
34-
fn apply(self, h: &mut Hugr) -> Result<Self::ApplyResult, Self::Error>;
35+
fn apply(self, h: &mut impl HugrMut) -> Result<Self::ApplyResult, Self::Error>;
3536
}
3637

3738
/// Wraps any rewrite into a transaction (i.e. that has no effect upon failure)
@@ -46,19 +47,28 @@ impl<R: Rewrite> Rewrite for Transactional<R> {
4647
type ApplyResult = R::ApplyResult;
4748
const UNCHANGED_ON_FAILURE: bool = true;
4849

49-
fn verify(&self, h: &Hugr) -> Result<(), Self::Error> {
50+
fn verify(&self, h: &impl HugrView) -> Result<(), Self::Error> {
5051
self.underlying.verify(h)
5152
}
5253

53-
fn apply(self, h: &mut Hugr) -> Result<Self::ApplyResult, Self::Error> {
54+
fn apply(self, h: &mut impl HugrMut) -> Result<Self::ApplyResult, Self::Error> {
5455
if R::UNCHANGED_ON_FAILURE {
5556
return self.underlying.apply(h);
5657
}
57-
let backup = h.clone();
58+
// Try to backup just the contents of this HugrMut.
59+
let mut backup = Hugr::new(h.root_type().clone());
60+
backup.insert_from_view(backup.root(), h).unwrap();
5861
let r = self.underlying.apply(h);
62+
fn first_child(h: &impl HugrView) -> Option<crate::Node> {
63+
h.children(h.root()).next()
64+
}
5965
if r.is_err() {
60-
// drop the old h, it was undefined
61-
let _ = mem::replace(h, backup);
66+
// Try to restore backup.
67+
h.replace_op(h.root(), backup.root_type().clone());
68+
while let Some(child) = first_child(h) {
69+
h.remove_node(child).unwrap();
70+
}
71+
h.insert_from_view(h.root(), &backup).unwrap();
6272
}
6373
r
6474
}

src/hugr/rewrite/insert_identity.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::hugr::{HugrMut, Node};
44
use crate::ops::{LeafOp, OpTag, OpTrait};
55
use crate::types::EdgeKind;
6-
use crate::{Direction, Hugr, HugrView, Port};
6+
use crate::{Direction, HugrView, Port};
77

88
use super::Rewrite;
99

@@ -52,7 +52,7 @@ impl Rewrite for IdentityInsertion {
5252
/// The inserted node.
5353
type ApplyResult = Node;
5454
const UNCHANGED_ON_FAILURE: bool = true;
55-
fn verify(&self, _h: &Hugr) -> Result<(), IdentityInsertionError> {
55+
fn verify(&self, _h: &impl HugrView) -> Result<(), IdentityInsertionError> {
5656
/*
5757
Assumptions:
5858
1. Value kind inputs can only have one connection.
@@ -65,7 +65,7 @@ impl Rewrite for IdentityInsertion {
6565

6666
unimplemented!()
6767
}
68-
fn apply(self, h: &mut Hugr) -> Result<Self::ApplyResult, IdentityInsertionError> {
68+
fn apply(self, h: &mut impl HugrMut) -> Result<Self::ApplyResult, IdentityInsertionError> {
6969
if self.post_port.direction() != Direction::Incoming {
7070
return Err(IdentityInsertionError::PortIsOutput);
7171
}
@@ -78,6 +78,7 @@ impl Rewrite for IdentityInsertion {
7878
let (pre_node, pre_port) = h
7979
.linked_ports(self.post_node, self.post_port)
8080
.exactly_one()
81+
.ok()
8182
.expect("Value kind input can only have one connection.");
8283

8384
h.disconnect(self.post_node, self.post_port).unwrap();

src/hugr/rewrite/outline_cfg.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ use thiserror::Error;
66

77
use crate::builder::{BlockBuilder, Container, Dataflow, SubContainer};
88
use crate::extension::PRELUDE_REGISTRY;
9-
use crate::hugr::hugrmut::sealed::HugrMutInternals;
109
use crate::hugr::rewrite::Rewrite;
1110
use crate::hugr::{HugrMut, HugrView};
1211
use crate::ops;
1312
use crate::ops::{BasicBlock, OpTag, OpTrait, OpType};
14-
use crate::{type_row, Hugr, Node};
13+
use crate::{type_row, Node};
1514

1615
/// Moves part of a Control-flow Sibling Graph into a new CFG-node
1716
/// that is the only child of a new Basic Block in the original CSG.
@@ -27,7 +26,10 @@ impl OutlineCfg {
2726
}
2827
}
2928

30-
fn compute_entry_exit_outside(&self, h: &Hugr) -> Result<(Node, Node, Node), OutlineCfgError> {
29+
fn compute_entry_exit_outside(
30+
&self,
31+
h: &impl HugrView,
32+
) -> Result<(Node, Node, Node), OutlineCfgError> {
3133
let cfg_n = match self
3234
.blocks
3335
.iter()
@@ -86,11 +88,11 @@ impl Rewrite for OutlineCfg {
8688
type ApplyResult = ();
8789

8890
const UNCHANGED_ON_FAILURE: bool = true;
89-
fn verify(&self, h: &Hugr) -> Result<(), OutlineCfgError> {
91+
fn verify(&self, h: &impl HugrView) -> Result<(), OutlineCfgError> {
9092
self.compute_entry_exit_outside(h)?;
9193
Ok(())
9294
}
93-
fn apply(self, h: &mut Hugr) -> Result<(), OutlineCfgError> {
95+
fn apply(self, h: &mut impl HugrMut) -> Result<(), OutlineCfgError> {
9496
let (entry, exit, outside) = self.compute_entry_exit_outside(h)?;
9597
// 1. Compute signature
9698
// These panic()s only happen if the Hugr would not have passed validate()
@@ -128,12 +130,13 @@ impl Rewrite for OutlineCfg {
128130
.children(new_block)
129131
.filter(|n| h.get_optype(*n).tag() == OpTag::Cfg)
130132
.exactly_one()
133+
.ok() // HugrMut::Children is not Debug
131134
.unwrap();
132-
let inner_exit = h.children(cfg_node).exactly_one().unwrap();
135+
let inner_exit = h.children(cfg_node).exactly_one().ok().unwrap();
133136

134137
// 4. Entry edges. Change any edges into entry_block from outside, to target new_block
135138
let preds: Vec<_> = h
136-
.linked_ports(entry, h.node_inputs(entry).exactly_one().unwrap())
139+
.linked_ports(entry, h.node_inputs(entry).exactly_one().ok().unwrap())
137140
.collect();
138141
for (pred, br) in preds {
139142
if !self.blocks.contains(&pred) {
@@ -168,6 +171,7 @@ impl Rewrite for OutlineCfg {
168171
t == outside
169172
})
170173
.exactly_one()
174+
.ok() // NodePorts does not implement Debug
171175
.unwrap();
172176
h.disconnect(exit, exit_port).unwrap();
173177
h.connect(exit, exit_port.index(), inner_exit, 0).unwrap();

src/hugr/rewrite/simple_replace.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ impl Rewrite for SimpleReplacement {
5555

5656
const UNCHANGED_ON_FAILURE: bool = true;
5757

58-
fn verify(&self, _h: &Hugr) -> Result<(), SimpleReplacementError> {
58+
fn verify(&self, _h: &impl HugrView) -> Result<(), SimpleReplacementError> {
5959
unimplemented!()
6060
}
6161

62-
fn apply(self, h: &mut Hugr) -> Result<(), SimpleReplacementError> {
62+
fn apply(self, h: &mut impl HugrMut) -> Result<(), SimpleReplacementError> {
6363
// 1. Check the parent node exists and is a DataflowParent.
6464
if !OpTag::DataflowParent.is_superset(h.get_optype(self.parent).tag()) {
6565
return Err(SimpleReplacementError::InvalidParentNode());
@@ -114,6 +114,7 @@ impl Rewrite for SimpleReplacement {
114114
let (rem_inp_pred_node, rem_inp_pred_port) = h
115115
.linked_ports(*rem_inp_node, *rem_inp_port)
116116
.exactly_one()
117+
.ok() // PortLinks does not implement Debug
117118
.unwrap();
118119
h.disconnect(*rem_inp_node, *rem_inp_port).unwrap();
119120
let new_inp_node = index_map.get(rep_inp_node).unwrap();
@@ -155,6 +156,7 @@ impl Rewrite for SimpleReplacement {
155156
let (rem_inp_pred_node, rem_inp_pred_port) = h
156157
.linked_ports(*rem_inp_node, *rem_inp_port)
157158
.exactly_one()
159+
.ok() // PortLinks does not implement Debug
158160
.unwrap();
159161
h.disconnect(*rem_inp_node, *rem_inp_port).unwrap();
160162
h.disconnect(*rem_out_node, *rem_out_port).unwrap();

0 commit comments

Comments
 (0)