From 4a2aefa9663066b8e2d400b4e98c99fdeb7a1e57 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Thu, 2 Oct 2025 11:25:27 +0100 Subject: [PATCH 1/5] Add failing test case. --- hugr-core/src/hugr/serialize/test.rs | 25 +++++++++++++++++++++++++ resources/test/issue-2600.hugr | Bin 0 -> 5427 bytes 2 files changed, 25 insertions(+) create mode 100644 resources/test/issue-2600.hugr diff --git a/hugr-core/src/hugr/serialize/test.rs b/hugr-core/src/hugr/serialize/test.rs index 2c7997468d..79c1f1b3da 100644 --- a/hugr-core/src/hugr/serialize/test.rs +++ b/hugr-core/src/hugr/serialize/test.rs @@ -5,6 +5,7 @@ use crate::builder::{ Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, HugrBuilder, ModuleBuilder, endo_sig, inout_sig, test::closed_dfg_root_hugr, }; +use crate::envelope::{EnvelopeConfig, read_envelope, write_envelope}; use crate::extension::ExtensionRegistry; use crate::extension::prelude::Noop; use crate::extension::prelude::{bool_t, qb_t, usize_t}; @@ -16,15 +17,21 @@ use crate::hugr::validate::ValidationError; use crate::hugr::views::ExtractionResult; use crate::ops::custom::{ExtensionOp, OpaqueOp, OpaqueOpError}; use crate::ops::{self, DFG, Input, Module, Output, Value, dataflow::IOTrait}; +use crate::package::Package; use crate::std_extensions::arithmetic::float_types::float64_type; use crate::std_extensions::arithmetic::int_types::{ConstInt, INT_TYPES}; use crate::std_extensions::logic::LogicOp; +use crate::std_extensions::std_reg; +use crate::test_file; use crate::types::type_param::TypeParam; use crate::types::{ FuncValueType, PolyFuncType, PolyFuncTypeRV, Signature, SumType, Type, TypeArg, TypeBound, TypeRV, }; use crate::{OutgoingPort, Visibility, type_row}; +use std::fs::File; +use std::io::{BufReader, Cursor}; + use std::sync::LazyLock; use itertools::Itertools; @@ -625,6 +632,24 @@ fn std_extensions_valid() { } } +#[test] +// https://github.com/CQCL/hugr/issues/2600 +fn cfg_edge_ordering() { + let pkg: Package = Package::load( + BufReader::new(File::open(test_file!("issue-2600.hugr")).unwrap()), + None, + ) + .unwrap(); + pkg.validate().unwrap(); + + let mut data1: Vec = Vec::new(); + let _ = write_envelope(&mut data1, &pkg, EnvelopeConfig::text()); + + let buff1 = Cursor::new(data1); + let (_, pkg1) = read_envelope(buff1, &std_reg()).unwrap(); + pkg1.validate().unwrap(); +} + mod proptest { use super::check_testing_roundtrip; use super::{NodeSer, SimpleOpDef}; diff --git a/resources/test/issue-2600.hugr b/resources/test/issue-2600.hugr new file mode 100644 index 0000000000000000000000000000000000000000..1f6cb01c10e5251a5c2da2859551f8288b906987 GIT binary patch literal 5427 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 literal 0 HcmV?d00001 From c37f5fc2d903884ee8ae9536c621f34ce5736d43 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Thu, 2 Oct 2025 15:33:43 +0100 Subject: [PATCH 2/5] Preserve offset for CFG edges. --- hugr-core/src/hugr/serialize.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hugr-core/src/hugr/serialize.rs b/hugr-core/src/hugr/serialize.rs index 8df7cf8357..b17991f120 100644 --- a/hugr-core/src/hugr/serialize.rs +++ b/hugr-core/src/hugr/serialize.rs @@ -215,7 +215,8 @@ impl TryFrom<&Hugr> for SerHugrLatest { let op = hugr.get_optype(node); let is_value_port = offset < op.value_port_count(dir); let is_static_input = op.static_port(dir).is_some_and(|p| p.index() == offset); - let offset = (is_value_port || is_static_input).then_some(offset as u32); + let is_cfg_edge = op.is_dataflow_block(); + let offset = (is_value_port || is_static_input || is_cfg_edge).then_some(offset as u32); (node_rekey[&node], offset) }; From 1c3051080882985f4d2fab0b5fd0678908df4ad7 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Thu, 2 Oct 2025 16:14:08 +0100 Subject: [PATCH 3/5] Tell miri to ignore test. --- hugr-core/src/hugr/serialize/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hugr-core/src/hugr/serialize/test.rs b/hugr-core/src/hugr/serialize/test.rs index 79c1f1b3da..12546a8e43 100644 --- a/hugr-core/src/hugr/serialize/test.rs +++ b/hugr-core/src/hugr/serialize/test.rs @@ -633,6 +633,7 @@ fn std_extensions_valid() { } #[test] +#[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri // https://github.com/CQCL/hugr/issues/2600 fn cfg_edge_ordering() { let pkg: Package = Package::load( From 2cfa31f14b2f2e8861eb994a915aaf270a193044 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Thu, 2 Oct 2025 16:24:23 +0100 Subject: [PATCH 4/5] Apply offset for all non-order edges. --- hugr-core/src/hugr/serialize.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/hugr/serialize.rs b/hugr-core/src/hugr/serialize.rs index b17991f120..5ddc9e3ae5 100644 --- a/hugr-core/src/hugr/serialize.rs +++ b/hugr-core/src/hugr/serialize.rs @@ -8,6 +8,7 @@ use thiserror::Error; use crate::core::NodeIndex; use crate::hugr::Hugr; use crate::ops::OpType; +use crate::types::EdgeKind; use crate::{Node, PortIndex}; use portgraph::hierarchy::AttachError; use portgraph::{Direction, LinkError, PortView}; @@ -215,8 +216,9 @@ impl TryFrom<&Hugr> for SerHugrLatest { let op = hugr.get_optype(node); let is_value_port = offset < op.value_port_count(dir); let is_static_input = op.static_port(dir).is_some_and(|p| p.index() == offset); - let is_cfg_edge = op.is_dataflow_block(); - let offset = (is_value_port || is_static_input || is_cfg_edge).then_some(offset as u32); + let other_port_is_not_order = op.other_port_kind(dir) != Some(EdgeKind::StateOrder); + let offset = (is_value_port || is_static_input || other_port_is_not_order) + .then_some(offset as u32); (node_rekey[&node], offset) }; From 87967504b64470d2ff182cb83ee1bf487cd5ada3 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Thu, 2 Oct 2025 16:39:52 +0100 Subject: [PATCH 5/5] Fix comment. --- hugr-py/src/hugr/hugr/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-py/src/hugr/hugr/base.py b/hugr-py/src/hugr/hugr/base.py index bf20c6c634..968714c221 100644 --- a/hugr-py/src/hugr/hugr/base.py +++ b/hugr-py/src/hugr/hugr/base.py @@ -971,7 +971,7 @@ def _serialize_link( def _constrain_offset(self, p: P) -> PortOffset | None: """Constrain an offset to be a valid encoded port offset. - Order edges and control flow edges should be encoded without an offset. + Order edges should be encoded without an offset. """ if p.offset < 0: assert p.offset == -1, "Only order edges are allowed with offset < 0"