Skip to content

Commit 5cdd65b

Browse files
authored
fix: panic removing InPorts in MultiPortGraph::set_num_ports (#191)
See test case. The problem is that iterating through the `dropped_ports` reads `self.multiport` using *old* indices, after `self.multiport` was mutated/renumbered by the callback earlier. (Thus, if the first OutPort was a multiport, removing the last InPort can end up reading a false positive.) I've tried to see if I can break it by adding inports too, but in such cases all the ports get moved to a new "block" so there is no problem (hence, a more thorough "fix" to do *all reading* of self.multiport before *any writing* to it, including the reading+writing performed by `self.multiport.swap`, does not appear necessary).
1 parent c9e7483 commit 5cdd65b

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

src/multiportgraph.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -209,18 +209,19 @@ impl PortMut for MultiPortGraph {
209209
let mut dropped_ports = Vec::new();
210210
let rekey_wrapper = |port, op| {
211211
match op {
212-
PortOperation::Removed { old_link } => dropped_ports.push((port, old_link)),
212+
PortOperation::Removed { old_link } => {
213+
if *self.multiport.get(port) {
214+
dropped_ports.push((port, old_link.expect("Multiport node has no link")));
215+
}
216+
}
213217
PortOperation::Moved { new_index } => self.multiport.swap(port, new_index),
214218
}
215219
rekey(port, op);
216220
};
217221
self.graph
218222
.set_num_ports(node, incoming, outgoing, rekey_wrapper);
219223
for (port, old_link) in dropped_ports {
220-
if self.is_multiport(port) {
221-
let link = old_link.expect("Multiport node has no link");
222-
self.remove_copy_node(port, link);
223-
}
224+
self.remove_copy_node(port, old_link);
224225
}
225226
}
226227

@@ -872,4 +873,22 @@ pub(crate) mod test {
872873

873874
Ok(())
874875
}
876+
877+
#[test]
878+
/// <https://github.com/CQCL/portgraph/pull/191>
879+
fn remove_ports() {
880+
let mut g = MultiPortGraph::new();
881+
let n = g.add_node(2, 2);
882+
let i1 = g.add_node(0, 1);
883+
g.link_nodes(i1, 0, n, 0).unwrap();
884+
885+
let [o0, o1] = [0, 1].map(|_| g.add_node(1, 0));
886+
// First Out-Port of n is a multiport
887+
g.link_nodes(n, 0, o0, 0).unwrap();
888+
g.link_nodes(n, 0, o1, 0).unwrap();
889+
g.link_nodes(n, 1, o0, 0).unwrap();
890+
// This line was panicking: the second InPort gets deleted, but used to read the multiport-status
891+
// of what was the second InPort but had become the first OutPort.
892+
g.set_num_ports(n, 1, 2, |_, _| {});
893+
}
875894
}

0 commit comments

Comments
 (0)