From 92d876df673b68728253c80eaaee05cc5d485b43 Mon Sep 17 00:00:00 2001 From: Luca Mondada <72734770+lmondada@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:04:47 +0200 Subject: [PATCH 01/22] feat: Add serial data types for SimpleReplacement and PersistentHugr (#2300) This PR provides serde serialization/deserialization support for `SimpleReplacement` as well as the main types in the `PersistentHugr` implementation as discussed in #2253. This can also be used as a template for other types that require ser/deser implementations in the future. Closes https://github.com/CQCL/hugr/issues/2253 --- Cargo.lock | 5 +- Cargo.toml | 2 +- hugr-core/Cargo.toml | 2 +- hugr-core/src/hugr/patch/simple_replace.rs | 2 + .../src/hugr/patch/simple_replace/serial.rs | 116 ++++++++++++++ hugr-core/src/hugr/persistent.rs | 7 + hugr-core/src/hugr/persistent/resolver.rs | 2 +- hugr-core/src/hugr/persistent/state_space.rs | 6 +- .../src/hugr/persistent/state_space/serial.rs | 144 ++++++++++++++++++ hugr-core/src/hugr/views/sibling_subgraph.rs | 2 +- 10 files changed, 281 insertions(+), 7 deletions(-) create mode 100644 hugr-core/src/hugr/patch/simple_replace/serial.rs create mode 100644 hugr-core/src/hugr/persistent/state_space/serial.rs diff --git a/Cargo.lock b/Cargo.lock index af96de9386..7d41cabbff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2527,15 +2527,16 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "relrc" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "036a8b094257e8a5bae0f9978044f2593619afd53476d534cfe1f31a5198ebeb" +checksum = "65b9e2100a2ee7d9efb575064b2f9a552d7f9f60289d7b95b9c22175101c7c4a" dependencies = [ "derive-where", "derive_more 0.99.20", "fxhash", "itertools 0.13.0", "petgraph 0.8.1", + "serde", "thiserror 1.0.69", ] diff --git a/Cargo.toml b/Cargo.toml index 68f3e8eee5..6c5dfb789f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ pest_derive = "2.8.0" pretty = "0.12.4" pretty_assertions = "1.4.1" zstd = "0.13.2" -relrc = "0.4.1" +relrc = "0.4.6" # These public dependencies usually require breaking changes downstream, so we # try to be as permissive as possible. diff --git a/hugr-core/Cargo.toml b/hugr-core/Cargo.toml index c78e1d9dd2..3e6279fbd7 100644 --- a/hugr-core/Cargo.toml +++ b/hugr-core/Cargo.toml @@ -63,7 +63,7 @@ thiserror = { workspace = true } typetag = { workspace = true } semver = { workspace = true, features = ["serde"] } zstd = { workspace = true, optional = true } -relrc = { workspace = true, features = ["petgraph"] } +relrc = { workspace = true, features = ["petgraph", "serde"] } [dev-dependencies] rstest = { workspace = true } diff --git a/hugr-core/src/hugr/patch/simple_replace.rs b/hugr-core/src/hugr/patch/simple_replace.rs index ee47e95f8b..c2111e849d 100644 --- a/hugr-core/src/hugr/patch/simple_replace.rs +++ b/hugr-core/src/hugr/patch/simple_replace.rs @@ -17,6 +17,8 @@ use thiserror::Error; use super::inline_dfg::InlineDFGError; use super::{BoundaryPort, HostPort, PatchHugrMut, PatchVerification, ReplacementPort}; +pub mod serial; + /// Specification of a simple replacement operation. /// /// # Type parameters diff --git a/hugr-core/src/hugr/patch/simple_replace/serial.rs b/hugr-core/src/hugr/patch/simple_replace/serial.rs new file mode 100644 index 0000000000..387c012d21 --- /dev/null +++ b/hugr-core/src/hugr/patch/simple_replace/serial.rs @@ -0,0 +1,116 @@ +//! Serialisation of [`SimpleReplacement`] + +use super::*; + +/// Serialized format for [`SimpleReplacement`] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct SerialSimpleReplacement { + /// The subgraph to be replaced + pub subgraph: SiblingSubgraph, + /// The replacement Hugr + pub replacement: H, +} + +impl SimpleReplacement { + /// Create a new [`SimpleReplacement`] from its serialized format + pub fn from_serial>(value: SerialSimpleReplacement) -> Self { + let SerialSimpleReplacement { + subgraph, + replacement, + } = value; + SimpleReplacement { + subgraph, + replacement: replacement.into(), + } + } + + /// Convert a [`SimpleReplacement`] into its serialized format + pub fn into_serial>(self) -> SerialSimpleReplacement { + let SimpleReplacement { + subgraph, + replacement, + } = self; + SerialSimpleReplacement { + subgraph, + replacement: replacement.into(), + } + } + + /// Create its serialized format from a reference to [`SimpleReplacement`] + pub fn to_serial<'a, H>(&'a self) -> SerialSimpleReplacement + where + N: Clone, + H: From<&'a Hugr>, + { + let SimpleReplacement { + subgraph, + replacement, + } = self; + SerialSimpleReplacement { + subgraph: subgraph.clone(), + replacement: replacement.into(), + } + } +} + +impl> From> for SerialSimpleReplacement { + fn from(value: SimpleReplacement) -> Self { + value.into_serial() + } +} + +impl, N> From> for SimpleReplacement { + fn from(value: SerialSimpleReplacement) -> Self { + SimpleReplacement::from_serial(value) + } +} + +#[cfg(test)] +mod test { + use super::super::test::*; + use super::*; + use crate::{envelope::serde_with::AsStringEnvelope, utils::test_quantum_extension::cx_gate}; + + use derive_more::derive::{From, Into}; + use rstest::rstest; + use serde_with::serde_as; + + #[serde_as] + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, From, Into)] + struct WrappedHugr { + #[serde_as(as = "AsStringEnvelope")] + pub hugr: Hugr, + } + + impl<'h> From<&'h Hugr> for WrappedHugr { + fn from(value: &'h Hugr) -> Self { + WrappedHugr { + hugr: value.clone(), + } + } + } + + #[rstest] + fn test_serial(simple_hugr: Hugr, dfg_hugr: Hugr) { + let h: Hugr = simple_hugr; + // 1. Locate the CX and its successor H's in h + let h_node_cx: Node = h + .entry_descendants() + .find(|node: &Node| *h.get_optype(*node) == cx_gate().into()) + .unwrap(); + let (h_node_h0, h_node_h1) = h.output_neighbours(h_node_cx).collect_tuple().unwrap(); + let s: Vec = vec![h_node_cx, h_node_h0, h_node_h1].into_iter().collect(); + // 2. Construct a new DFG-rooted hugr for the replacement + let replacement = dfg_hugr; + // 4. Define the replacement + let r = SimpleReplacement { + subgraph: SiblingSubgraph::try_from_nodes(s, &h).unwrap(), + replacement, + }; + + let other_repl_serial = r.to_serial::(); + let repl_serial = r.into_serial::(); + + assert_eq!(repl_serial, other_repl_serial); + } +} diff --git a/hugr-core/src/hugr/persistent.rs b/hugr-core/src/hugr/persistent.rs index 3436523e1f..94061300fe 100644 --- a/hugr-core/src/hugr/persistent.rs +++ b/hugr-core/src/hugr/persistent.rs @@ -760,5 +760,12 @@ fn find_conflicting_node<'a>( }) } +pub mod serial { + //! Serialization formats of [`CommitStateSpace`](super::CommitStateSpace) + //! and related types + #[doc(inline)] + pub use super::state_space::serial::*; +} + #[cfg(test)] mod tests; diff --git a/hugr-core/src/hugr/persistent/resolver.rs b/hugr-core/src/hugr/persistent/resolver.rs index 2390a51f20..0a0d140ee5 100644 --- a/hugr-core/src/hugr/persistent/resolver.rs +++ b/hugr-core/src/hugr/persistent/resolver.rs @@ -7,7 +7,7 @@ use relrc::EquivalenceResolver; /// /// This is a trivial resolver (to be expanded on later), that considers two /// patches equivalent if they point to the same data in memory. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct PointerEqResolver; impl EquivalenceResolver for PointerEqResolver { diff --git a/hugr-core/src/hugr/persistent/state_space.rs b/hugr-core/src/hugr/persistent/state_space.rs index 55c54ff8c8..d710c1aaec 100644 --- a/hugr-core/src/hugr/persistent/state_space.rs +++ b/hugr-core/src/hugr/persistent/state_space.rs @@ -16,11 +16,15 @@ use crate::{ ops::OpType, }; +pub mod serial; + /// A copyable handle to a [`Commit`] vertex within a [`CommitStateSpace`] pub type CommitId = relrc::NodeId; /// A HUGR node within a commit of the commit state space -#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] +#[derive( + Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash, serde::Serialize, serde::Deserialize, +)] pub struct PatchNode(pub CommitId, pub Node); impl std::fmt::Display for PatchNode { diff --git a/hugr-core/src/hugr/persistent/state_space/serial.rs b/hugr-core/src/hugr/persistent/state_space/serial.rs new file mode 100644 index 0000000000..c345308b8b --- /dev/null +++ b/hugr-core/src/hugr/persistent/state_space/serial.rs @@ -0,0 +1,144 @@ +use relrc::serialization::SerializedHistoryGraph; + +use super::*; +use crate::hugr::patch::simple_replace::serial::SerialSimpleReplacement; + +/// Serialized format for [`PersistentReplacement`] +pub type SerialPersistentReplacement = SerialSimpleReplacement; + +/// Serialized format for CommitData +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum SerialCommitData { + /// Base commit containing a Hugr + Base(H), + /// Replacement commit containing a serialized replacement + Replacement(SerialPersistentReplacement), +} + +impl CommitData { + /// Create a new [`CommitData`]` from its serialized format + pub fn from_serial>(value: SerialCommitData) -> Self { + match value { + SerialCommitData::Base(h) => CommitData::Base(h.into()), + SerialCommitData::Replacement(replacement) => { + CommitData::Replacement(replacement.into()) + } + } + } + + /// Convert this [`CommitData`] into its serialized format + pub fn into_serial>(self) -> SerialCommitData { + match self { + CommitData::Base(h) => SerialCommitData::Base(h.into()), + CommitData::Replacement(replacement) => { + SerialCommitData::Replacement(replacement.into_serial()) + } + } + } +} + +impl> From for SerialCommitData { + fn from(value: CommitData) -> Self { + value.into_serial() + } +} + +impl> From> for CommitData { + fn from(value: SerialCommitData) -> Self { + CommitData::from_serial(value) + } +} + +/// Serialized format for commit state space +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct SerialCommitStateSpace { + /// The serialized history graph containing commit data + pub graph: SerializedHistoryGraph, (), PointerEqResolver>, + /// The base commit ID + pub base_commit: CommitId, +} + +impl CommitStateSpace { + /// Create a new [`CommitStateSpace`] from its serialized format + pub fn from_serial + Clone>(value: SerialCommitStateSpace) -> Self { + let SerialCommitStateSpace { graph, base_commit } = value; + + // Deserialize the SerializedHistoryGraph into a HistoryGraph with CommitData + let graph = graph.map_nodes(|n| CommitData::from_serial(n)); + let graph = HistoryGraph::try_from_serialized(graph, PointerEqResolver) + .expect("failed to deserialize history graph"); + + Self { graph, base_commit } + } + + /// Convert a [`CommitStateSpace`] into its serialized format + pub fn into_serial>(self) -> SerialCommitStateSpace { + let Self { graph, base_commit } = self; + let graph = graph.to_serialized(); + let graph = graph.map_nodes(|n| n.into_serial()); + SerialCommitStateSpace { graph, base_commit } + } + + /// Create a serialized format from a reference to [`CommitStateSpace`] + pub fn to_serial(&self) -> SerialCommitStateSpace + where + H: From, + { + let Self { graph, base_commit } = self; + let graph = graph.to_serialized(); + let graph = graph.map_nodes(|n| n.into_serial()); + SerialCommitStateSpace { + graph, + base_commit: *base_commit, + } + } +} + +impl> From for SerialCommitStateSpace { + fn from(value: CommitStateSpace) -> Self { + value.into_serial() + } +} + +impl> From> for CommitStateSpace { + fn from(value: SerialCommitStateSpace) -> Self { + CommitStateSpace::from_serial(value) + } +} + +#[cfg(test)] +mod tests { + use derive_more::derive::Into; + use rstest::rstest; + use serde_with::serde_as; + + use super::*; + use crate::{ + envelope::serde_with::AsStringEnvelope, hugr::persistent::tests::test_state_space, + }; + + #[serde_as] + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, From, Into)] + struct WrappedHugr { + #[serde_as(as = "AsStringEnvelope")] + pub hugr: Hugr, + } + + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + #[rstest] + fn test_serialize_state_space(test_state_space: (CommitStateSpace, [CommitId; 4])) { + let (state_space, _) = test_state_space; + let serialized = state_space.to_serial::(); + + let deser = CommitStateSpace::from_serial(serialized); + let _serialized_2 = deser.to_serial::(); + + // TODO: add this once PointerEqResolver is replaced by a deterministic resolver + // insta::assert_snapshot!(serde_json::to_string_pretty(&serialized).unwrap()); + // see https://github.com/CQCL/hugr/issues/2299 + // assert_eq!( + // serde_json::to_string(&serialized), + // serde_json::to_string(&serialized_2) + // ); + } +} diff --git a/hugr-core/src/hugr/views/sibling_subgraph.rs b/hugr-core/src/hugr/views/sibling_subgraph.rs index f31265884f..03dff67f51 100644 --- a/hugr-core/src/hugr/views/sibling_subgraph.rs +++ b/hugr-core/src/hugr/views/sibling_subgraph.rs @@ -50,7 +50,7 @@ use super::root_checked::RootCheckable; /// The `boundary_port` and `signature` methods will panic if any are found. /// State order edges are also unsupported in replacements in /// `create_simple_replacement`. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct SiblingSubgraph { /// The nodes of the induced subgraph. nodes: Vec, From c292185be0294d0faaccb514e95cd9696be1c266 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:13:37 +0100 Subject: [PATCH 02/22] chore(deps-rs): bump the patch group across 1 directory with 2 updates (#2306) Bumps the patch group with 2 updates in the / directory: [clap](https://github.com/clap-rs/clap) and [clap-verbosity-flag](https://github.com/clap-rs/clap-verbosity-flag). Updates `clap` from 4.5.38 to 4.5.39
Release notes

Sourced from clap's releases.

v4.5.39

[4.5.39] - 2025-05-27

Fixes

  • (help) Show short flag aliases before long
  • (help) Merge the short and long flag alias lists
Changelog

Sourced from clap's changelog.

[4.5.39] - 2025-05-27

Fixes

  • (help) Show short flag aliases before long
  • (help) Merge the short and long flag alias lists
Commits

Updates `clap-verbosity-flag` from 3.0.2 to 3.0.3
Changelog

Sourced from clap-verbosity-flag's changelog.

[3.0.3] - 2025-05-20

Commits
  • 7ac7bff chore: Release clap-verbosity-flag version 3.0.3
  • 496f3ed docs(readme): Switch to console code blocks
  • 6ca4f7a Merge pull request #143 from joshka/jm/docs-tracing
  • 3523546 chore(deps): Update Rust Stable to v1.87 (#144)
  • cda6017 docs: Document tracing support in README and lib.rs
  • d97ade1 chore(deps): Update Rust Stable to v1.86 (#141)
  • a58fde5 chore(deps): Update Rust crate clap to v4.5.31 (#140)
  • d5e6463 chore(deps): Update Rust Stable to v1.85 (#139)
  • e815115 chore(deps): Update Rust Stable to v1.84 (#137)
  • 6b0a9d4 Merge pull request #138 from epage/template
  • Additional commits viewable in compare view

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ Cargo.toml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d41cabbff..b230a0cd9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -440,9 +440,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", "clap_derive", @@ -450,9 +450,9 @@ dependencies = [ [[package]] name = "clap-verbosity-flag" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678fade3b77aa3a8ff3aae87e9c008d3fb00473a41c71fbf74e91c8c7b37e84" +checksum = "eeab6a5cdfc795a05538422012f20a5496f050223c91be4e5420bfd13c641fb1" dependencies = [ "clap", "log", @@ -460,9 +460,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstream", "anstyle", diff --git a/Cargo.toml b/Cargo.toml index 6c5dfb789f..6ac053a654 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,9 +73,9 @@ strum = "0.27.0" tempfile = "3.20" thiserror = "2.0.12" typetag = "0.2.20" -clap = { version = "4.5.38" } +clap = { version = "4.5.39" } clio = "0.3.5" -clap-verbosity-flag = "3.0.1" +clap-verbosity-flag = "3.0.3" assert_cmd = "2.0.17" assert_fs = "1.1.3" predicates = "3.1.0" From 71ee7b73a882f26b3b3ff14cb2b1093cebc47dac Mon Sep 17 00:00:00 2001 From: Luca Mondada <72734770+lmondada@users.noreply.github.com> Date: Mon, 9 Jun 2025 13:22:54 +0200 Subject: [PATCH 03/22] feat: Add MermaidFormatter to replace RenderConfig (#2275) To move `PersistentHugr` out of `hugr-core`, I need to be able to provide custom node labels to the mermaid printer. Whilst adding a field for this to `RenderConfig` would not be breaking, the problem is that such a field would have to be an `Option`, which is non-`Copy` and non-`Hash`. That would be a breaking change to RenderConfig. I have found a creative way around this here, keeping the Copy-able `RenderConfig` and adding a `FullRenderConfig` struct with support for `From` and `TryInto`. Is this the preferred approach? How would I make a note that this ought to be simplified ahead of the next breaking release? --- hugr-core/src/hugr/persistent/parents_view.rs | 5 +- hugr-core/src/hugr/persistent/trait_impls.rs | 95 +++--- hugr-core/src/hugr/views.rs | 77 +++-- hugr-core/src/hugr/views/impls.rs | 2 + hugr-core/src/hugr/views/render.rs | 307 ++++++++++++++++-- hugr-core/src/hugr/views/rerooted.rs | 24 +- ...ws__render__tests__custom_node_labels.snap | 22 ++ 7 files changed, 430 insertions(+), 102 deletions(-) create mode 100644 hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap diff --git a/hugr-core/src/hugr/persistent/parents_view.rs b/hugr-core/src/hugr/persistent/parents_view.rs index 639acefccc..b4aa076060 100644 --- a/hugr-core/src/hugr/persistent/parents_view.rs +++ b/hugr-core/src/hugr/persistent/parents_view.rs @@ -5,7 +5,7 @@ use crate::{ extension::ExtensionRegistry, hugr::{ internal::HugrInternals, - views::{ExtractionResult, render::RenderConfig}, + views::{ExtractionResult, render}, }, ops::OpType, }; @@ -196,7 +196,8 @@ impl HugrView for ParentsView<'_> { unimplemented!() } - fn mermaid_string_with_config(&self, _config: RenderConfig) -> String { + #[allow(deprecated)] + fn mermaid_string_with_config(&self, _config: render::RenderConfig) -> String { unimplemented!() } diff --git a/hugr-core/src/hugr/persistent/trait_impls.rs b/hugr-core/src/hugr/persistent/trait_impls.rs index c11949db9a..6c68762029 100644 --- a/hugr-core/src/hugr/persistent/trait_impls.rs +++ b/hugr-core/src/hugr/persistent/trait_impls.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use itertools::{Either, Itertools}; -use portgraph::render::{DotFormat, MermaidFormat}; +use portgraph::render::MermaidFormat; use crate::{ Direction, Hugr, HugrView, Node, Port, @@ -10,7 +10,7 @@ use crate::{ internal::HugrInternals, views::{ ExtractionResult, - render::{self, RenderConfig}, + render::{self, MermaidFormatter, NodeLabel}, }, }, }; @@ -242,35 +242,49 @@ impl HugrView for PersistentHugr { .flat_map(move |port| self.linked_ports(node, port).map(|(opp_node, _)| opp_node)) } - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + #[allow(deprecated)] + fn mermaid_string_with_config(&self, config: render::RenderConfig) -> String { + self.mermaid_string_with_formatter(MermaidFormatter::from_render_config(config, self)) } - fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { // Extract a concrete HUGR for displaying let (hugr, node_map) = self.apply_all(); - // Map config accordingly - let config = RenderConfig { - entrypoint: config.entrypoint.map(|n| node_map[&n]), - node_indices: config.node_indices, - port_offsets_in_edges: config.port_offsets_in_edges, - type_labels_in_edges: config.type_labels_in_edges, - }; - // Render the extracted HUGR but map the node indices back to the // original patch node IDs - let inv_node_map: HashMap<_, _> = node_map.into_iter().map(|(k, v)| (v, k)).collect(); - let fmt_node_index = |n: portgraph::NodeIndex| format!("{:?}", inv_node_map[&n.into()]); + let entrypoint = formatter.entrypoint().map(|n| node_map[&n]); + let node_labels = match formatter.node_labels() { + NodeLabel::None => NodeLabel::None, + NodeLabel::Numeric => { + // replace node labels with patch node IDs + let node_labels_map: HashMap<_, _> = node_map + .into_iter() + .map(|(k, v)| (v, format!("{:?}", k))) + .collect(); + NodeLabel::Custom(node_labels_map) + } + NodeLabel::Custom(labels) => { + // rekey labels to the extracted HUGR node IDs + let labels = labels + .iter() + .map(|(k, v)| (node_map[k], v.clone())) + .collect(); + NodeLabel::Custom(labels) + } + }; + + // Map config accordingly + let config = MermaidFormatter::new(&hugr) + .with_entrypoint(entrypoint) + .with_node_labels(node_labels) + .with_port_offsets(formatter.port_offsets()) + .with_type_labels(formatter.type_labels()); + hugr.graph .mermaid_format() .with_hierarchy(&hugr.hierarchy) - .with_node_style(render::node_style(&hugr, config, fmt_node_index)) + .with_node_style(render::node_style(&hugr, config.clone())) .with_edge_style(render::edge_style(&hugr, config)) .finish() } @@ -279,26 +293,7 @@ impl HugrView for PersistentHugr { where Self: Sized, { - // Extract a concrete HUGR for displaying - let (hugr, node_map) = self.apply_all(); - - // Map config accordingly - let config = RenderConfig { - entrypoint: Some(node_map[&self.entrypoint()]), - ..RenderConfig::default() - }; - - // Render the extracted HUGR but map the node indices back to the - // original patch node IDs - let inv_node_map: HashMap<_, _> = node_map.into_iter().map(|(k, v)| (v, k)).collect(); - let fmt_node_index = |n: portgraph::NodeIndex| format!("{:?}", inv_node_map[&n.into()]); - hugr.graph - .dot_format() - .with_hierarchy(&hugr.hierarchy) - .with_node_style(render::node_style(&hugr, config, fmt_node_index)) - .with_port_style(render::port_style(&hugr, config)) - .with_edge_style(render::edge_style(&hugr, config)) - .finish() + unimplemented!("use mermaid_string instead") } fn extensions(&self) -> &crate::extension::ExtensionRegistry { @@ -351,19 +346,15 @@ mod tests { .try_extract_hugr([commit1, commit2, commit4]) .unwrap(); - let mermaid_str = hugr.mermaid_string_with_config(RenderConfig { - node_indices: false, - entrypoint: Some(hugr.entrypoint()), - ..Default::default() - }); + let mermaid_str = hugr + .mermaid_format() + .with_node_labels(NodeLabel::None) + .finish(); let extracted_hugr = hugr.to_hugr(); let exp_str = extracted_hugr - .mermaid_string_with_config(RenderConfig { - node_indices: false, - entrypoint: Some(extracted_hugr.entrypoint()), - ..Default::default() - }) - .to_string(); + .mermaid_format() + .with_node_labels(NodeLabel::None) + .finish(); assert_eq!(mermaid_str, exp_str); } diff --git a/hugr-core/src/hugr/views.rs b/hugr-core/src/hugr/views.rs index e86b024eb3..1704d79f65 100644 --- a/hugr-core/src/hugr/views.rs +++ b/hugr-core/src/hugr/views.rs @@ -14,7 +14,8 @@ use std::borrow::Cow; use std::collections::HashMap; pub use self::petgraph::PetgraphWrapper; -use self::render::RenderConfig; +#[allow(deprecated)] +use self::render::{MermaidFormatter, RenderConfig}; pub use rerooted::Rerooted; pub use root_checked::{InvalidSignature, RootCheckable, RootChecked, check_tag}; pub use sibling_subgraph::SiblingSubgraph; @@ -385,7 +386,9 @@ pub trait HugrView: HugrInternals { /// /// For a more detailed representation, use the [`HugrView::dot_string`] /// format instead. - fn mermaid_string(&self) -> String; + fn mermaid_string(&self) -> String { + self.mermaid_string_with_formatter(self.mermaid_format()) + } /// Return the mermaid representation of the underlying hierarchical graph. /// @@ -394,8 +397,51 @@ pub trait HugrView: HugrInternals { /// /// For a more detailed representation, use the [`HugrView::dot_string`] /// format instead. + #[deprecated(note = "Use `mermaid_format` instead")] + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: RenderConfig) -> String; + /// Return the mermaid representation of the underlying hierarchical graph + /// according to the provided [`MermaidFormatter`] formatting options. + /// + /// The hierarchy is represented using subgraphs. Edges are labelled with + /// their source and target ports. + /// + /// For a more detailed representation, use the [`HugrView::dot_string`] + /// format instead. + /// + /// ## Deprecation of [`RenderConfig`] + /// While the deprecated [HugrView::mermaid_string_with_config] exists, this + /// will by default try to convert the formatter options to a [`RenderConfig`], + /// but this may panic if the configuration is not supported. Users are + /// encouraged to provide an implementation of this method overriding the default + /// and no longer rely on [HugrView::mermaid_string_with_config]. + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { + #[allow(deprecated)] + let config = match RenderConfig::try_from(formatter) { + Ok(config) => config, + Err(e) => { + panic!("Unsupported format option: {}", e); + } + }; + #[allow(deprecated)] + self.mermaid_string_with_config(config) + } + + /// Construct a mermaid representation of the underlying hierarchical graph. + /// + /// Options can be set on the returned [`MermaidFormatter`] struct, before + /// generating the String with [`MermaidFormatter::finish`]. + /// + /// The hierarchy is represented using subgraphs. Edges are labelled with + /// their source and target ports. + /// + /// For a more detailed representation, use the [`HugrView::dot_string`] + /// format instead. + fn mermaid_format(&self) -> MermaidFormatter { + MermaidFormatter::new(self).with_entrypoint(self.entrypoint()) + } + /// Return the graphviz representation of the underlying graph and hierarchy side by side. /// /// For a simpler representation, use the [`HugrView::mermaid_string`] format instead. @@ -629,21 +675,17 @@ impl HugrView for Hugr { self.graph.all_neighbours(node.into_portgraph()).map_into() } - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + #[allow(deprecated)] + fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + self.mermaid_string_with_formatter(MermaidFormatter::from_render_config(config, self)) } - fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { self.graph .mermaid_format() .with_hierarchy(&self.hierarchy) - .with_node_style(render::node_style(self, config, |n| n.index().to_string())) - .with_edge_style(render::edge_style(self, config)) + .with_node_style(render::node_style(self, formatter.clone())) + .with_edge_style(render::edge_style(self, formatter)) .finish() } @@ -651,16 +693,13 @@ impl HugrView for Hugr { where Self: Sized, { - let config = RenderConfig { - entrypoint: Some(self.entrypoint()), - ..RenderConfig::default() - }; + let formatter = MermaidFormatter::new(self).with_entrypoint(self.entrypoint()); self.graph .dot_format() .with_hierarchy(&self.hierarchy) - .with_node_style(render::node_style(self, config, |n| n.index().to_string())) - .with_port_style(render::port_style(self, config)) - .with_edge_style(render::edge_style(self, config)) + .with_node_style(render::node_style(self, formatter.clone())) + .with_port_style(render::port_style(self)) + .with_edge_style(render::edge_style(self, formatter)) .finish() } diff --git a/hugr-core/src/hugr/views/impls.rs b/hugr-core/src/hugr/views/impls.rs index 09c4475be8..75311f2f73 100644 --- a/hugr-core/src/hugr/views/impls.rs +++ b/hugr-core/src/hugr/views/impls.rs @@ -58,7 +58,9 @@ macro_rules! hugr_view_methods { fn neighbours(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator + Clone; fn all_neighbours(&self, node: Self::Node) -> impl Iterator + Clone; fn mermaid_string(&self) -> String; + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: crate::hugr::views::render::RenderConfig) -> String; + fn mermaid_string_with_formatter(&self, #[into] formatter: crate::hugr::views::render::MermaidFormatter) -> String; fn dot_string(&self) -> String; fn static_source(&self, node: Self::Node) -> Option; fn static_targets(&self, node: Self::Node) -> Option>; diff --git a/hugr-core/src/hugr/views/render.rs b/hugr-core/src/hugr/views/render.rs index 9d597f1c04..b787a9e383 100644 --- a/hugr-core/src/hugr/views/render.rs +++ b/hugr-core/src/hugr/views/render.rs @@ -1,16 +1,23 @@ //! Helper methods to compute the node/edge/port style when rendering a HUGR //! into dot or mermaid format. +use std::collections::HashMap; + use portgraph::render::{EdgeStyle, NodeStyle, PortStyle, PresentationStyle}; use portgraph::{LinkView, MultiPortGraph, NodeIndex, PortIndex, PortView}; +use crate::core::HugrNode; +use crate::hugr::internal::HugrInternals; use crate::ops::{NamedOp, OpType}; use crate::types::EdgeKind; use crate::{Hugr, HugrView, Node}; -/// Configuration for rendering a HUGR graph. +/// Reduced configuration for rendering a HUGR graph. +/// +/// Additional options are available in the [`MermaidFormatter`] struct. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] +#[deprecated(note = "Use `MermaidFormatter` instead")] pub struct RenderConfig { /// Show the node index in the graph nodes. pub node_indices: bool, @@ -22,6 +29,213 @@ pub struct RenderConfig { pub entrypoint: Option, } +/// Configuration for rendering a HUGR graph. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MermaidFormatter<'h, H: HugrInternals + ?Sized = Hugr> { + /// The HUGR to render. + hugr: &'h H, + /// How to display the node indices. + node_labels: NodeLabel, + /// Show port offsets in the graph edges. + port_offsets_in_edges: bool, + /// Show type labels on edges. + type_labels_in_edges: bool, + /// A node to highlight as the graph entrypoint. + entrypoint: Option, +} + +impl<'h, H: HugrInternals + ?Sized> MermaidFormatter<'h, H> { + /// Create a new [`MermaidFormatter`] from a [`RenderConfig`]. + #[allow(deprecated)] + pub fn from_render_config(config: RenderConfig, hugr: &'h H) -> Self { + let node_labels = if config.node_indices { + NodeLabel::Numeric + } else { + NodeLabel::None + }; + Self { + hugr, + node_labels, + port_offsets_in_edges: config.port_offsets_in_edges, + type_labels_in_edges: config.type_labels_in_edges, + entrypoint: config.entrypoint, + } + } + + /// Create a new [`MermaidFormatter`] for the given [`Hugr`]. + pub fn new(hugr: &'h H) -> Self { + Self { + hugr, + node_labels: NodeLabel::Numeric, + port_offsets_in_edges: true, + type_labels_in_edges: true, + entrypoint: None, + } + } + + /// The entrypoint to highlight in the rendered graph. + pub fn entrypoint(&self) -> Option { + self.entrypoint + } + + /// The rendering style of the node labels. + pub fn node_labels(&self) -> &NodeLabel { + &self.node_labels + } + + /// Whether to show port offsets on edges. + pub fn port_offsets(&self) -> bool { + self.port_offsets_in_edges + } + + /// Whether to show type labels on edges. + pub fn type_labels(&self) -> bool { + self.type_labels_in_edges + } + + /// Set the node labels style. + pub fn with_node_labels(mut self, node_labels: NodeLabel) -> Self { + self.node_labels = node_labels; + self + } + + /// Set whether to show port offsets in edges. + pub fn with_port_offsets(mut self, show: bool) -> Self { + self.port_offsets_in_edges = show; + self + } + + /// Set whether to show type labels in edges. + pub fn with_type_labels(mut self, show: bool) -> Self { + self.type_labels_in_edges = show; + self + } + + /// Set the entrypoint node to highlight. + pub fn with_entrypoint(mut self, entrypoint: impl Into>) -> Self { + self.entrypoint = entrypoint.into(); + self + } + + /// Render the graph into a Mermaid string. + pub fn finish(self) -> String + where + H: HugrView, + { + self.hugr.mermaid_string_with_formatter(self) + } + + pub(crate) fn with_hugr>( + self, + hugr: &NewH, + ) -> MermaidFormatter<'_, NewH> { + let MermaidFormatter { + hugr: _, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = self; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } +} + +/// An error that occurs when trying to convert a `FullRenderConfig` into a +/// `RenderConfig`. +#[derive(Debug, thiserror::Error)] +pub enum UnsupportedRenderConfig { + /// Custom node labels are not supported in the `RenderConfig` struct. + #[error("Custom node labels are not supported in the `RenderConfig` struct")] + CustomNodeLabels, +} + +#[allow(deprecated)] +impl<'h, H: HugrInternals + ?Sized> TryFrom> for RenderConfig { + type Error = UnsupportedRenderConfig; + + fn try_from(value: MermaidFormatter<'h, H>) -> Result { + if matches!(value.node_labels, NodeLabel::Custom(_)) { + return Err(UnsupportedRenderConfig::CustomNodeLabels); + } + let node_indices = matches!(value.node_labels, NodeLabel::Numeric); + Ok(Self { + node_indices, + port_offsets_in_edges: value.port_offsets_in_edges, + type_labels_in_edges: value.type_labels_in_edges, + entrypoint: value.entrypoint, + }) + } +} + +macro_rules! impl_mermaid_formatter_from { + ($t:ty, $($lifetime:tt)?) => { + impl<'h, $($lifetime,)? H: HugrView> From> for MermaidFormatter<'h, H> { + fn from(value: MermaidFormatter<'h, $t>) -> Self { + let MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = value; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } + } + }; +} + +impl_mermaid_formatter_from!(&'hh H, 'hh); +impl_mermaid_formatter_from!(&'hh mut H, 'hh); +impl_mermaid_formatter_from!(std::rc::Rc,); +impl_mermaid_formatter_from!(std::sync::Arc,); +impl_mermaid_formatter_from!(Box,); + +impl<'h, H: HugrView + ToOwned> From>> + for MermaidFormatter<'h, H> +{ + fn from(value: MermaidFormatter<'h, std::borrow::Cow<'_, H>>) -> Self { + let MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = value; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } +} + +/// How to display the node indices. +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub enum NodeLabel { + /// Do not display the node index. + None, + /// Display the node index as a number. + #[default] + Numeric, + /// Display the labels corresponding to the node indices. + Custom(HashMap), +} + +#[allow(deprecated)] impl Default for RenderConfig { fn default() -> Self { Self { @@ -36,8 +250,7 @@ impl Default for RenderConfig { /// Formatter method to compute a node style. pub(in crate::hugr) fn node_style<'a>( h: &'a Hugr, - config: RenderConfig, - fmt_node_index: impl Fn(NodeIndex) -> String + 'a, + formatter: MermaidFormatter<'a>, ) -> Box NodeStyle + 'a> { fn node_name(h: &Hugr, n: NodeIndex) -> String { match h.get_optype(n.into()) { @@ -50,42 +263,54 @@ pub(in crate::hugr) fn node_style<'a>( let mut entrypoint_style = PresentationStyle::default(); entrypoint_style.stroke = Some("#832561".to_string()); entrypoint_style.stroke_width = Some("3px".to_string()); - let entrypoint = config.entrypoint.map(Node::into_portgraph); + let entrypoint = formatter.entrypoint.map(Node::into_portgraph); - if config.node_indices { - Box::new(move |n| { + match formatter.node_labels { + NodeLabel::Numeric => Box::new(move |n| { if Some(n) == entrypoint { NodeStyle::boxed(format!( "({ni}) [**{name}**]", - ni = fmt_node_index(n), + ni = n.index(), name = node_name(h, n) )) .with_attrs(entrypoint_style.clone()) } else { NodeStyle::boxed(format!( "({ni}) {name}", - ni = fmt_node_index(n), + ni = n.index(), name = node_name(h, n) )) } - }) - } else { - Box::new(move |n| { + }), + NodeLabel::None => Box::new(move |n| { if Some(n) == entrypoint { NodeStyle::boxed(format!("[**{name}**]", name = node_name(h, n))) .with_attrs(entrypoint_style.clone()) } else { NodeStyle::boxed(node_name(h, n)) } - }) + }), + NodeLabel::Custom(labels) => Box::new(move |n| { + if Some(n) == entrypoint { + NodeStyle::boxed(format!( + "({label}) [**{name}**]", + label = labels.get(&n.into()).unwrap_or(&n.index().to_string()), + name = node_name(h, n) + )) + .with_attrs(entrypoint_style.clone()) + } else { + NodeStyle::boxed(format!( + "({label}) {name}", + label = labels.get(&n.into()).unwrap_or(&n.index().to_string()), + name = node_name(h, n) + )) + } + }), } } /// Formatter method to compute a port style. -pub(in crate::hugr) fn port_style( - h: &Hugr, - _config: RenderConfig, -) -> Box PortStyle + '_> { +pub(in crate::hugr) fn port_style(h: &Hugr) -> Box PortStyle + '_> { let graph = &h.graph; Box::new(move |port| { let node = graph.port_node(port).unwrap(); @@ -110,15 +335,15 @@ pub(in crate::hugr) fn port_style( /// Formatter method to compute an edge style. #[allow(clippy::type_complexity)] -pub(in crate::hugr) fn edge_style( - h: &Hugr, - config: RenderConfig, +pub(in crate::hugr) fn edge_style<'a>( + h: &'a Hugr, + config: MermaidFormatter<'_>, ) -> Box< dyn FnMut( ::LinkEndpoint, ::LinkEndpoint, ) -> EdgeStyle - + '_, + + 'a, > { let graph = &h.graph; Box::new(move |src, tgt| { @@ -162,3 +387,45 @@ pub(in crate::hugr) fn edge_style( style.with_label(label) }) } + +#[cfg(test)] +mod tests { + use crate::{NodeIndex, builder::test::simple_dfg_hugr}; + + use super::*; + + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + #[test] + fn test_custom_node_labels() { + let h = simple_dfg_hugr(); + let node_labels = h + .nodes() + .map(|n| (n, format!("node_{}", n.index()))) + .collect(); + let config = h + .mermaid_format() + .with_node_labels(NodeLabel::Custom(node_labels)); + insta::assert_snapshot!(h.mermaid_string_with_formatter(config)); + } + + #[test] + fn convert_full_render_config_to_render_config() { + let h = simple_dfg_hugr(); + let config: MermaidFormatter = + MermaidFormatter::new(&h).with_node_labels(NodeLabel::Custom(HashMap::new())); + #[allow(deprecated)] + { + assert!(RenderConfig::try_from(config).is_err()); + } + + #[allow(deprecated)] + let config = RenderConfig { + entrypoint: Some(h.entrypoint()), + ..Default::default() + }; + assert_eq!( + MermaidFormatter::from_render_config(config, &h), + h.mermaid_format() + ) + } +} diff --git a/hugr-core/src/hugr/views/rerooted.rs b/hugr-core/src/hugr/views/rerooted.rs index ea849372db..8c84abdc71 100644 --- a/hugr-core/src/hugr/views/rerooted.rs +++ b/hugr-core/src/hugr/views/rerooted.rs @@ -4,7 +4,6 @@ use crate::hugr::HugrMut; use crate::hugr::internal::{HugrInternals, HugrMutInternals}; -use super::render::RenderConfig; use super::{HugrView, panic_invalid_node}; /// A HUGR wrapper with a modified entrypoint node. @@ -60,14 +59,12 @@ impl HugrView for Rerooted { self.hugr.get_optype(self.entrypoint) } - #[inline] - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + fn mermaid_string_with_formatter( + &self, + formatter: crate::hugr::views::render::MermaidFormatter, + ) -> String { + self.hugr + .mermaid_string_with_formatter(formatter.with_hugr(&self.hugr)) } delegate::delegate! { @@ -103,6 +100,7 @@ impl HugrView for Rerooted { fn first_child(&self, node: Self::Node) -> Option; fn neighbours(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator + Clone; fn all_neighbours(&self, node: Self::Node) -> impl Iterator + Clone; + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: crate::hugr::views::render::RenderConfig) -> String; fn dot_string(&self) -> String; fn static_source(&self, node: Self::Node) -> Option; @@ -235,4 +233,12 @@ mod test { ); assert!(extracted_hugr.get_optype(extracted_bb).is_dataflow_block()); } + + #[test] + fn mermaid_format() { + let h = simple_cfg_hugr(); + let rerooted = h.with_entrypoint(h.entrypoint()); + let mermaid_str = rerooted.mermaid_format().finish(); + assert_eq!(mermaid_str, h.mermaid_format().finish()); + } } diff --git a/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap b/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap new file mode 100644 index 0000000000..22b2ed8a51 --- /dev/null +++ b/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap @@ -0,0 +1,22 @@ +--- +source: hugr-core/src/hugr/views/render.rs +expression: h.mermaid_string_with_formatter(config) +--- +graph LR + subgraph 0 ["(node_0) Module"] + direction LR + subgraph 1 ["(node_1) FuncDefn: #quot;main#quot;"] + direction LR + 2["(node_2) Input"] + 3["(node_3) Output"] + subgraph 4 ["(node_4) [**DFG**]"] + direction LR + style 4 stroke:#832561,stroke-width:3px + 5["(node_5) Input"] + 6["(node_6) Output"] + 5--"0:0
Bool"-->6 + end + 2--"0:0
Bool"-->4 + 4--"0:0
Bool"-->3 + end + end From f51d0481cf2ce6a940f4c7810c31cc0a461cdd09 Mon Sep 17 00:00:00 2001 From: Lukas Heidemann Date: Tue, 10 Jun 2025 09:48:55 +0100 Subject: [PATCH 04/22] fix: Fixed invalid extension name in test. (#2319) The Python testing suite defined an extension named `pytest.quantum,`. The `,` is not allowed as part of an extension name, making the model import fail. --- hugr-py/tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-py/tests/conftest.py b/hugr-py/tests/conftest.py index f3ce57336f..f4b2617a01 100644 --- a/hugr-py/tests/conftest.py +++ b/hugr-py/tests/conftest.py @@ -21,7 +21,7 @@ from hugr.ops import ComWire -QUANTUM_EXT = ext.Extension("pytest.quantum,", ext.Version(0, 1, 0)) +QUANTUM_EXT = ext.Extension("pytest.quantum", ext.Version(0, 1, 0)) QUANTUM_EXT.add_op_def( ext.OpDef( name="H", From aee0e741c7f64116ee892bd8266c2d2c1e40739a Mon Sep 17 00:00:00 2001 From: Lukas Heidemann Date: Tue, 10 Jun 2025 09:57:34 +0100 Subject: [PATCH 05/22] fix: Fixed bug in python model export name mangling. (#2323) This PR fixes a bug in the Python model export: `Call` nodes referred to themselves instead of the function to be called. --- hugr-py/src/hugr/model/export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-py/src/hugr/model/export.py b/hugr-py/src/hugr/model/export.py index eec5e99958..a79ba4f8e3 100644 --- a/hugr-py/src/hugr/model/export.py +++ b/hugr-py/src/hugr/model/export.py @@ -539,7 +539,7 @@ def find_func_input(self, node: Node) -> str | None: case _: return None - return _mangle_name(node, name) + return _mangle_name(func_node, name) def find_const_input(self, node: Node) -> model.Term | None: """Find and export the constant that a node is connected to, if any.""" From 273e81dc90ab7c82d0e1ffed871387248a45be16 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Tue, 10 Jun 2025 10:02:58 +0100 Subject: [PATCH 06/22] refactor(llvm): replace HashMap with BTreeMap (#2313) I don't believe any of these changes are breaking or cause serious performance regressions but worth double checking. Should help with determinism (avoiding future pit-falls even if not resolving any current issues) --- hugr-llvm/src/custom/extension_op.rs | 4 ++-- hugr-llvm/src/custom/load_constant.rs | 4 ++-- hugr-llvm/src/emit/func.rs | 4 ++-- hugr-llvm/src/emit/ops/cfg.rs | 6 +++--- hugr-llvm/src/utils/type_map.rs | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hugr-llvm/src/custom/extension_op.rs b/hugr-llvm/src/custom/extension_op.rs index 4afef24e3e..640b43bcb5 100644 --- a/hugr-llvm/src/custom/extension_op.rs +++ b/hugr-llvm/src/custom/extension_op.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{collections::BTreeMap, rc::Rc}; use hugr_core::{ HugrView, Node, @@ -56,7 +56,7 @@ impl< /// /// Those callbacks may hold references with lifetimes older than `'a`. #[derive(Default)] -pub struct ExtensionOpMap<'a, H>(HashMap<(ExtensionId, OpName), Box>>); +pub struct ExtensionOpMap<'a, H>(BTreeMap<(ExtensionId, OpName), Box>>); impl<'a, H: HugrView> ExtensionOpMap<'a, H> { /// Register a callback to emit a [`ExtensionOp`], keyed by fully diff --git a/hugr-llvm/src/custom/load_constant.rs b/hugr-llvm/src/custom/load_constant.rs index 0ea964d160..0d84a99ae8 100644 --- a/hugr-llvm/src/custom/load_constant.rs +++ b/hugr-llvm/src/custom/load_constant.rs @@ -1,5 +1,5 @@ //! Provides the implementation for a collection of [`CustomConst`] callbacks. -use std::{any::TypeId, collections::HashMap}; +use std::{any::TypeId, collections::BTreeMap}; use hugr_core::{HugrView, Node, ops::constant::CustomConst}; use inkwell::values::BasicValueEnum; @@ -36,7 +36,7 @@ impl< /// The callbacks are keyed by the [`TypeId`] of those impls. #[derive(Default)] pub struct LoadConstantsMap<'a, H>( - HashMap>>, + BTreeMap>>, ); impl<'a, H: HugrView> LoadConstantsMap<'a, H> { diff --git a/hugr-llvm/src/emit/func.rs b/hugr-llvm/src/emit/func.rs index e6ce4c9c4e..77d865540f 100644 --- a/hugr-llvm/src/emit/func.rs +++ b/hugr-llvm/src/emit/func.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, rc::Rc}; +use std::{collections::BTreeMap, rc::Rc}; use anyhow::{Result, anyhow}; use hugr_core::{ @@ -51,7 +51,7 @@ where emit_context: EmitModuleContext<'c, 'a, H>, todo: EmissionSet, func: FunctionValue<'c>, - env: HashMap>, + env: BTreeMap>, builder: Builder<'c>, prologue_bb: BasicBlock<'c>, launch_bb: BasicBlock<'c>, diff --git a/hugr-llvm/src/emit/ops/cfg.rs b/hugr-llvm/src/emit/ops/cfg.rs index 744427a1c6..bdf27389a2 100644 --- a/hugr-llvm/src/emit/ops/cfg.rs +++ b/hugr-llvm/src/emit/ops/cfg.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::BTreeMap; use anyhow::{Result, anyhow}; use hugr_core::{ @@ -21,7 +21,7 @@ use crate::{ use super::emit_dataflow_parent; pub struct CfgEmitter<'c, 'hugr, H> { - bbs: HashMap, (BasicBlock<'c>, RowMailBox<'c>)>, + bbs: BTreeMap, (BasicBlock<'c>, RowMailBox<'c>)>, inputs: Option>>, outputs: Option>, node: FatNode<'hugr, CFG, H>, @@ -47,7 +47,7 @@ impl<'c, 'hugr, H: HugrView> CfgEmitter<'c, 'hugr, H> { // to crate the other blocks immediately before it. This is just for // nice block ordering. let exit_block = context.new_basic_block("", None); - let mut bbs = HashMap::new(); + let mut bbs = BTreeMap::new(); for child in node.children() { if child.is_exit_block() { let output_row = { diff --git a/hugr-llvm/src/utils/type_map.rs b/hugr-llvm/src/utils/type_map.rs index 020d567a67..c4e64d90e8 100644 --- a/hugr-llvm/src/utils/type_map.rs +++ b/hugr-llvm/src/utils/type_map.rs @@ -1,5 +1,5 @@ //! Provides a generic mapping from [`HugrType`] into some domain. -use std::collections::HashMap; +use std::collections::BTreeMap; use hugr_core::{ extension::ExtensionId, @@ -94,7 +94,7 @@ pub type CustomTypeKey = (ExtensionId, TypeName); #[derive(Default)] pub struct TypeMap<'a, TM: TypeMapping> { type_map: TM, - custom_hooks: HashMap + 'a>>, + custom_hooks: BTreeMap + 'a>>, } impl<'a, TM: TypeMapping + 'a> TypeMap<'a, TM> { From 9d039d03502fe70fefc93c5286f8fbafafce4f40 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Tue, 10 Jun 2025 10:40:39 +0100 Subject: [PATCH 07/22] fix(py): correct ConstString JSON encoding (#2325) --- hugr-py/src/hugr/std/prelude.py | 3 +-- hugr-py/tests/test_prelude.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/hugr-py/src/hugr/std/prelude.py b/hugr-py/src/hugr/std/prelude.py index 1818bd9d1b..9534549538 100644 --- a/hugr-py/src/hugr/std/prelude.py +++ b/hugr-py/src/hugr/std/prelude.py @@ -22,11 +22,10 @@ class StringVal(val.ExtensionValue): def to_value(self) -> val.Extension: name = "ConstString" - payload = {"value": self.v} return val.Extension( name, typ=STRING_T, - val=payload, + val=self.v, ) def __str__(self) -> str: diff --git a/hugr-py/tests/test_prelude.py b/hugr-py/tests/test_prelude.py index a0f47ac5a4..c2ef2cbeec 100644 --- a/hugr-py/tests/test_prelude.py +++ b/hugr-py/tests/test_prelude.py @@ -1,8 +1,18 @@ +from hugr.build.dfg import Dfg from hugr.std.prelude import STRING_T, StringVal +from .conftest import validate + def test_string_val(): - ext_val = StringVal("test").to_value() + val = StringVal("test") + ext_val = val.to_value() assert ext_val.name == "ConstString" assert ext_val.typ == STRING_T - assert ext_val.val == {"value": "test"} + assert ext_val.val == "test" + + dfg = Dfg() + v = dfg.load(val) + dfg.set_outputs(v) + + validate(dfg.hugr) From 3b63b8ae8a72abb6a91c78ad6a9fd785e93c6100 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Wed, 11 Jun 2025 10:19:33 +0100 Subject: [PATCH 08/22] feat(core): builder pattern for EnvelopeConfig (#2330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit non exhaustive structs require constructors to be usable outside the crate --------- Co-authored-by: Agustín Borgna <121866228+aborgna-q@users.noreply.github.com> --- hugr-core/src/envelope/header.rs | 40 ++++++++++++++++++++++---------- hugr/benches/benchmarks/hugr.rs | 9 +++---- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/hugr-core/src/envelope/header.rs b/hugr-core/src/envelope/header.rs index 99004b7efb..66af887454 100644 --- a/hugr-core/src/envelope/header.rs +++ b/hugr-core/src/envelope/header.rs @@ -95,7 +95,7 @@ impl EnvelopeFormat { } /// Configuration for encoding an envelope. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[non_exhaustive] pub struct EnvelopeConfig { /// The format to use for the payload. @@ -105,19 +105,29 @@ pub struct EnvelopeConfig { pub zstd: Option, } -impl Default for EnvelopeConfig { - fn default() -> Self { - let format = Default::default(); - let zstd = if cfg!(feature = "zstd") { - Some(ZstdConfig::default()) - } else { - None - }; - Self { format, zstd } +impl EnvelopeConfig { + /// Create a new envelope configuration with the specified format. + /// `zstd` compression is disabled by default. + pub fn new(format: EnvelopeFormat) -> Self { + Self { + format, + ..Default::default() + } + } + + /// Set the zstd compression configuration for the envelope. + pub fn with_zstd(self, zstd: ZstdConfig) -> Self { + Self { + zstd: Some(zstd), + ..self + } + } + + /// Disable zstd compression in the envelope configuration. + pub fn disable_compression(self) -> Self { + Self { zstd: None, ..self } } -} -impl EnvelopeConfig { /// Create a new envelope header with the specified configuration. pub(super) fn make_header(&self) -> EnvelopeHeader { EnvelopeHeader { @@ -162,6 +172,12 @@ pub struct ZstdConfig { } impl ZstdConfig { + /// Create a new zstd configuration with the specified compression level. + pub fn new(level: u8) -> Self { + Self { + level: NonZeroU8::new(level), + } + } /// Create a new zstd configuration with default compression level. #[must_use] pub const fn default_level() -> Self { diff --git a/hugr/benches/benchmarks/hugr.rs b/hugr/benches/benchmarks/hugr.rs index 3616e1ed5f..e5f2d4de2b 100644 --- a/hugr/benches/benchmarks/hugr.rs +++ b/hugr/benches/benchmarks/hugr.rs @@ -19,9 +19,7 @@ trait Serializer { struct JsonSer; impl Serializer for JsonSer { fn serialize(&self, hugr: &Hugr) -> Vec { - let mut cfg = EnvelopeConfig::default(); - cfg.format = EnvelopeFormat::PackageJson; - cfg.zstd = None; + let cfg = EnvelopeConfig::new(EnvelopeFormat::PackageJson).disable_compression(); let mut bytes = Vec::new(); hugr.store(&mut bytes, cfg).unwrap(); @@ -36,9 +34,8 @@ struct CapnpSer; impl Serializer for CapnpSer { fn serialize(&self, hugr: &Hugr) -> Vec { - let mut cfg = EnvelopeConfig::default(); - cfg.format = EnvelopeFormat::ModelWithExtensions; - cfg.zstd = Some(Default::default()); + let cfg = + EnvelopeConfig::new(EnvelopeFormat::ModelWithExtensions).with_zstd(Default::default()); let mut bytes = Vec::new(); hugr.store(&mut bytes, cfg).unwrap(); From 1cf16d2aefa57cf6ce69ce1c999d3f2e9ed34af9 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 13 Jun 2025 12:13:13 +0100 Subject: [PATCH 09/22] fix: update CallGraph and remove_dead_funcs for module-only FuncDefns (#2336) This should have been part of #2256 but some confusion dates from #2147. Making a callgraph for only the functions beneath the entrypoint makes no sense - that's either all the functions (if the entrypoint is the root) or at most one of them (if the entrypoint is one of the funcdefns). Of course, we didn't test with any Hugrs that had a non-module-root entrypoint.... so the sensible thing to do now, seems to be for callgraph to ignore the entrypoint. (We can optimize it looking for FuncDefns tho, they are now easy to find.) Dead function removal allowed specifying starting points for analysis (top-level FuncDefns) but only if the Hugr's entrypoint was the module. There seems no good reason not to allow specifying module-level FuncDefns even if the entrypoint is lower, and similarly, we should use the entrypoint as one starting point (perhaps among many) for analysis... (One could rename `entry_points` used by analysis to "starting_points" but that would be breaking) --- hugr-passes/src/call_graph.rs | 19 ++++--- hugr-passes/src/dead_funcs.rs | 99 +++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 54 deletions(-) diff --git a/hugr-passes/src/call_graph.rs b/hugr-passes/src/call_graph.rs index 6bee5eaa0c..e33881b1f7 100644 --- a/hugr-passes/src/call_graph.rs +++ b/hugr-passes/src/call_graph.rs @@ -26,13 +26,17 @@ pub enum CallGraphNode { } /// Details the [`Call`]s and [`LoadFunction`]s in a Hugr. -/// Each node in the `CallGraph` corresponds to a [`FuncDefn`] in the Hugr; each edge corresponds -/// to a [`Call`]/[`LoadFunction`] of the edge's target, contained in the edge's source. /// -/// For Hugrs whose root is neither a [Module](OpType::Module) nor a [`FuncDefn`], the call graph -/// will have an additional [`CallGraphNode::NonFuncRoot`] corresponding to the Hugr's root, with no incoming edges. +/// Each node in the `CallGraph` corresponds to a [`FuncDefn`] or [`FuncDecl`] in the Hugr; +/// each edge corresponds to a [`Call`]/[`LoadFunction`] of the edge's target, contained in +/// the edge's source. +/// +/// For Hugrs whose entrypoint is neither a [Module](OpType::Module) nor a [`FuncDefn`], the +/// call graph will have an additional [`CallGraphNode::NonFuncRoot`] corresponding to the Hugr's +/// entrypoint, with no incoming edges. /// /// [`Call`]: OpType::Call +/// [`FuncDecl`]: OpType::FuncDecl /// [`FuncDefn`]: OpType::FuncDefn /// [`LoadFunction`]: OpType::LoadFunction pub struct CallGraph { @@ -41,14 +45,13 @@ pub struct CallGraph { } impl CallGraph { - /// Makes a new `CallGraph` for a specified (subview) of a Hugr. - /// Calls to functions outside the view will be dropped. + /// Makes a new `CallGraph` for a Hugr. pub fn new(hugr: &impl HugrView) -> Self { let mut g = Graph::default(); let non_func_root = (!hugr.get_optype(hugr.entrypoint()).is_module()).then_some(hugr.entrypoint()); let node_to_g = hugr - .entry_descendants() + .children(hugr.module_root()) .filter_map(|n| { let weight = match hugr.get_optype(n) { OpType::FuncDecl(_) => CallGraphNode::FuncDecl(n), @@ -94,7 +97,7 @@ impl CallGraph { /// Convert a Hugr [Node] into a petgraph node index. /// Result will be `None` if `n` is not a [`FuncDefn`](OpType::FuncDefn), - /// [`FuncDecl`](OpType::FuncDecl) or the hugr root. + /// [`FuncDecl`](OpType::FuncDecl) or the [HugrView::entrypoint]. pub fn node_index(&self, n: N) -> Option> { self.node_to_g.get(&n).copied() } diff --git a/hugr-passes/src/dead_funcs.rs b/hugr-passes/src/dead_funcs.rs index 7109a141f3..e09b6a3b43 100644 --- a/hugr-passes/src/dead_funcs.rs +++ b/hugr-passes/src/dead_funcs.rs @@ -21,7 +21,7 @@ use super::call_graph::{CallGraph, CallGraphNode}; #[non_exhaustive] /// Errors produced by [`RemoveDeadFuncsPass`]. pub enum RemoveDeadFuncsError { - /// The specified entry point is not a `FuncDefn` node or is not a child of the root. + /// The specified entry point is not a `FuncDefn` node #[error( "Entrypoint for RemoveDeadFuncsPass {node} was not a function definition in the root module" )] @@ -35,30 +35,17 @@ fn reachable_funcs<'a, H: HugrView>( cg: &'a CallGraph, h: &'a H, entry_points: impl IntoIterator, -) -> Result + 'a, RemoveDeadFuncsError> { +) -> impl Iterator + 'a { let g = cg.graph(); - let mut entry_points = entry_points.into_iter(); - let searcher = if h.get_optype(h.entrypoint()).is_module() { - let mut d = Dfs::new(g, 0.into()); - d.stack.clear(); - for n in entry_points { - if !h.get_optype(n).is_func_defn() || h.get_parent(n) != Some(h.entrypoint()) { - return Err(RemoveDeadFuncsError::InvalidEntryPoint { node: n }); - } - d.stack.push(cg.node_index(n).unwrap()); - } - d - } else { - if let Some(n) = entry_points.next() { - // Can't be a child of the module root as there isn't a module root! - return Err(RemoveDeadFuncsError::InvalidEntryPoint { node: n }); - } - Dfs::new(g, cg.node_index(h.entrypoint()).unwrap()) - }; - Ok(searcher.iter(g).map(|i| match g.node_weight(i).unwrap() { + let mut d = Dfs::new(g, 0.into()); + d.stack.clear(); // Remove the fake 0 + for n in entry_points { + d.stack.push(cg.node_index(n).unwrap()); + } + d.iter(g).map(|i| match g.node_weight(i).unwrap() { CallGraphNode::FuncDefn(n) | CallGraphNode::FuncDecl(n) => *n, CallGraphNode::NonFuncRoot => h.entrypoint(), - })) + }) } #[derive(Debug, Clone, Default)] @@ -86,14 +73,31 @@ impl> ComposablePass for RemoveDeadFuncsPass { type Error = RemoveDeadFuncsError; type Result = (); fn run(&self, hugr: &mut H) -> Result<(), RemoveDeadFuncsError> { - let reachable = reachable_funcs( - &CallGraph::new(hugr), - hugr, - self.entry_points.iter().copied(), - )? - .collect::>(); + let mut entry_points = Vec::new(); + for &n in self.entry_points.iter() { + if !hugr.get_optype(n).is_func_defn() { + return Err(RemoveDeadFuncsError::InvalidEntryPoint { node: n }); + } + debug_assert_eq!(hugr.get_parent(n), Some(hugr.module_root())); + entry_points.push(n); + } + if hugr.entrypoint() != hugr.module_root() { + entry_points.push(hugr.entrypoint()) + } + + let mut reachable = + reachable_funcs(&CallGraph::new(hugr), hugr, entry_points).collect::>(); + // Also prevent removing the entrypoint itself + let mut n = Some(hugr.entrypoint()); + while let Some(n2) = n { + n = hugr.get_parent(n2); + if n == Some(hugr.module_root()) { + reachable.insert(n2); + } + } + let unreachable = hugr - .entry_descendants() + .children(hugr.module_root()) .filter(|n| { OpTag::Function.is_superset(hugr.get_optype(*n).tag()) && !reachable.contains(n) }) @@ -108,17 +112,13 @@ impl> ComposablePass for RemoveDeadFuncsPass { /// Deletes from the Hugr any functions that are not used by either [`Call`] or /// [`LoadFunction`] nodes in reachable parts. /// -/// For [`Module`]-rooted Hugrs, `entry_points` may provide a list of entry points, -/// which must be children of the root. Note that if `entry_points` is empty, this will -/// result in all functions in the module being removed. -/// -/// For non-[`Module`]-rooted Hugrs, `entry_points` must be empty; the root node is used. +/// `entry_points` may provide a list of entry points, which must be [`FuncDefn`]s (children of the root). +/// The [HugrView::entrypoint] will also be used unless it is the [HugrView::module_root]. +/// Note that for a [`Module`]-rooted Hugr with no `entry_points` provided, this will remove +/// all functions from the module. /// /// # Errors -/// * If there are any `entry_points` but the root of the hugr is not a [`Module`] -/// * If any node in `entry_points` is -/// * not a [`FuncDefn`], or -/// * not a child of the root +/// * If any node in `entry_points` is not a [`FuncDefn`] /// /// [`Call`]: hugr_core::ops::OpType::Call /// [`FuncDefn`]: hugr_core::ops::OpType::FuncDefn @@ -138,24 +138,28 @@ pub fn remove_dead_funcs( mod test { use std::collections::HashMap; + use hugr_core::ops::handle::NodeHandle; use itertools::Itertools; use rstest::rstest; use hugr_core::builder::{ Container, Dataflow, DataflowSubContainer, HugrBuilder, ModuleBuilder, }; + use hugr_core::hugr::hugrmut::HugrMut; use hugr_core::{HugrView, extension::prelude::usize_t, types::Signature}; use super::remove_dead_funcs; #[rstest] - #[case([], vec![])] // No entry_points removes everything! - #[case(["main"], vec!["from_main", "main"])] - #[case(["from_main"], vec!["from_main"])] - #[case(["other1"], vec!["other1", "other2"])] - #[case(["other2"], vec!["other2"])] - #[case(["other1", "other2"], vec!["other1", "other2"])] + #[case(false, [], vec![])] // No entry_points removes everything! + #[case(true, [], vec!["from_main", "main"])] + #[case(false, ["main"], vec!["from_main", "main"])] + #[case(false, ["from_main"], vec!["from_main"])] + #[case(false, ["other1"], vec!["other1", "other2"])] + #[case(true, ["other2"], vec!["from_main", "main", "other2"])] + #[case(false, ["other1", "other2"], vec!["other1", "other2"])] fn remove_dead_funcs_entry_points( + #[case] use_hugr_entrypoint: bool, #[case] entry_points: impl IntoIterator, #[case] retained_funcs: Vec<&'static str>, ) -> Result<(), Box> { @@ -173,12 +177,15 @@ mod test { let fm = fm.finish_with_outputs(f_inp)?; let mut m = hb.define_function("main", Signature::new_endo(usize_t()))?; let mc = m.call(fm.handle(), &[], m.input_wires())?; - m.finish_with_outputs(mc.outputs())?; + let m = m.finish_with_outputs(mc.outputs())?; let mut hugr = hb.finish_hugr()?; + if use_hugr_entrypoint { + hugr.set_entrypoint(m.node()); + } let avail_funcs = hugr - .entry_descendants() + .children(hugr.module_root()) .filter_map(|n| { hugr.get_optype(n) .as_func_defn() From a660fc3fe13717ed64e7256095ddbbe9555f30d8 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Fri, 13 Jun 2025 13:12:54 +0100 Subject: [PATCH 10/22] feat(cli): convert sub-command for converting envelope formats (#2331) Closes #2307 --- Cargo.lock | 2 + Cargo.toml | 1 + hugr-cli/Cargo.toml | 2 + hugr-cli/src/convert.rs | 85 +++++++++++++ hugr-cli/src/hugr_io.rs | 25 +++- hugr-cli/src/lib.rs | 10 +- hugr-cli/src/main.rs | 13 +- hugr-cli/tests/convert.rs | 262 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 391 insertions(+), 9 deletions(-) create mode 100644 hugr-cli/src/convert.rs create mode 100644 hugr-cli/tests/convert.rs diff --git a/Cargo.lock b/Cargo.lock index b230a0cd9f..466d271f8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1222,6 +1222,7 @@ dependencies = [ name = "hugr-cli" version = "0.20.1" dependencies = [ + "anyhow", "assert_cmd", "assert_fs", "clap", @@ -1233,6 +1234,7 @@ dependencies = [ "rstest", "serde_json", "tempfile", + "thiserror 2.0.12", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6ac053a654..289eb9dd13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ result_large_err = "allow" large_enum_variant = "allow" [workspace.dependencies] +anyhow = "1.0.98" insta = { version = "1.43.1" } bitvec = "1.0.1" capnp = "0.20.6" diff --git a/hugr-cli/Cargo.toml b/hugr-cli/Cargo.toml index faa64c220d..43f44b040b 100644 --- a/hugr-cli/Cargo.toml +++ b/hugr-cli/Cargo.toml @@ -22,6 +22,8 @@ derive_more = { workspace = true, features = ["display", "error", "from"] } hugr = { path = "../hugr", version = "0.20.1" } serde_json.workspace = true clio = { workspace = true, features = ["clap-parse"] } +anyhow.workspace = true +thiserror.workspace = true [lints] workspace = true diff --git a/hugr-cli/src/convert.rs b/hugr-cli/src/convert.rs new file mode 100644 index 0000000000..d330f1b43c --- /dev/null +++ b/hugr-cli/src/convert.rs @@ -0,0 +1,85 @@ +//! Convert between different HUGR envelope formats. +use anyhow::Result; +use clap::Parser; +use clio::Output; +use hugr::envelope::{EnvelopeConfig, EnvelopeFormat, ZstdConfig}; + +use crate::CliError; +use crate::hugr_io::HugrInputArgs; + +/// Convert between different HUGR envelope formats. +#[derive(Parser, Debug)] +#[clap(version = "1.0", long_about = None)] +#[clap(about = "Convert a HUGR between different envelope formats.")] +#[group(id = "hugr")] +#[non_exhaustive] +pub struct ConvertArgs { + /// Hugr input. + #[command(flatten)] + pub input_args: HugrInputArgs, + + /// Output file. Use '-' for stdout. + #[clap(short, long, value_parser, default_value = "-")] + pub output: Output, + + /// Output format. One of: json, model, model-exts, model-text, model-text-exts + #[clap(short, long, value_name = "FORMAT")] + pub format: Option, + + /// Use default text-based envelope configuration. + /// Cannot be combined with --format or --binary. + #[clap(long, conflicts_with_all = ["format", "binary"])] + pub text: bool, + + /// Use default binary envelope configuration. + /// Cannot be combined with --format or --text. + #[clap(long, conflicts_with_all = ["format", "text"])] + pub binary: bool, + + /// Enable zstd compression for the output + #[clap(long)] + pub compress: bool, + + /// Zstd compression level (1-22, where 1 is fastest and 22 is best compression) + /// Uses the default level if not specified. + #[clap(long, value_name = "LEVEL", requires = "compress")] + pub compression_level: Option, +} + +impl ConvertArgs { + /// Convert a HUGR between different envelope formats + pub fn run_convert(&mut self) -> Result<()> { + let (env_config, package) = self.input_args.get_envelope()?; + + // Handle text and binary format flags, which override the format option + let mut config = if self.text { + EnvelopeConfig::text() + } else if self.binary { + EnvelopeConfig::binary() + } else { + // Parse the requested format + let format = match &self.format { + Some(fmt) => match fmt.as_str() { + "json" => EnvelopeFormat::PackageJson, + "model" => EnvelopeFormat::Model, + "model-exts" => EnvelopeFormat::ModelWithExtensions, + "model-text" => EnvelopeFormat::ModelText, + "model-text-exts" => EnvelopeFormat::ModelTextWithExtensions, + _ => Err(CliError::InvalidFormat(fmt.clone()))?, + }, + None => env_config.format, // Use input format if not specified + }; + EnvelopeConfig::new(format) + }; + + // Configure compression + if let Some(level) = self.compress.then_some(self.compression_level).flatten() { + config = config.with_zstd(ZstdConfig::new(level)); + } + + // Write the package with the requested format + hugr::envelope::write_envelope(&mut self.output, &package, config)?; + + Ok(()) + } +} diff --git a/hugr-cli/src/hugr_io.rs b/hugr-cli/src/hugr_io.rs index bc7da686c2..9ff4b0cf85 100644 --- a/hugr-cli/src/hugr_io.rs +++ b/hugr-cli/src/hugr_io.rs @@ -1,7 +1,7 @@ //! Input/output arguments for the HUGR CLI. use clio::Input; -use hugr::envelope::{EnvelopeError, read_envelope}; +use hugr::envelope::{EnvelopeConfig, EnvelopeError, read_envelope}; use hugr::extension::ExtensionRegistry; use hugr::package::Package; use hugr::{Extension, Hugr}; @@ -43,18 +43,29 @@ impl HugrInputArgs { /// Read a hugr envelope from the input and return the package encoded /// within. /// + /// # Errors + /// /// If [`HugrInputArgs::hugr_json`] is `true`, [`HugrInputArgs::get_hugr`] should be called instead as /// reading the input as a package will fail. pub fn get_package(&mut self) -> Result { + self.get_envelope().map(|(_, package)| package) + } + + /// Read a hugr envelope from the input and return the envelope + /// configuration and the package encoded within. + /// + /// # Errors + /// + /// If [`HugrInputArgs::hugr_json`] is `true`, [`HugrInputArgs::get_hugr`] should be called instead as + /// reading the input as a package will fail. + pub fn get_envelope(&mut self) -> Result<(EnvelopeConfig, Package), CliError> { let extensions = self.load_extensions()?; let buffer = BufReader::new(&mut self.input); - match read_envelope(buffer, &extensions) { - Ok((_, pkg)) => Ok(pkg), - Err(EnvelopeError::MagicNumber { .. }) => Err(CliError::NotAnEnvelope), - Err(e) => Err(CliError::Envelope(e)), - } + read_envelope(buffer, &extensions).map_err(|e| match e { + EnvelopeError::MagicNumber { .. } => CliError::NotAnEnvelope, + _ => CliError::Envelope(e), + }) } - /// Read a hugr JSON file from the input. /// /// This is a legacy option for reading old HUGR JSON files when the diff --git a/hugr-cli/src/lib.rs b/hugr-cli/src/lib.rs index d70519a2c5..0b91ed547b 100644 --- a/hugr-cli/src/lib.rs +++ b/hugr-cli/src/lib.rs @@ -63,6 +63,7 @@ use hugr::envelope::EnvelopeError; use hugr::package::PackageValidationError; use std::ffi::OsString; +pub mod convert; pub mod extensions; pub mod hugr_io; pub mod mermaid; @@ -81,13 +82,15 @@ pub enum CliArgs { GenExtensions(extensions::ExtArgs), /// Write HUGR as mermaid diagrams. Mermaid(mermaid::MermaidArgs), + /// Convert between different HUGR envelope formats. + Convert(convert::ConvertArgs), /// External commands #[command(external_subcommand)] External(Vec), } /// Error type for the CLI. -#[derive(Debug, derive_more::Display, derive_more::Error, derive_more::From)] +#[derive(Debug, derive_more::Display, thiserror::Error, derive_more::From)] #[non_exhaustive] pub enum CliError { /// Error reading input. @@ -107,6 +110,11 @@ pub enum CliError { "Input file is not a HUGR envelope. Invalid magic number.\n\nUse `--hugr-json` to read a raw HUGR JSON file instead." )] NotAnEnvelope, + /// Invalid format string for conversion. + #[display( + "Invalid format: '{_0}'. Valid formats are: json, model, model-exts, model-text, model-text-exts" + )] + InvalidFormat(String), } /// Other arguments affecting the HUGR CLI runtime. diff --git a/hugr-cli/src/main.rs b/hugr-cli/src/main.rs index fe405101db..8063f25916 100644 --- a/hugr-cli/src/main.rs +++ b/hugr-cli/src/main.rs @@ -2,7 +2,7 @@ use clap::Parser as _; -use hugr_cli::{CliArgs, mermaid, validate}; +use hugr_cli::{CliArgs, convert, mermaid, validate}; use clap_verbosity_flag::log::Level; @@ -11,6 +11,7 @@ fn main() { CliArgs::Validate(args) => run_validate(args), CliArgs::GenExtensions(args) => args.run_dump(&hugr::std_extensions::STD_REG), CliArgs::Mermaid(args) => run_mermaid(args), + CliArgs::Convert(args) => run_convert(args), CliArgs::External(args) => { // External subcommand support: invoke `hugr-` if args.is_empty() { @@ -71,3 +72,13 @@ fn run_mermaid(mut args: mermaid::MermaidArgs) { std::process::exit(1); } } + +/// Run the `convert` subcommand. +fn run_convert(mut args: convert::ConvertArgs) { + let result = args.run_convert(); + + if let Err(e) = result { + eprintln!("{e}"); + std::process::exit(1); + } +} diff --git a/hugr-cli/tests/convert.rs b/hugr-cli/tests/convert.rs new file mode 100644 index 0000000000..343e0bccd5 --- /dev/null +++ b/hugr-cli/tests/convert.rs @@ -0,0 +1,262 @@ +//! Tests for the convert subcommand +//! +//! Miri is globally disabled for these tests because they mostly involve +//! calling the CLI binary, which Miri doesn't support. +#![cfg(all(test, not(miri)))] + +use assert_cmd::Command; +use assert_fs::{NamedTempFile, fixture::FileWriteStr}; +use hugr::builder::{DataflowSubContainer, ModuleBuilder}; +use hugr::envelope::{EnvelopeConfig, EnvelopeFormat, read_envelope}; +use hugr::package::Package; +use hugr::types::Type; +use hugr::{ + builder::{Container, Dataflow}, + extension::ExtensionRegistry, + extension::prelude::bool_t, + types::Signature, +}; +use predicates::str::contains; +use rstest::{fixture, rstest}; +use std::io::BufReader; + +#[fixture] +fn cmd() -> Command { + Command::cargo_bin("hugr").unwrap() +} + +#[fixture] +fn convert_cmd(mut cmd: Command) -> Command { + cmd.arg("convert"); + cmd +} + +/// A test package, containing a module-rooted HUGR. +#[fixture] +fn test_package(#[default(bool_t())] id_type: Type) -> Package { + let mut module = ModuleBuilder::new(); + let df = module + .define_function("test", Signature::new_endo(id_type)) + .unwrap(); + let [i] = df.input_wires_arr(); + df.finish_with_outputs([i]).unwrap(); + let hugr = module.hugr().clone(); // unvalidated + + Package::new(vec![hugr]) +} + +#[fixture] +fn test_envelope_text(test_package: Package) -> (String, Package) { + let config = EnvelopeConfig::text(); + (test_package.store_str(config).unwrap(), test_package) +} + +#[fixture] +fn test_envelope_file(test_envelope_text: (String, Package)) -> NamedTempFile { + let file = assert_fs::NamedTempFile::new("sample.hugr").unwrap(); + file.write_str(&test_envelope_text.0).unwrap(); + file +} + +#[rstest] +fn test_convert_to_json(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "json", + ]); + + convert_cmd.assert().success(); + + // Verify the output exists and is a valid hugr envelope + let output_content = std::fs::read(output_file.path()).expect("Failed to read output file"); + let reader = BufReader::new(output_content.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify the format is correct + assert_eq!(config.format, EnvelopeFormat::PackageJson); +} + +#[rstest] +fn test_convert_to_model(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "model", + ]); + + convert_cmd.assert().success(); + + // Verify the output exists and is a valid hugr envelope + let output_content = std::fs::read(output_file.path()).expect("Failed to read output file"); + let reader = BufReader::new(output_content.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify the format is correct + assert_eq!(config.format, EnvelopeFormat::Model); +} + +#[rstest] +fn test_convert_invalid_format(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "invalid-format", + ]); + + // This should fail with an error message about the invalid format + convert_cmd + .assert() + .failure() + .stderr(contains("Invalid format")); +} + +#[rstest] +fn test_convert_with_compression(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--compress", + "--compression-level", + "5", + ]); + + convert_cmd.assert().success(); +} + +#[rstest] +fn test_convert_stdin_stdout(test_envelope_text: (String, Package), mut convert_cmd: Command) { + // Use stdin/stdout + convert_cmd.args(["-", "--format", "model-exts"]); + convert_cmd.write_stdin(test_envelope_text.0); + + // Should succeed and produce output to stdout + convert_cmd.assert().success(); +} + +#[rstest] +fn test_convert_model_text_format(test_envelope_file: NamedTempFile, mut convert_cmd: Command) { + // Create output file + let output_file = assert_fs::NamedTempFile::new("output.hugr").unwrap(); + + convert_cmd.args([ + test_envelope_file.path().to_str().unwrap(), + "-o", + output_file.path().to_str().unwrap(), + "--format", + "model-text", + ]); + + convert_cmd.assert().success(); + + // Verify the output exists and is a valid hugr envelope + let output_content = std::fs::read(output_file.path()).expect("Failed to read output file"); + let reader = BufReader::new(output_content.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify the format is correct + assert_eq!(config.format, EnvelopeFormat::ModelText); +} + +#[rstest] +fn test_format_roundtrip(test_package: Package) { + // Test conversion between all formats in a roundtrip + // Start with JSON format + let config_json = EnvelopeConfig::new(EnvelopeFormat::PackageJson); + let mut json_data = Vec::new(); + hugr::envelope::write_envelope(&mut json_data, &test_package, config_json).unwrap(); + + // Convert to Model format + let config_model = EnvelopeConfig::new(EnvelopeFormat::Model); + let reader = BufReader::new(json_data.as_slice()); + let registry = ExtensionRegistry::default(); + let (_, package) = read_envelope(reader, ®istry).unwrap(); + + let mut model_data = Vec::new(); + hugr::envelope::write_envelope(&mut model_data, &package, config_model).unwrap(); + + // Convert back to JSON + let reader = BufReader::new(model_data.as_slice()); + let (_, package_back) = read_envelope(reader, ®istry).unwrap(); + + // Package should be the same after roundtrip conversion + assert_eq!(test_package, package_back); +} + +#[rstest] +fn test_convert_text_flag(test_envelope_text: (String, Package), mut convert_cmd: Command) { + convert_cmd.args(["-", "--text"]); + convert_cmd.write_stdin(test_envelope_text.0); + + let output = convert_cmd.assert().success().get_output().to_owned(); + let stdout = output.stdout.clone(); + + let reader = BufReader::new(stdout.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify it's a text-based format + assert!(config.format.ascii_printable()); +} + +#[rstest] +fn test_convert_binary_flag(test_envelope_text: (String, Package), mut convert_cmd: Command) { + convert_cmd.args(["-", "--binary"]); + convert_cmd.write_stdin(test_envelope_text.0); + + let output = convert_cmd.assert().success().get_output().to_owned(); + let stdout = output.stdout.clone(); + + let reader = BufReader::new(stdout.as_slice()); + let registry = ExtensionRegistry::default(); + let (config, _) = read_envelope(reader, ®istry).expect("Failed to read output envelope"); + + // Verify it's a binary format (not ASCII printable) + assert!(!config.format.ascii_printable()); + assert!(config.zstd.is_some()); +} + +#[rstest] +fn test_format_conflicts(mut convert_cmd: Command) { + // Test that --format and --text cannot be combined + convert_cmd.args(["-", "--format", "json", "--text"]); + + // Should fail due to conflicting options + convert_cmd + .assert() + .failure() + .stderr(contains("cannot be used with")); + + // Test that --text and --binary cannot be combined + let mut convert_cmd = Command::cargo_bin("hugr").unwrap(); + convert_cmd.arg("convert"); + convert_cmd.args(["-", "--text", "--binary"]); + + // Should fail due to conflicting options + convert_cmd + .assert() + .failure() + .stderr(contains("cannot be used with")); +} From d7c5d3f3db67e488fe7de20245325be8b3ea9be1 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Fri, 13 Jun 2025 14:04:05 +0100 Subject: [PATCH 11/22] feat(core, llvm): add array unpack operations (#2339) Dual of new_array Closes #1947 --- .../src/std_extensions/collections/array.rs | 28 +- .../collections/array/array_op.rs | 39 +- .../collections/array/op_builder.rs | 37 + .../std_extensions/collections/value_array.rs | 2 +- hugr-llvm/src/extension/collections/array.rs | 98 +++ ...ons__array__test__emit_all_ops@llvm14.snap | 392 ++++----- ...test__emit_all_ops@pre-mem2reg@llvm14.snap | 775 +++++++++--------- ...tack_array__test__emit_all_ops@llvm14.snap | 364 ++++---- ...test__emit_all_ops@pre-mem2reg@llvm14.snap | 749 ++++++++--------- .../src/extension/collections/stack_array.rs | 57 ++ .../std/_json_defs/collections/array.json | 9 +- .../_json_defs/collections/value_array.json | 9 +- .../std_extensions/collections/array.json | 9 +- .../collections/value_array.json | 9 +- 14 files changed, 1452 insertions(+), 1125 deletions(-) diff --git a/hugr-core/src/std_extensions/collections/array.rs b/hugr-core/src/std_extensions/collections/array.rs index 04360ba345..eb31441453 100644 --- a/hugr-core/src/std_extensions/collections/array.rs +++ b/hugr-core/src/std_extensions/collections/array.rs @@ -43,7 +43,7 @@ pub const ARRAY_VALUENAME: TypeName = TypeName::new_inline("array"); /// Reported unique name of the extension pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("collections.array"); /// Extension version. -pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); +pub const VERSION: semver::Version = semver::Version::new(0, 1, 1); /// A linear, fixed-length collection of values. /// @@ -197,7 +197,31 @@ pub trait ArrayOpBuilder: GenericArrayOpBuilder { ) -> Result { self.add_new_generic_array::(elem_ty, values) } - + /// Adds an array unpack operation to the dataflow graph. + /// + /// This operation unpacks an array into individual elements. + /// + /// # Arguments + /// + /// * `elem_ty` - The type of the elements in the array. + /// * `size` - The size of the array. + /// * `input` - The wire representing the array to unpack. + /// + /// # Errors + /// + /// If building the operation fails. + /// + /// # Returns + /// + /// A vector of wires representing the individual elements from the array. + fn add_array_unpack( + &mut self, + elem_ty: Type, + size: u64, + input: Wire, + ) -> Result, BuildError> { + self.add_generic_array_unpack::(elem_ty, size, input) + } /// Adds an array clone operation to the dataflow graph and return the wires /// representing the originala and cloned array. /// diff --git a/hugr-core/src/std_extensions/collections/array/array_op.rs b/hugr-core/src/std_extensions/collections/array/array_op.rs index 3df679d591..915603c1da 100644 --- a/hugr-core/src/std_extensions/collections/array/array_op.rs +++ b/hugr-core/src/std_extensions/collections/array/array_op.rs @@ -21,7 +21,7 @@ use crate::utils::Never; use super::array_kind::ArrayKind; -/// Array operation definitions. Generic over the conrete array implementation. +/// Array operation definitions. Generic over the concrete array implementation. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, IntoStaticStr, EnumIter, EnumString)] #[allow(non_camel_case_types)] #[non_exhaustive] @@ -58,6 +58,10 @@ pub enum GenericArrayOpDef { /// references `AK` to ensure that the type parameter is used. #[strum(disabled)] _phantom(PhantomData, Never), + /// Unpacks an array into its individual elements: + /// `unpack: array -> (elemty)^SIZE` + /// where `SIZE` must be statically known (not a variable) + unpack, } /// Static parameters for array operations. Includes array size. Type is part of the type scheme. @@ -76,6 +80,10 @@ impl SignatureFromArgs for GenericArrayOpDef { params, FuncValueType::new(vec![elem_ty_var.clone(); n as usize], array_ty), ), + GenericArrayOpDef::unpack => PolyFuncTypeRV::new( + params, + FuncValueType::new(array_ty, vec![elem_ty_var.clone(); n as usize]), + ), GenericArrayOpDef::pop_left | GenericArrayOpDef::pop_right => { let popped_array_ty = AK::ty(n - 1, elem_ty_var.clone()); PolyFuncTypeRV::new( @@ -124,9 +132,9 @@ impl GenericArrayOpDef { _extension_ref: &Weak, ) -> SignatureFunc { use GenericArrayOpDef::{ - _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, + _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, unpack, }; - if let new_array | pop_left | pop_right = self { + if let new_array | unpack | pop_left | pop_right = self { // implements SignatureFromArgs // signature computed dynamically, so can rely on type definition in extension. (*self).into() @@ -184,7 +192,7 @@ impl GenericArrayOpDef { ), ), _phantom(_, never) => match *never {}, - new_array | pop_left | pop_right => unreachable!(), + new_array | unpack | pop_left | pop_right => unreachable!(), } .into() } @@ -218,6 +226,7 @@ impl MakeOpDef for GenericArrayOpDef { fn description(&self) -> String { match self { GenericArrayOpDef::new_array => "Create a new array from elements", + GenericArrayOpDef::unpack => "Unpack an array into its elements", GenericArrayOpDef::get => "Get an element from an array", GenericArrayOpDef::set => "Set an element in an array", GenericArrayOpDef::swap => "Swap two elements in an array", @@ -250,7 +259,7 @@ impl MakeOpDef for GenericArrayOpDef { } #[derive(Clone, Debug, PartialEq)] -/// Concrete array operation. Generic over the actual array implemenation. +/// Concrete array operation. Generic over the actual array implementation. pub struct GenericArrayOp { /// The operation definition. pub def: GenericArrayOpDef, @@ -275,7 +284,7 @@ impl MakeExtensionOp for GenericArrayOp { fn type_args(&self) -> Vec { use GenericArrayOpDef::{ - _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, + _phantom, discard_empty, get, new_array, pop_left, pop_right, set, swap, unpack, }; let ty_arg = TypeArg::Type { ty: self.elem_ty.clone(), @@ -288,7 +297,7 @@ impl MakeExtensionOp for GenericArrayOp { ); vec![ty_arg] } - new_array | pop_left | pop_right | get | set | swap => { + new_array | unpack | pop_left | pop_right | get | set | swap => { vec![TypeArg::BoundedNat { n: self.size }, ty_arg] } _phantom(_, never) => match never {}, @@ -379,6 +388,22 @@ mod tests { b.finish_hugr_with_outputs(out.outputs()).unwrap(); } + #[rstest] + #[case(Array)] + #[case(ValueArray)] + /// Test building a HUGR involving an unpack operation. + fn test_unpack(#[case] _kind: AK) { + let mut b = DFGBuilder::new(inout_sig(AK::ty(2, qb_t()), vec![qb_t(), qb_t()])).unwrap(); + + let [array] = b.input_wires_arr(); + + let op = GenericArrayOpDef::::unpack.to_concrete(qb_t(), 2); + + let out = b.add_dataflow_op(op, [array]).unwrap(); + + b.finish_hugr_with_outputs(out.outputs()).unwrap(); + } + #[rstest] #[case(Array)] #[case(ValueArray)] diff --git a/hugr-core/src/std_extensions/collections/array/op_builder.rs b/hugr-core/src/std_extensions/collections/array/op_builder.rs index 9f913e753d..2740673f80 100644 --- a/hugr-core/src/std_extensions/collections/array/op_builder.rs +++ b/hugr-core/src/std_extensions/collections/array/op_builder.rs @@ -49,6 +49,32 @@ pub trait GenericArrayOpBuilder: Dataflow { Ok(out) } + /// Adds an array unpack operation to the dataflow graph. + /// + /// This operation unpacks an array into individual elements. + /// + /// # Arguments + /// + /// * `elem_ty` - The type of the elements in the array. + /// * `size` - The size of the array. + /// * `input` - The wire representing the array. + /// + /// # Errors + /// + /// Returns an error if building the operation fails. + /// + /// # Returns + /// + /// A vector of wires representing the individual elements of the array. + fn add_generic_array_unpack( + &mut self, + elem_ty: Type, + size: u64, + input: Wire, + ) -> Result, BuildError> { + let op = GenericArrayOpDef::::unpack.instantiate(&[size.into(), elem_ty.into()])?; + Ok(self.add_dataflow_op(op, vec![input])?.outputs().collect()) + } /// Adds an array clone operation to the dataflow graph and return the wires /// representing the originala and cloned array. /// @@ -283,6 +309,17 @@ pub fn build_all_array_ops_generic(mut builder: B) - let us0 = builder.add_load_value(ConstUsize::new(0)); let us1 = builder.add_load_value(ConstUsize::new(1)); let us2 = builder.add_load_value(ConstUsize::new(2)); + let arr = builder + .add_new_generic_array::(usize_t(), [us1, us2]) + .unwrap(); + + // Add array unpack operation + let [_us1, _us2] = builder + .add_generic_array_unpack::(usize_t(), 2, arr) + .unwrap() + .try_into() + .unwrap(); + let arr = builder .add_new_generic_array::(usize_t(), [us1, us2]) .unwrap(); diff --git a/hugr-core/src/std_extensions/collections/value_array.rs b/hugr-core/src/std_extensions/collections/value_array.rs index a9668931cd..fe89824d77 100644 --- a/hugr-core/src/std_extensions/collections/value_array.rs +++ b/hugr-core/src/std_extensions/collections/value_array.rs @@ -31,7 +31,7 @@ pub const VALUE_ARRAY_VALUENAME: TypeName = TypeName::new_inline("value_array"); /// Reported unique name of the extension pub const EXTENSION_ID: ExtensionId = ExtensionId::new_static_unchecked("collections.value_array"); /// Extension version. -pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); +pub const VERSION: semver::Version = semver::Version::new(0, 1, 1); /// A fixed-length collection of values. /// diff --git a/hugr-llvm/src/extension/collections/array.rs b/hugr-llvm/src/extension/collections/array.rs index 2a602e291c..725fbe2724 100644 --- a/hugr-llvm/src/extension/collections/array.rs +++ b/hugr-llvm/src/extension/collections/array.rs @@ -455,6 +455,24 @@ pub fn emit_array_op<'c, H: HugrView>( } outputs.finish(ctx.builder(), [array_v.into()]) } + ArrayOpDef::unpack => { + let [array_v] = inputs + .try_into() + .map_err(|_| anyhow!("ArrayOpDef::unpack expects one argument"))?; + let (array_ptr, array_offset) = decompose_array_fat_pointer(builder, array_v)?; + + let mut result = Vec::with_capacity(size as usize); + let usize_t = usize_ty(&ctx.typing_session()); + + for i in 0..size { + let idx = builder.build_int_add(array_offset, usize_t.const_int(i, false), "")?; + let elem_addr = unsafe { builder.build_in_bounds_gep(array_ptr, &[idx], "")? }; + let elem_v = builder.build_load(elem_addr, "")?; + result.push(elem_v); + } + + outputs.finish(ctx.builder(), result) + } ArrayOpDef::get => { let [array_v, index_v] = inputs .try_into() @@ -999,6 +1017,41 @@ mod test { check_emission!(hugr, llvm_ctx); } + // #[rstest] + // #[case(1, 2, 3)] + // #[case(0, 0, 0)] + // #[case(10, 20, 30)] + // fn exec_unpack_and_sum(mut exec_ctx: TestContext, #[case] a: u64, #[case] b: u64, #[case] expected: u64) { + // let hugr = SimpleHugrConfig::new() + // .with_extensions(exec_registry()) + // .with_outs(vec![usize_t()]) + // .finish(|mut builder| { + // // Create an array with the test values + // let values = vec![ConstUsize::new(a).into(), ConstUsize::new(b).into()]; + // let arr = builder.add_load_value(array::ArrayValue::new(usize_t(), values)); + + // // Unpack the array + // let [val_a, val_b] = builder.add_array_unpack(usize_t(), 2, arr).unwrap().try_into().unwrap(); + + // // Add the values + // let sum = { + // let int_ty = int_type(6); + // let a_int = builder.cast(val_a, int_ty.clone()).unwrap(); + // let b_int = builder.cast(val_b, int_ty.clone()).unwrap(); + // let sum_int = builder.add_iadd(6, a_int, b_int).unwrap(); + // builder.cast(sum_int, usize_t()).unwrap() + // }; + + // builder.finish_hugr_with_outputs([sum]).unwrap() + // }); + // exec_ctx.add_extensions(|cge| { + // cge.add_default_prelude_extensions() + // .add_default_array_extensions() + // .add_default_int_extensions() + // }); + // assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); + // } + fn exec_registry() -> ExtensionRegistry { ExtensionRegistry::new([ int_types::EXTENSION.to_owned(), @@ -1398,6 +1451,51 @@ mod test { assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); } + #[rstest] + #[case(&[], 0)] + #[case(&[1, 2], 3)] + #[case(&[6, 6, 6], 18)] + fn exec_unpack( + mut exec_ctx: TestContext, + #[case] array_contents: &[u64], + #[case] expected: u64, + ) { + // We build a HUGR that: + // - Loads an array with the given contents + // - Unpacks all the elements + // - Returns the sum of the elements + + let int_ty = int_type(6); + let hugr = SimpleHugrConfig::new() + .with_outs(int_ty.clone()) + .with_extensions(exec_registry()) + .finish(|mut builder| { + let array = array::ArrayValue::new( + int_ty.clone(), + array_contents + .iter() + .map(|&i| ConstInt::new_u(6, i).unwrap().into()) + .collect_vec(), + ); + let array = builder.add_load_value(array); + let unpacked = builder + .add_array_unpack(int_ty.clone(), array_contents.len() as u64, array) + .unwrap(); + let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap()); + for elem in unpacked { + r = builder.add_iadd(6, r, elem).unwrap(); + } + + builder.finish_with_outputs([r]).unwrap() + }); + exec_ctx.add_extensions(|cge| { + cge.add_default_prelude_extensions() + .add_default_array_extensions() + .add_default_int_extensions() + }); + assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); + } + #[rstest] #[case(5, 42, 0)] #[case(5, 42, 1)] diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap index 3f4d8cecb2..c2ed6a10df 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@llvm14.snap @@ -29,220 +29,236 @@ entry_block: ; preds = %alloca_block store i64 1, i64* %4, align 4 %5 = getelementptr inbounds i64, i64* %1, i64 1 store i64 2, i64* %5, align 4 - %array_ptr = extractvalue { i64*, i64 } %3, 0 - %array_offset = extractvalue { i64*, i64 } %3, 1 - %6 = icmp ult i64 0, 2 - %7 = icmp ult i64 1, 2 - %8 = and i1 %6, %7 - br i1 %8, label %11, label %9 - -9: ; preds = %entry_block - %10 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %3, 1 - br label %19 - -11: ; preds = %entry_block - %12 = add i64 0, %array_offset - %13 = add i64 1, %array_offset - %14 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 - %15 = load i64, i64* %14, align 4 - %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %13 + %6 = call i8* @malloc(i64 mul (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2)) + %7 = bitcast i8* %6 to i64* + %8 = insertvalue { i64*, i64 } poison, i64* %7, 0 + %9 = insertvalue { i64*, i64 } %8, i64 0, 1 + %10 = getelementptr inbounds i64, i64* %7, i64 0 + store i64 1, i64* %10, align 4 + %11 = getelementptr inbounds i64, i64* %7, i64 1 + store i64 2, i64* %11, align 4 + %array_ptr = extractvalue { i64*, i64 } %9, 0 + %array_offset = extractvalue { i64*, i64 } %9, 1 + %12 = add i64 %array_offset, 0 + %13 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 + %14 = load i64, i64* %13, align 4 + %15 = add i64 %array_offset, 1 + %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %15 %17 = load i64, i64* %16, align 4 - store i64 %17, i64* %14, align 4 - store i64 %15, i64* %16, align 4 - %18 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %3, 1 - br label %19 - -19: ; preds = %9, %11 - %"0.0" = phi { i1, { i64*, i64 } } [ %18, %11 ], [ %10, %9 ] - %20 = extractvalue { i1, { i64*, i64 } } %"0.0", 0 - switch i1 %20, label %21 [ - i1 true, label %23 + %array_ptr9 = extractvalue { i64*, i64 } %3, 0 + %array_offset10 = extractvalue { i64*, i64 } %3, 1 + %18 = icmp ult i64 0, 2 + %19 = icmp ult i64 1, 2 + %20 = and i1 %18, %19 + br i1 %20, label %23, label %21 + +21: ; preds = %entry_block + %22 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %3, 1 + br label %31 + +23: ; preds = %entry_block + %24 = add i64 0, %array_offset10 + %25 = add i64 1, %array_offset10 + %26 = getelementptr inbounds i64, i64* %array_ptr9, i64 %24 + %27 = load i64, i64* %26, align 4 + %28 = getelementptr inbounds i64, i64* %array_ptr9, i64 %25 + %29 = load i64, i64* %28, align 4 + store i64 %29, i64* %26, align 4 + store i64 %27, i64* %28, align 4 + %30 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %3, 1 + br label %31 + +31: ; preds = %21, %23 + %"0.0" = phi { i1, { i64*, i64 } } [ %30, %23 ], [ %22, %21 ] + %32 = extractvalue { i1, { i64*, i64 } } %"0.0", 0 + switch i1 %32, label %33 [ + i1 true, label %35 ] -21: ; preds = %19 - %22 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 - br label %cond_16_case_0 +33: ; preds = %31 + %34 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 + br label %cond_19_case_0 -23: ; preds = %19 - %24 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 - br label %cond_16_case_1 +35: ; preds = %31 + %36 = extractvalue { i1, { i64*, i64 } } %"0.0", 1 + br label %cond_19_case_1 -cond_16_case_0: ; preds = %21 - %25 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 - %26 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 - %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) +cond_19_case_0: ; preds = %33 + %37 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 + %38 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 + %39 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %37, i8* %38) call void @abort() - br label %cond_exit_16 - -cond_16_case_1: ; preds = %23 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"08.0" = phi { i64*, i64 } [ zeroinitializer, %cond_16_case_0 ], [ %24, %cond_16_case_1 ] - %array_ptr20 = extractvalue { i64*, i64 } %"08.0", 0 - %array_offset21 = extractvalue { i64*, i64 } %"08.0", 1 - %28 = icmp ult i64 0, 2 - br i1 %28, label %30, label %29 - -29: ; preds = %cond_exit_16 - br label %35 - -30: ; preds = %cond_exit_16 - %31 = add i64 0, %array_offset21 - %32 = getelementptr inbounds i64, i64* %array_ptr20, i64 %31 - %33 = load i64, i64* %32, align 4 - %34 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %33, 1 - br label %35 - -35: ; preds = %29, %30 - %"022.0" = phi { i1, i64 } [ %34, %30 ], [ { i1 false, i64 poison }, %29 ] - %36 = extractvalue { i1, i64 } %"022.0", 0 - switch i1 %36, label %37 [ - i1 true, label %38 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %35 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"013.0" = phi { i64*, i64 } [ zeroinitializer, %cond_19_case_0 ], [ %36, %cond_19_case_1 ] + %array_ptr25 = extractvalue { i64*, i64 } %"013.0", 0 + %array_offset26 = extractvalue { i64*, i64 } %"013.0", 1 + %40 = icmp ult i64 0, 2 + br i1 %40, label %42, label %41 + +41: ; preds = %cond_exit_19 + br label %47 + +42: ; preds = %cond_exit_19 + %43 = add i64 0, %array_offset26 + %44 = getelementptr inbounds i64, i64* %array_ptr25, i64 %43 + %45 = load i64, i64* %44, align 4 + %46 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %45, 1 + br label %47 + +47: ; preds = %41, %42 + %"027.0" = phi { i1, i64 } [ %46, %42 ], [ { i1 false, i64 poison }, %41 ] + %48 = extractvalue { i1, i64 } %"027.0", 0 + switch i1 %48, label %49 [ + i1 true, label %50 ] -37: ; preds = %35 - br label %cond_28_case_0 +49: ; preds = %47 + br label %cond_31_case_0 -38: ; preds = %35 - %39 = extractvalue { i1, i64 } %"022.0", 1 - br label %cond_28_case_1 +50: ; preds = %47 + %51 = extractvalue { i1, i64 } %"027.0", 1 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %37 - %40 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 - %41 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 - %42 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %40, i8* %41) +cond_31_case_0: ; preds = %49 + %52 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 + %53 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 + %54 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %52, i8* %53) call void @abort() - br label %cond_exit_28 - -cond_28_case_1: ; preds = %38 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"026.0" = phi i64 [ 0, %cond_28_case_0 ], [ %39, %cond_28_case_1 ] - %array_ptr36 = extractvalue { i64*, i64 } %"08.0", 0 - %array_offset37 = extractvalue { i64*, i64 } %"08.0", 1 - %43 = icmp ult i64 1, 2 - br i1 %43, label %47, label %44 - -44: ; preds = %cond_exit_28 - %45 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"026.0", 2 - %46 = insertvalue { i1, { i64*, i64 }, i64 } %45, { i64*, i64 } %"08.0", 1 - br label %53 - -47: ; preds = %cond_exit_28 - %48 = add i64 1, %array_offset37 - %49 = getelementptr inbounds i64, i64* %array_ptr36, i64 %48 - %50 = load i64, i64* %49, align 4 - store i64 %"026.0", i64* %49, align 4 - %51 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %50, 2 - %52 = insertvalue { i1, { i64*, i64 }, i64 } %51, { i64*, i64 } %"08.0", 1 - br label %53 - -53: ; preds = %44, %47 - %"038.0" = phi { i1, { i64*, i64 }, i64 } [ %52, %47 ], [ %46, %44 ] - %54 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 0 - switch i1 %54, label %55 [ - i1 true, label %58 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %50 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 + %"031.0" = phi i64 [ 0, %cond_31_case_0 ], [ %51, %cond_31_case_1 ] + %array_ptr41 = extractvalue { i64*, i64 } %"013.0", 0 + %array_offset42 = extractvalue { i64*, i64 } %"013.0", 1 + %55 = icmp ult i64 1, 2 + br i1 %55, label %59, label %56 + +56: ; preds = %cond_exit_31 + %57 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"031.0", 2 + %58 = insertvalue { i1, { i64*, i64 }, i64 } %57, { i64*, i64 } %"013.0", 1 + br label %65 + +59: ; preds = %cond_exit_31 + %60 = add i64 1, %array_offset42 + %61 = getelementptr inbounds i64, i64* %array_ptr41, i64 %60 + %62 = load i64, i64* %61, align 4 + store i64 %"031.0", i64* %61, align 4 + %63 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %62, 2 + %64 = insertvalue { i1, { i64*, i64 }, i64 } %63, { i64*, i64 } %"013.0", 1 + br label %65 + +65: ; preds = %56, %59 + %"043.0" = phi { i1, { i64*, i64 }, i64 } [ %64, %59 ], [ %58, %56 ] + %66 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 0 + switch i1 %66, label %67 [ + i1 true, label %70 ] -55: ; preds = %53 - %56 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 2 - %57 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 1 - br label %cond_39_case_0 +67: ; preds = %65 + %68 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 2 + %69 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 1 + br label %cond_42_case_0 -58: ; preds = %53 - %59 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 2 - %60 = extractvalue { i1, { i64*, i64 }, i64 } %"038.0", 1 - br label %cond_39_case_1 +70: ; preds = %65 + %71 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 2 + %72 = extractvalue { i1, { i64*, i64 }, i64 } %"043.0", 1 + br label %cond_42_case_1 -cond_39_case_0: ; preds = %55 - %61 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 - %62 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 - %63 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %61, i8* %62) +cond_42_case_0: ; preds = %67 + %73 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 + %74 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 + %75 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %73, i8* %74) call void @abort() - br label %cond_exit_39 - -cond_39_case_1: ; preds = %58 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"041.0" = phi i64 [ 0, %cond_39_case_0 ], [ %59, %cond_39_case_1 ] - %"142.0" = phi { i64*, i64 } [ zeroinitializer, %cond_39_case_0 ], [ %60, %cond_39_case_1 ] - %array_ptr61 = extractvalue { i64*, i64 } %"142.0", 0 - %array_offset62 = extractvalue { i64*, i64 } %"142.0", 1 - %new_offset = add i64 %array_offset62, 1 - %64 = getelementptr inbounds i64, i64* %array_ptr61, i64 %array_offset62 - %65 = load i64, i64* %64, align 4 - %66 = insertvalue { i64*, i64 } poison, i64* %array_ptr61, 0 - %67 = insertvalue { i64*, i64 } %66, i64 %new_offset, 1 - %68 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %65, 2 - %69 = insertvalue { i1, { i64*, i64 }, i64 } %68, { i64*, i64 } %67, 1 - %70 = extractvalue { i1, { i64*, i64 }, i64 } %69, 0 - switch i1 %70, label %71 [ - i1 true, label %72 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %70 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 + %"046.0" = phi i64 [ 0, %cond_42_case_0 ], [ %71, %cond_42_case_1 ] + %"147.0" = phi { i64*, i64 } [ zeroinitializer, %cond_42_case_0 ], [ %72, %cond_42_case_1 ] + %array_ptr66 = extractvalue { i64*, i64 } %"147.0", 0 + %array_offset67 = extractvalue { i64*, i64 } %"147.0", 1 + %new_offset = add i64 %array_offset67, 1 + %76 = getelementptr inbounds i64, i64* %array_ptr66, i64 %array_offset67 + %77 = load i64, i64* %76, align 4 + %78 = insertvalue { i64*, i64 } poison, i64* %array_ptr66, 0 + %79 = insertvalue { i64*, i64 } %78, i64 %new_offset, 1 + %80 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %77, 2 + %81 = insertvalue { i1, { i64*, i64 }, i64 } %80, { i64*, i64 } %79, 1 + %82 = extractvalue { i1, { i64*, i64 }, i64 } %81, 0 + switch i1 %82, label %83 [ + i1 true, label %84 ] -71: ; preds = %cond_exit_39 - br label %cond_50_case_0 +83: ; preds = %cond_exit_42 + br label %cond_53_case_0 -72: ; preds = %cond_exit_39 - %73 = extractvalue { i1, { i64*, i64 }, i64 } %69, 2 - %74 = extractvalue { i1, { i64*, i64 }, i64 } %69, 1 - br label %cond_50_case_1 +84: ; preds = %cond_exit_42 + %85 = extractvalue { i1, { i64*, i64 }, i64 } %81, 2 + %86 = extractvalue { i1, { i64*, i64 }, i64 } %81, 1 + br label %cond_53_case_1 -cond_50_case_0: ; preds = %71 - %75 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 - %76 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 - %77 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %75, i8* %76) +cond_53_case_0: ; preds = %83 + %87 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 + %88 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 + %89 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %87, i8* %88) call void @abort() - br label %cond_exit_50 - -cond_50_case_1: ; preds = %72 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"064.0" = phi i64 [ 0, %cond_50_case_0 ], [ %73, %cond_50_case_1 ] - %"165.0" = phi { i64*, i64 } [ zeroinitializer, %cond_50_case_0 ], [ %74, %cond_50_case_1 ] - %array_ptr78 = extractvalue { i64*, i64 } %"165.0", 0 - %array_offset79 = extractvalue { i64*, i64 } %"165.0", 1 - %78 = add i64 %array_offset79, 0 - %79 = getelementptr inbounds i64, i64* %array_ptr78, i64 %78 - %80 = load i64, i64* %79, align 4 - %81 = insertvalue { i64*, i64 } poison, i64* %array_ptr78, 0 - %82 = insertvalue { i64*, i64 } %81, i64 %array_offset79, 1 - %83 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %80, 2 - %84 = insertvalue { i1, { i64*, i64 }, i64 } %83, { i64*, i64 } %82, 1 - %85 = extractvalue { i1, { i64*, i64 }, i64 } %84, 0 - switch i1 %85, label %86 [ - i1 true, label %87 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %84 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"069.0" = phi i64 [ 0, %cond_53_case_0 ], [ %85, %cond_53_case_1 ] + %"170.0" = phi { i64*, i64 } [ zeroinitializer, %cond_53_case_0 ], [ %86, %cond_53_case_1 ] + %array_ptr83 = extractvalue { i64*, i64 } %"170.0", 0 + %array_offset84 = extractvalue { i64*, i64 } %"170.0", 1 + %90 = add i64 %array_offset84, 0 + %91 = getelementptr inbounds i64, i64* %array_ptr83, i64 %90 + %92 = load i64, i64* %91, align 4 + %93 = insertvalue { i64*, i64 } poison, i64* %array_ptr83, 0 + %94 = insertvalue { i64*, i64 } %93, i64 %array_offset84, 1 + %95 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %92, 2 + %96 = insertvalue { i1, { i64*, i64 }, i64 } %95, { i64*, i64 } %94, 1 + %97 = extractvalue { i1, { i64*, i64 }, i64 } %96, 0 + switch i1 %97, label %98 [ + i1 true, label %99 ] -86: ; preds = %cond_exit_50 - br label %cond_61_case_0 +98: ; preds = %cond_exit_53 + br label %cond_64_case_0 -87: ; preds = %cond_exit_50 - %88 = extractvalue { i1, { i64*, i64 }, i64 } %84, 2 - %89 = extractvalue { i1, { i64*, i64 }, i64 } %84, 1 - br label %cond_61_case_1 +99: ; preds = %cond_exit_53 + %100 = extractvalue { i1, { i64*, i64 }, i64 } %96, 2 + %101 = extractvalue { i1, { i64*, i64 }, i64 } %96, 1 + br label %cond_64_case_1 -cond_61_case_0: ; preds = %86 - %90 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 - %91 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 - %92 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %90, i8* %91) +cond_64_case_0: ; preds = %98 + %102 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 + %103 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 + %104 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %102, i8* %103) call void @abort() - br label %cond_exit_61 - -cond_61_case_1: ; preds = %87 - br label %cond_exit_61 - -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"081.0" = phi i64 [ 0, %cond_61_case_0 ], [ %88, %cond_61_case_1 ] - %"182.0" = phi { i64*, i64 } [ zeroinitializer, %cond_61_case_0 ], [ %89, %cond_61_case_1 ] - %array_ptr95 = extractvalue { i64*, i64 } %"182.0", 0 - %array_offset96 = extractvalue { i64*, i64 } %"182.0", 1 - %93 = bitcast i64* %array_ptr95 to i8* - call void @free(i8* %93) + br label %cond_exit_64 + +cond_64_case_1: ; preds = %99 + br label %cond_exit_64 + +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"086.0" = phi i64 [ 0, %cond_64_case_0 ], [ %100, %cond_64_case_1 ] + %"187.0" = phi { i64*, i64 } [ zeroinitializer, %cond_64_case_0 ], [ %101, %cond_64_case_1 ] + %array_ptr100 = extractvalue { i64*, i64 } %"187.0", 0 + %array_offset101 = extractvalue { i64*, i64 } %"187.0", 1 + %105 = bitcast i64* %array_ptr100 to i8* + call void @free(i8* %105) ret void } diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap index c4b46a02a8..8224af5a5c 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__array__test__emit_all_ops@pre-mem2reg@llvm14.snap @@ -20,69 +20,72 @@ define void @_hl.main.1() { alloca_block: %"12_0" = alloca i64, align 8 %"10_0" = alloca i64, align 8 + %"15_0" = alloca { i64*, i64 }, align 8 %"13_0" = alloca { i64*, i64 }, align 8 + %"14_0" = alloca i64, align 8 + %"14_1" = alloca i64, align 8 %"8_0" = alloca i64, align 8 - %"14_0" = alloca { i1, { i64*, i64 } }, align 8 + %"18_0" = alloca { i1, { i64*, i64 } }, align 8 %"0" = alloca { i1, { i64*, i64 } }, align 8 - %"16_0" = alloca { i64*, i64 }, align 8 - %"08" = alloca { i64*, i64 }, align 8 - %"010" = alloca { i64*, i64 }, align 8 - %"21_0" = alloca { i32, i8* }, align 8 - %"18_0" = alloca { i64*, i64 }, align 8 - %"22_0" = alloca { i64*, i64 }, align 8 + %"19_0" = alloca { i64*, i64 }, align 8 + %"013" = alloca { i64*, i64 }, align 8 %"015" = alloca { i64*, i64 }, align 8 - %"24_0" = alloca { i64*, i64 }, align 8 - %"26_0" = alloca { i1, i64 }, align 8 - %"26_1" = alloca { i64*, i64 }, align 8 - %"022" = alloca { i1, i64 }, align 8 + %"24_0" = alloca { i32, i8* }, align 8 + %"21_0" = alloca { i64*, i64 }, align 8 + %"25_0" = alloca { i64*, i64 }, align 8 + %"020" = alloca { i64*, i64 }, align 8 + %"27_0" = alloca { i64*, i64 }, align 8 + %"29_0" = alloca { i1, i64 }, align 8 + %"29_1" = alloca { i64*, i64 }, align 8 + %"027" = alloca { i1, i64 }, align 8 %"1" = alloca { i64*, i64 }, align 8 - %"28_0" = alloca i64, align 8 - %"026" = alloca i64, align 8 - %"33_0" = alloca { i32, i8* }, align 8 - %"34_0" = alloca i64, align 8 - %"030" = alloca i64, align 8 - %"36_0" = alloca i64, align 8 - %"38_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 - %"038" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"31_0" = alloca i64, align 8 + %"031" = alloca i64, align 8 + %"36_0" = alloca { i32, i8* }, align 8 + %"37_0" = alloca i64, align 8 + %"035" = alloca i64, align 8 %"39_0" = alloca i64, align 8 - %"39_1" = alloca { i64*, i64 }, align 8 - %"041" = alloca i64, align 8 - %"142" = alloca { i64*, i64 }, align 8 - %"045" = alloca i64, align 8 - %"146" = alloca { i64*, i64 }, align 8 - %"44_0" = alloca { i32, i8* }, align 8 - %"41_0" = alloca i64, align 8 - %"41_1" = alloca { i64*, i64 }, align 8 - %"45_0" = alloca i64, align 8 - %"45_1" = alloca { i64*, i64 }, align 8 - %"054" = alloca i64, align 8 - %"155" = alloca { i64*, i64 }, align 8 - %"47_0" = alloca i64, align 8 - %"47_1" = alloca { i64*, i64 }, align 8 - %"49_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"41_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"043" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"42_0" = alloca i64, align 8 + %"42_1" = alloca { i64*, i64 }, align 8 + %"046" = alloca i64, align 8 + %"147" = alloca { i64*, i64 }, align 8 + %"050" = alloca i64, align 8 + %"151" = alloca { i64*, i64 }, align 8 + %"47_0" = alloca { i32, i8* }, align 8 + %"44_0" = alloca i64, align 8 + %"44_1" = alloca { i64*, i64 }, align 8 + %"48_0" = alloca i64, align 8 + %"48_1" = alloca { i64*, i64 }, align 8 + %"059" = alloca i64, align 8 + %"160" = alloca { i64*, i64 }, align 8 %"50_0" = alloca i64, align 8 %"50_1" = alloca { i64*, i64 }, align 8 - %"064" = alloca i64, align 8 - %"165" = alloca { i64*, i64 }, align 8 - %"55_0" = alloca { i32, i8* }, align 8 - %"56_0" = alloca i64, align 8 - %"56_1" = alloca { i64*, i64 }, align 8 - %"071" = alloca i64, align 8 - %"172" = alloca { i64*, i64 }, align 8 - %"58_0" = alloca i64, align 8 - %"58_1" = alloca { i64*, i64 }, align 8 - %"60_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"52_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"53_0" = alloca i64, align 8 + %"53_1" = alloca { i64*, i64 }, align 8 + %"069" = alloca i64, align 8 + %"170" = alloca { i64*, i64 }, align 8 + %"58_0" = alloca { i32, i8* }, align 8 + %"59_0" = alloca i64, align 8 + %"59_1" = alloca { i64*, i64 }, align 8 + %"076" = alloca i64, align 8 + %"177" = alloca { i64*, i64 }, align 8 %"61_0" = alloca i64, align 8 %"61_1" = alloca { i64*, i64 }, align 8 - %"081" = alloca i64, align 8 - %"182" = alloca { i64*, i64 }, align 8 - %"66_0" = alloca { i32, i8* }, align 8 - %"67_0" = alloca i64, align 8 - %"67_1" = alloca { i64*, i64 }, align 8 - %"088" = alloca i64, align 8 - %"189" = alloca { i64*, i64 }, align 8 - %"69_0" = alloca i64, align 8 - %"69_1" = alloca { i64*, i64 }, align 8 + %"63_0" = alloca { i1, { i64*, i64 }, i64 }, align 8 + %"64_0" = alloca i64, align 8 + %"64_1" = alloca { i64*, i64 }, align 8 + %"086" = alloca i64, align 8 + %"187" = alloca { i64*, i64 }, align 8 + %"69_0" = alloca { i32, i8* }, align 8 + %"70_0" = alloca i64, align 8 + %"70_1" = alloca { i64*, i64 }, align 8 + %"093" = alloca i64, align 8 + %"194" = alloca { i64*, i64 }, align 8 + %"72_0" = alloca i64, align 8 + %"72_1" = alloca { i64*, i64 }, align 8 br label %entry_block entry_block: ; preds = %alloca_block @@ -98,347 +101,369 @@ entry_block: ; preds = %alloca_block store i64 %"10_01", i64* %4, align 4 %5 = getelementptr inbounds i64, i64* %1, i64 1 store i64 %"12_02", i64* %5, align 4 - store { i64*, i64 } %3, { i64*, i64 }* %"13_0", align 8 - store i64 0, i64* %"8_0", align 4 - %"13_03" = load { i64*, i64 }, { i64*, i64 }* %"13_0", align 8 - %"8_04" = load i64, i64* %"8_0", align 4 - %"10_05" = load i64, i64* %"10_0", align 4 - %array_ptr = extractvalue { i64*, i64 } %"13_03", 0 - %array_offset = extractvalue { i64*, i64 } %"13_03", 1 - %6 = icmp ult i64 %"8_04", 2 - %7 = icmp ult i64 %"10_05", 2 - %8 = and i1 %6, %7 - br i1 %8, label %11, label %9 - -9: ; preds = %entry_block - %10 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %"13_03", 1 - store { i1, { i64*, i64 } } %10, { i1, { i64*, i64 } }* %"0", align 8 - br label %19 - -11: ; preds = %entry_block - %12 = add i64 %"8_04", %array_offset - %13 = add i64 %"10_05", %array_offset - %14 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 - %15 = load i64, i64* %14, align 4 - %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %13 + store { i64*, i64 } %3, { i64*, i64 }* %"15_0", align 8 + %"10_03" = load i64, i64* %"10_0", align 4 + %"12_04" = load i64, i64* %"12_0", align 4 + %6 = call i8* @malloc(i64 mul (i64 ptrtoint (i64* getelementptr (i64, i64* null, i32 1) to i64), i64 2)) + %7 = bitcast i8* %6 to i64* + %8 = insertvalue { i64*, i64 } poison, i64* %7, 0 + %9 = insertvalue { i64*, i64 } %8, i64 0, 1 + %10 = getelementptr inbounds i64, i64* %7, i64 0 + store i64 %"10_03", i64* %10, align 4 + %11 = getelementptr inbounds i64, i64* %7, i64 1 + store i64 %"12_04", i64* %11, align 4 + store { i64*, i64 } %9, { i64*, i64 }* %"13_0", align 8 + %"13_05" = load { i64*, i64 }, { i64*, i64 }* %"13_0", align 8 + %array_ptr = extractvalue { i64*, i64 } %"13_05", 0 + %array_offset = extractvalue { i64*, i64 } %"13_05", 1 + %12 = add i64 %array_offset, 0 + %13 = getelementptr inbounds i64, i64* %array_ptr, i64 %12 + %14 = load i64, i64* %13, align 4 + %15 = add i64 %array_offset, 1 + %16 = getelementptr inbounds i64, i64* %array_ptr, i64 %15 %17 = load i64, i64* %16, align 4 - store i64 %17, i64* %14, align 4 - store i64 %15, i64* %16, align 4 - %18 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %"13_03", 1 - store { i1, { i64*, i64 } } %18, { i1, { i64*, i64 } }* %"0", align 8 - br label %19 - -19: ; preds = %9, %11 - %"06" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"0", align 8 - store { i1, { i64*, i64 } } %"06", { i1, { i64*, i64 } }* %"14_0", align 8 - %"14_07" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"14_0", align 8 - %20 = extractvalue { i1, { i64*, i64 } } %"14_07", 0 - switch i1 %20, label %21 [ - i1 true, label %23 + store i64 %14, i64* %"14_0", align 4 + store i64 %17, i64* %"14_1", align 4 + store i64 0, i64* %"8_0", align 4 + %"15_06" = load { i64*, i64 }, { i64*, i64 }* %"15_0", align 8 + %"8_07" = load i64, i64* %"8_0", align 4 + %"10_08" = load i64, i64* %"10_0", align 4 + %array_ptr9 = extractvalue { i64*, i64 } %"15_06", 0 + %array_offset10 = extractvalue { i64*, i64 } %"15_06", 1 + %18 = icmp ult i64 %"8_07", 2 + %19 = icmp ult i64 %"10_08", 2 + %20 = and i1 %18, %19 + br i1 %20, label %23, label %21 + +21: ; preds = %entry_block + %22 = insertvalue { i1, { i64*, i64 } } { i1 false, { i64*, i64 } poison }, { i64*, i64 } %"15_06", 1 + store { i1, { i64*, i64 } } %22, { i1, { i64*, i64 } }* %"0", align 8 + br label %31 + +23: ; preds = %entry_block + %24 = add i64 %"8_07", %array_offset10 + %25 = add i64 %"10_08", %array_offset10 + %26 = getelementptr inbounds i64, i64* %array_ptr9, i64 %24 + %27 = load i64, i64* %26, align 4 + %28 = getelementptr inbounds i64, i64* %array_ptr9, i64 %25 + %29 = load i64, i64* %28, align 4 + store i64 %29, i64* %26, align 4 + store i64 %27, i64* %28, align 4 + %30 = insertvalue { i1, { i64*, i64 } } { i1 true, { i64*, i64 } poison }, { i64*, i64 } %"15_06", 1 + store { i1, { i64*, i64 } } %30, { i1, { i64*, i64 } }* %"0", align 8 + br label %31 + +31: ; preds = %21, %23 + %"011" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"0", align 8 + store { i1, { i64*, i64 } } %"011", { i1, { i64*, i64 } }* %"18_0", align 8 + %"18_012" = load { i1, { i64*, i64 } }, { i1, { i64*, i64 } }* %"18_0", align 8 + %32 = extractvalue { i1, { i64*, i64 } } %"18_012", 0 + switch i1 %32, label %33 [ + i1 true, label %35 ] -21: ; preds = %19 - %22 = extractvalue { i1, { i64*, i64 } } %"14_07", 1 - store { i64*, i64 } %22, { i64*, i64 }* %"010", align 8 - br label %cond_16_case_0 - -23: ; preds = %19 - %24 = extractvalue { i1, { i64*, i64 } } %"14_07", 1 - store { i64*, i64 } %24, { i64*, i64 }* %"015", align 8 - br label %cond_16_case_1 - -cond_16_case_0: ; preds = %21 - %"011" = load { i64*, i64 }, { i64*, i64 }* %"010", align 8 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"21_0", align 8 - store { i64*, i64 } %"011", { i64*, i64 }* %"18_0", align 8 - %"21_012" = load { i32, i8* }, { i32, i8* }* %"21_0", align 8 - %"18_013" = load { i64*, i64 }, { i64*, i64 }* %"18_0", align 8 - %25 = extractvalue { i32, i8* } %"21_012", 0 - %26 = extractvalue { i32, i8* } %"21_012", 1 - %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) - call void @abort() - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"22_0", align 8 - %"22_014" = load { i64*, i64 }, { i64*, i64 }* %"22_0", align 8 - store { i64*, i64 } %"22_014", { i64*, i64 }* %"08", align 8 - br label %cond_exit_16 +33: ; preds = %31 + %34 = extractvalue { i1, { i64*, i64 } } %"18_012", 1 + store { i64*, i64 } %34, { i64*, i64 }* %"015", align 8 + br label %cond_19_case_0 -cond_16_case_1: ; preds = %23 +35: ; preds = %31 + %36 = extractvalue { i1, { i64*, i64 } } %"18_012", 1 + store { i64*, i64 } %36, { i64*, i64 }* %"020", align 8 + br label %cond_19_case_1 + +cond_19_case_0: ; preds = %33 %"016" = load { i64*, i64 }, { i64*, i64 }* %"015", align 8 - store { i64*, i64 } %"016", { i64*, i64 }* %"24_0", align 8 - %"24_017" = load { i64*, i64 }, { i64*, i64 }* %"24_0", align 8 - store { i64*, i64 } %"24_017", { i64*, i64 }* %"08", align 8 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"09" = load { i64*, i64 }, { i64*, i64 }* %"08", align 8 - store { i64*, i64 } %"09", { i64*, i64 }* %"16_0", align 8 - %"16_018" = load { i64*, i64 }, { i64*, i64 }* %"16_0", align 8 - %"8_019" = load i64, i64* %"8_0", align 4 - %array_ptr20 = extractvalue { i64*, i64 } %"16_018", 0 - %array_offset21 = extractvalue { i64*, i64 } %"16_018", 1 - %28 = icmp ult i64 %"8_019", 2 - br i1 %28, label %30, label %29 - -29: ; preds = %cond_exit_16 - store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"022", align 4 - store { i64*, i64 } %"16_018", { i64*, i64 }* %"1", align 8 - br label %35 - -30: ; preds = %cond_exit_16 - %31 = add i64 %"8_019", %array_offset21 - %32 = getelementptr inbounds i64, i64* %array_ptr20, i64 %31 - %33 = load i64, i64* %32, align 4 - %34 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %33, 1 - store { i1, i64 } %34, { i1, i64 }* %"022", align 4 - store { i64*, i64 } %"16_018", { i64*, i64 }* %"1", align 8 - br label %35 - -35: ; preds = %29, %30 - %"023" = load { i1, i64 }, { i1, i64 }* %"022", align 4 - %"124" = load { i64*, i64 }, { i64*, i64 }* %"1", align 8 - store { i1, i64 } %"023", { i1, i64 }* %"26_0", align 4 - store { i64*, i64 } %"124", { i64*, i64 }* %"26_1", align 8 - %"26_025" = load { i1, i64 }, { i1, i64 }* %"26_0", align 4 - %36 = extractvalue { i1, i64 } %"26_025", 0 - switch i1 %36, label %37 [ - i1 true, label %38 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"24_0", align 8 + store { i64*, i64 } %"016", { i64*, i64 }* %"21_0", align 8 + %"24_017" = load { i32, i8* }, { i32, i8* }* %"24_0", align 8 + %"21_018" = load { i64*, i64 }, { i64*, i64 }* %"21_0", align 8 + %37 = extractvalue { i32, i8* } %"24_017", 0 + %38 = extractvalue { i32, i8* } %"24_017", 1 + %39 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %37, i8* %38) + call void @abort() + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"25_0", align 8 + %"25_019" = load { i64*, i64 }, { i64*, i64 }* %"25_0", align 8 + store { i64*, i64 } %"25_019", { i64*, i64 }* %"013", align 8 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %35 + %"021" = load { i64*, i64 }, { i64*, i64 }* %"020", align 8 + store { i64*, i64 } %"021", { i64*, i64 }* %"27_0", align 8 + %"27_022" = load { i64*, i64 }, { i64*, i64 }* %"27_0", align 8 + store { i64*, i64 } %"27_022", { i64*, i64 }* %"013", align 8 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"014" = load { i64*, i64 }, { i64*, i64 }* %"013", align 8 + store { i64*, i64 } %"014", { i64*, i64 }* %"19_0", align 8 + %"19_023" = load { i64*, i64 }, { i64*, i64 }* %"19_0", align 8 + %"8_024" = load i64, i64* %"8_0", align 4 + %array_ptr25 = extractvalue { i64*, i64 } %"19_023", 0 + %array_offset26 = extractvalue { i64*, i64 } %"19_023", 1 + %40 = icmp ult i64 %"8_024", 2 + br i1 %40, label %42, label %41 + +41: ; preds = %cond_exit_19 + store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"027", align 4 + store { i64*, i64 } %"19_023", { i64*, i64 }* %"1", align 8 + br label %47 + +42: ; preds = %cond_exit_19 + %43 = add i64 %"8_024", %array_offset26 + %44 = getelementptr inbounds i64, i64* %array_ptr25, i64 %43 + %45 = load i64, i64* %44, align 4 + %46 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %45, 1 + store { i1, i64 } %46, { i1, i64 }* %"027", align 4 + store { i64*, i64 } %"19_023", { i64*, i64 }* %"1", align 8 + br label %47 + +47: ; preds = %41, %42 + %"028" = load { i1, i64 }, { i1, i64 }* %"027", align 4 + %"129" = load { i64*, i64 }, { i64*, i64 }* %"1", align 8 + store { i1, i64 } %"028", { i1, i64 }* %"29_0", align 4 + store { i64*, i64 } %"129", { i64*, i64 }* %"29_1", align 8 + %"29_030" = load { i1, i64 }, { i1, i64 }* %"29_0", align 4 + %48 = extractvalue { i1, i64 } %"29_030", 0 + switch i1 %48, label %49 [ + i1 true, label %50 ] -37: ; preds = %35 - br label %cond_28_case_0 +49: ; preds = %47 + br label %cond_31_case_0 -38: ; preds = %35 - %39 = extractvalue { i1, i64 } %"26_025", 1 - store i64 %39, i64* %"030", align 4 - br label %cond_28_case_1 +50: ; preds = %47 + %51 = extractvalue { i1, i64 } %"29_030", 1 + store i64 %51, i64* %"035", align 4 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %37 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"33_0", align 8 - %"33_028" = load { i32, i8* }, { i32, i8* }* %"33_0", align 8 - %40 = extractvalue { i32, i8* } %"33_028", 0 - %41 = extractvalue { i32, i8* } %"33_028", 1 - %42 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %40, i8* %41) +cond_31_case_0: ; preds = %49 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"36_0", align 8 + %"36_033" = load { i32, i8* }, { i32, i8* }* %"36_0", align 8 + %52 = extractvalue { i32, i8* } %"36_033", 0 + %53 = extractvalue { i32, i8* } %"36_033", 1 + %54 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %52, i8* %53) call void @abort() - store i64 0, i64* %"34_0", align 4 - %"34_029" = load i64, i64* %"34_0", align 4 - store i64 %"34_029", i64* %"026", align 4 - br label %cond_exit_28 - -cond_28_case_1: ; preds = %38 - %"031" = load i64, i64* %"030", align 4 - store i64 %"031", i64* %"36_0", align 4 - %"36_032" = load i64, i64* %"36_0", align 4 - store i64 %"36_032", i64* %"026", align 4 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"027" = load i64, i64* %"026", align 4 - store i64 %"027", i64* %"28_0", align 4 - %"26_133" = load { i64*, i64 }, { i64*, i64 }* %"26_1", align 8 - %"10_034" = load i64, i64* %"10_0", align 4 - %"28_035" = load i64, i64* %"28_0", align 4 - %array_ptr36 = extractvalue { i64*, i64 } %"26_133", 0 - %array_offset37 = extractvalue { i64*, i64 } %"26_133", 1 - %43 = icmp ult i64 %"10_034", 2 - br i1 %43, label %47, label %44 - -44: ; preds = %cond_exit_28 - %45 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"28_035", 2 - %46 = insertvalue { i1, { i64*, i64 }, i64 } %45, { i64*, i64 } %"26_133", 1 - store { i1, { i64*, i64 }, i64 } %46, { i1, { i64*, i64 }, i64 }* %"038", align 8 - br label %53 - -47: ; preds = %cond_exit_28 - %48 = add i64 %"10_034", %array_offset37 - %49 = getelementptr inbounds i64, i64* %array_ptr36, i64 %48 - %50 = load i64, i64* %49, align 4 - store i64 %"28_035", i64* %49, align 4 - %51 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %50, 2 - %52 = insertvalue { i1, { i64*, i64 }, i64 } %51, { i64*, i64 } %"26_133", 1 - store { i1, { i64*, i64 }, i64 } %52, { i1, { i64*, i64 }, i64 }* %"038", align 8 - br label %53 - -53: ; preds = %44, %47 - %"039" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"038", align 8 - store { i1, { i64*, i64 }, i64 } %"039", { i1, { i64*, i64 }, i64 }* %"38_0", align 8 - %"38_040" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"38_0", align 8 - %54 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 0 - switch i1 %54, label %55 [ - i1 true, label %58 + store i64 0, i64* %"37_0", align 4 + %"37_034" = load i64, i64* %"37_0", align 4 + store i64 %"37_034", i64* %"031", align 4 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %50 + %"036" = load i64, i64* %"035", align 4 + store i64 %"036", i64* %"39_0", align 4 + %"39_037" = load i64, i64* %"39_0", align 4 + store i64 %"39_037", i64* %"031", align 4 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 + %"032" = load i64, i64* %"031", align 4 + store i64 %"032", i64* %"31_0", align 4 + %"29_138" = load { i64*, i64 }, { i64*, i64 }* %"29_1", align 8 + %"10_039" = load i64, i64* %"10_0", align 4 + %"31_040" = load i64, i64* %"31_0", align 4 + %array_ptr41 = extractvalue { i64*, i64 } %"29_138", 0 + %array_offset42 = extractvalue { i64*, i64 } %"29_138", 1 + %55 = icmp ult i64 %"10_039", 2 + br i1 %55, label %59, label %56 + +56: ; preds = %cond_exit_31 + %57 = insertvalue { i1, { i64*, i64 }, i64 } { i1 false, { i64*, i64 } poison, i64 poison }, i64 %"31_040", 2 + %58 = insertvalue { i1, { i64*, i64 }, i64 } %57, { i64*, i64 } %"29_138", 1 + store { i1, { i64*, i64 }, i64 } %58, { i1, { i64*, i64 }, i64 }* %"043", align 8 + br label %65 + +59: ; preds = %cond_exit_31 + %60 = add i64 %"10_039", %array_offset42 + %61 = getelementptr inbounds i64, i64* %array_ptr41, i64 %60 + %62 = load i64, i64* %61, align 4 + store i64 %"31_040", i64* %61, align 4 + %63 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %62, 2 + %64 = insertvalue { i1, { i64*, i64 }, i64 } %63, { i64*, i64 } %"29_138", 1 + store { i1, { i64*, i64 }, i64 } %64, { i1, { i64*, i64 }, i64 }* %"043", align 8 + br label %65 + +65: ; preds = %56, %59 + %"044" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"043", align 8 + store { i1, { i64*, i64 }, i64 } %"044", { i1, { i64*, i64 }, i64 }* %"41_0", align 8 + %"41_045" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"41_0", align 8 + %66 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 0 + switch i1 %66, label %67 [ + i1 true, label %70 ] -55: ; preds = %53 - %56 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 2 - %57 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 1 - store i64 %56, i64* %"045", align 4 - store { i64*, i64 } %57, { i64*, i64 }* %"146", align 8 - br label %cond_39_case_0 - -58: ; preds = %53 - %59 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 2 - %60 = extractvalue { i1, { i64*, i64 }, i64 } %"38_040", 1 - store i64 %59, i64* %"054", align 4 - store { i64*, i64 } %60, { i64*, i64 }* %"155", align 8 - br label %cond_39_case_1 - -cond_39_case_0: ; preds = %55 - %"047" = load i64, i64* %"045", align 4 - %"148" = load { i64*, i64 }, { i64*, i64 }* %"146", align 8 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"44_0", align 8 - store i64 %"047", i64* %"41_0", align 4 - store { i64*, i64 } %"148", { i64*, i64 }* %"41_1", align 8 - %"44_049" = load { i32, i8* }, { i32, i8* }* %"44_0", align 8 - %"41_050" = load i64, i64* %"41_0", align 4 - %"41_151" = load { i64*, i64 }, { i64*, i64 }* %"41_1", align 8 - %61 = extractvalue { i32, i8* } %"44_049", 0 - %62 = extractvalue { i32, i8* } %"44_049", 1 - %63 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %61, i8* %62) +67: ; preds = %65 + %68 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 2 + %69 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 1 + store i64 %68, i64* %"050", align 4 + store { i64*, i64 } %69, { i64*, i64 }* %"151", align 8 + br label %cond_42_case_0 + +70: ; preds = %65 + %71 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 2 + %72 = extractvalue { i1, { i64*, i64 }, i64 } %"41_045", 1 + store i64 %71, i64* %"059", align 4 + store { i64*, i64 } %72, { i64*, i64 }* %"160", align 8 + br label %cond_42_case_1 + +cond_42_case_0: ; preds = %67 + %"052" = load i64, i64* %"050", align 4 + %"153" = load { i64*, i64 }, { i64*, i64 }* %"151", align 8 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"47_0", align 8 + store i64 %"052", i64* %"44_0", align 4 + store { i64*, i64 } %"153", { i64*, i64 }* %"44_1", align 8 + %"47_054" = load { i32, i8* }, { i32, i8* }* %"47_0", align 8 + %"44_055" = load i64, i64* %"44_0", align 4 + %"44_156" = load { i64*, i64 }, { i64*, i64 }* %"44_1", align 8 + %73 = extractvalue { i32, i8* } %"47_054", 0 + %74 = extractvalue { i32, i8* } %"47_054", 1 + %75 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %73, i8* %74) call void @abort() - store i64 0, i64* %"45_0", align 4 - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"45_1", align 8 - %"45_052" = load i64, i64* %"45_0", align 4 - %"45_153" = load { i64*, i64 }, { i64*, i64 }* %"45_1", align 8 - store i64 %"45_052", i64* %"041", align 4 - store { i64*, i64 } %"45_153", { i64*, i64 }* %"142", align 8 - br label %cond_exit_39 - -cond_39_case_1: ; preds = %58 - %"056" = load i64, i64* %"054", align 4 - %"157" = load { i64*, i64 }, { i64*, i64 }* %"155", align 8 - store i64 %"056", i64* %"47_0", align 4 - store { i64*, i64 } %"157", { i64*, i64 }* %"47_1", align 8 - %"47_058" = load i64, i64* %"47_0", align 4 - %"47_159" = load { i64*, i64 }, { i64*, i64 }* %"47_1", align 8 - store i64 %"47_058", i64* %"041", align 4 - store { i64*, i64 } %"47_159", { i64*, i64 }* %"142", align 8 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"043" = load i64, i64* %"041", align 4 - %"144" = load { i64*, i64 }, { i64*, i64 }* %"142", align 8 - store i64 %"043", i64* %"39_0", align 4 - store { i64*, i64 } %"144", { i64*, i64 }* %"39_1", align 8 - %"39_160" = load { i64*, i64 }, { i64*, i64 }* %"39_1", align 8 - %array_ptr61 = extractvalue { i64*, i64 } %"39_160", 0 - %array_offset62 = extractvalue { i64*, i64 } %"39_160", 1 - %new_offset = add i64 %array_offset62, 1 - %64 = getelementptr inbounds i64, i64* %array_ptr61, i64 %array_offset62 - %65 = load i64, i64* %64, align 4 - %66 = insertvalue { i64*, i64 } poison, i64* %array_ptr61, 0 - %67 = insertvalue { i64*, i64 } %66, i64 %new_offset, 1 - %68 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %65, 2 - %69 = insertvalue { i1, { i64*, i64 }, i64 } %68, { i64*, i64 } %67, 1 - store { i1, { i64*, i64 }, i64 } %69, { i1, { i64*, i64 }, i64 }* %"49_0", align 8 - %"49_063" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"49_0", align 8 - %70 = extractvalue { i1, { i64*, i64 }, i64 } %"49_063", 0 - switch i1 %70, label %71 [ - i1 true, label %72 + store i64 0, i64* %"48_0", align 4 + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"48_1", align 8 + %"48_057" = load i64, i64* %"48_0", align 4 + %"48_158" = load { i64*, i64 }, { i64*, i64 }* %"48_1", align 8 + store i64 %"48_057", i64* %"046", align 4 + store { i64*, i64 } %"48_158", { i64*, i64 }* %"147", align 8 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %70 + %"061" = load i64, i64* %"059", align 4 + %"162" = load { i64*, i64 }, { i64*, i64 }* %"160", align 8 + store i64 %"061", i64* %"50_0", align 4 + store { i64*, i64 } %"162", { i64*, i64 }* %"50_1", align 8 + %"50_063" = load i64, i64* %"50_0", align 4 + %"50_164" = load { i64*, i64 }, { i64*, i64 }* %"50_1", align 8 + store i64 %"50_063", i64* %"046", align 4 + store { i64*, i64 } %"50_164", { i64*, i64 }* %"147", align 8 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 + %"048" = load i64, i64* %"046", align 4 + %"149" = load { i64*, i64 }, { i64*, i64 }* %"147", align 8 + store i64 %"048", i64* %"42_0", align 4 + store { i64*, i64 } %"149", { i64*, i64 }* %"42_1", align 8 + %"42_165" = load { i64*, i64 }, { i64*, i64 }* %"42_1", align 8 + %array_ptr66 = extractvalue { i64*, i64 } %"42_165", 0 + %array_offset67 = extractvalue { i64*, i64 } %"42_165", 1 + %new_offset = add i64 %array_offset67, 1 + %76 = getelementptr inbounds i64, i64* %array_ptr66, i64 %array_offset67 + %77 = load i64, i64* %76, align 4 + %78 = insertvalue { i64*, i64 } poison, i64* %array_ptr66, 0 + %79 = insertvalue { i64*, i64 } %78, i64 %new_offset, 1 + %80 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %77, 2 + %81 = insertvalue { i1, { i64*, i64 }, i64 } %80, { i64*, i64 } %79, 1 + store { i1, { i64*, i64 }, i64 } %81, { i1, { i64*, i64 }, i64 }* %"52_0", align 8 + %"52_068" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"52_0", align 8 + %82 = extractvalue { i1, { i64*, i64 }, i64 } %"52_068", 0 + switch i1 %82, label %83 [ + i1 true, label %84 ] -71: ; preds = %cond_exit_39 - br label %cond_50_case_0 - -72: ; preds = %cond_exit_39 - %73 = extractvalue { i1, { i64*, i64 }, i64 } %"49_063", 2 - %74 = extractvalue { i1, { i64*, i64 }, i64 } %"49_063", 1 - store i64 %73, i64* %"071", align 4 - store { i64*, i64 } %74, { i64*, i64 }* %"172", align 8 - br label %cond_50_case_1 - -cond_50_case_0: ; preds = %71 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"55_0", align 8 - %"55_068" = load { i32, i8* }, { i32, i8* }* %"55_0", align 8 - %75 = extractvalue { i32, i8* } %"55_068", 0 - %76 = extractvalue { i32, i8* } %"55_068", 1 - %77 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %75, i8* %76) +83: ; preds = %cond_exit_42 + br label %cond_53_case_0 + +84: ; preds = %cond_exit_42 + %85 = extractvalue { i1, { i64*, i64 }, i64 } %"52_068", 2 + %86 = extractvalue { i1, { i64*, i64 }, i64 } %"52_068", 1 + store i64 %85, i64* %"076", align 4 + store { i64*, i64 } %86, { i64*, i64 }* %"177", align 8 + br label %cond_53_case_1 + +cond_53_case_0: ; preds = %83 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"58_0", align 8 + %"58_073" = load { i32, i8* }, { i32, i8* }* %"58_0", align 8 + %87 = extractvalue { i32, i8* } %"58_073", 0 + %88 = extractvalue { i32, i8* } %"58_073", 1 + %89 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %87, i8* %88) call void @abort() - store i64 0, i64* %"56_0", align 4 - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"56_1", align 8 - %"56_069" = load i64, i64* %"56_0", align 4 - %"56_170" = load { i64*, i64 }, { i64*, i64 }* %"56_1", align 8 - store i64 %"56_069", i64* %"064", align 4 - store { i64*, i64 } %"56_170", { i64*, i64 }* %"165", align 8 - br label %cond_exit_50 - -cond_50_case_1: ; preds = %72 - %"073" = load i64, i64* %"071", align 4 - %"174" = load { i64*, i64 }, { i64*, i64 }* %"172", align 8 - store i64 %"073", i64* %"58_0", align 4 - store { i64*, i64 } %"174", { i64*, i64 }* %"58_1", align 8 - %"58_075" = load i64, i64* %"58_0", align 4 - %"58_176" = load { i64*, i64 }, { i64*, i64 }* %"58_1", align 8 - store i64 %"58_075", i64* %"064", align 4 - store { i64*, i64 } %"58_176", { i64*, i64 }* %"165", align 8 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"066" = load i64, i64* %"064", align 4 - %"167" = load { i64*, i64 }, { i64*, i64 }* %"165", align 8 - store i64 %"066", i64* %"50_0", align 4 - store { i64*, i64 } %"167", { i64*, i64 }* %"50_1", align 8 - %"50_177" = load { i64*, i64 }, { i64*, i64 }* %"50_1", align 8 - %array_ptr78 = extractvalue { i64*, i64 } %"50_177", 0 - %array_offset79 = extractvalue { i64*, i64 } %"50_177", 1 - %78 = add i64 %array_offset79, 0 - %79 = getelementptr inbounds i64, i64* %array_ptr78, i64 %78 - %80 = load i64, i64* %79, align 4 - %81 = insertvalue { i64*, i64 } poison, i64* %array_ptr78, 0 - %82 = insertvalue { i64*, i64 } %81, i64 %array_offset79, 1 - %83 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %80, 2 - %84 = insertvalue { i1, { i64*, i64 }, i64 } %83, { i64*, i64 } %82, 1 - store { i1, { i64*, i64 }, i64 } %84, { i1, { i64*, i64 }, i64 }* %"60_0", align 8 - %"60_080" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"60_0", align 8 - %85 = extractvalue { i1, { i64*, i64 }, i64 } %"60_080", 0 - switch i1 %85, label %86 [ - i1 true, label %87 + store i64 0, i64* %"59_0", align 4 + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"59_1", align 8 + %"59_074" = load i64, i64* %"59_0", align 4 + %"59_175" = load { i64*, i64 }, { i64*, i64 }* %"59_1", align 8 + store i64 %"59_074", i64* %"069", align 4 + store { i64*, i64 } %"59_175", { i64*, i64 }* %"170", align 8 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %84 + %"078" = load i64, i64* %"076", align 4 + %"179" = load { i64*, i64 }, { i64*, i64 }* %"177", align 8 + store i64 %"078", i64* %"61_0", align 4 + store { i64*, i64 } %"179", { i64*, i64 }* %"61_1", align 8 + %"61_080" = load i64, i64* %"61_0", align 4 + %"61_181" = load { i64*, i64 }, { i64*, i64 }* %"61_1", align 8 + store i64 %"61_080", i64* %"069", align 4 + store { i64*, i64 } %"61_181", { i64*, i64 }* %"170", align 8 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"071" = load i64, i64* %"069", align 4 + %"172" = load { i64*, i64 }, { i64*, i64 }* %"170", align 8 + store i64 %"071", i64* %"53_0", align 4 + store { i64*, i64 } %"172", { i64*, i64 }* %"53_1", align 8 + %"53_182" = load { i64*, i64 }, { i64*, i64 }* %"53_1", align 8 + %array_ptr83 = extractvalue { i64*, i64 } %"53_182", 0 + %array_offset84 = extractvalue { i64*, i64 } %"53_182", 1 + %90 = add i64 %array_offset84, 0 + %91 = getelementptr inbounds i64, i64* %array_ptr83, i64 %90 + %92 = load i64, i64* %91, align 4 + %93 = insertvalue { i64*, i64 } poison, i64* %array_ptr83, 0 + %94 = insertvalue { i64*, i64 } %93, i64 %array_offset84, 1 + %95 = insertvalue { i1, { i64*, i64 }, i64 } { i1 true, { i64*, i64 } poison, i64 poison }, i64 %92, 2 + %96 = insertvalue { i1, { i64*, i64 }, i64 } %95, { i64*, i64 } %94, 1 + store { i1, { i64*, i64 }, i64 } %96, { i1, { i64*, i64 }, i64 }* %"63_0", align 8 + %"63_085" = load { i1, { i64*, i64 }, i64 }, { i1, { i64*, i64 }, i64 }* %"63_0", align 8 + %97 = extractvalue { i1, { i64*, i64 }, i64 } %"63_085", 0 + switch i1 %97, label %98 [ + i1 true, label %99 ] -86: ; preds = %cond_exit_50 - br label %cond_61_case_0 - -87: ; preds = %cond_exit_50 - %88 = extractvalue { i1, { i64*, i64 }, i64 } %"60_080", 2 - %89 = extractvalue { i1, { i64*, i64 }, i64 } %"60_080", 1 - store i64 %88, i64* %"088", align 4 - store { i64*, i64 } %89, { i64*, i64 }* %"189", align 8 - br label %cond_61_case_1 - -cond_61_case_0: ; preds = %86 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"66_0", align 8 - %"66_085" = load { i32, i8* }, { i32, i8* }* %"66_0", align 8 - %90 = extractvalue { i32, i8* } %"66_085", 0 - %91 = extractvalue { i32, i8* } %"66_085", 1 - %92 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %90, i8* %91) +98: ; preds = %cond_exit_53 + br label %cond_64_case_0 + +99: ; preds = %cond_exit_53 + %100 = extractvalue { i1, { i64*, i64 }, i64 } %"63_085", 2 + %101 = extractvalue { i1, { i64*, i64 }, i64 } %"63_085", 1 + store i64 %100, i64* %"093", align 4 + store { i64*, i64 } %101, { i64*, i64 }* %"194", align 8 + br label %cond_64_case_1 + +cond_64_case_0: ; preds = %98 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"69_0", align 8 + %"69_090" = load { i32, i8* }, { i32, i8* }* %"69_0", align 8 + %102 = extractvalue { i32, i8* } %"69_090", 0 + %103 = extractvalue { i32, i8* } %"69_090", 1 + %104 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %102, i8* %103) call void @abort() - store i64 0, i64* %"67_0", align 4 - store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"67_1", align 8 - %"67_086" = load i64, i64* %"67_0", align 4 - %"67_187" = load { i64*, i64 }, { i64*, i64 }* %"67_1", align 8 - store i64 %"67_086", i64* %"081", align 4 - store { i64*, i64 } %"67_187", { i64*, i64 }* %"182", align 8 - br label %cond_exit_61 - -cond_61_case_1: ; preds = %87 - %"090" = load i64, i64* %"088", align 4 - %"191" = load { i64*, i64 }, { i64*, i64 }* %"189", align 8 - store i64 %"090", i64* %"69_0", align 4 - store { i64*, i64 } %"191", { i64*, i64 }* %"69_1", align 8 - %"69_092" = load i64, i64* %"69_0", align 4 - %"69_193" = load { i64*, i64 }, { i64*, i64 }* %"69_1", align 8 - store i64 %"69_092", i64* %"081", align 4 - store { i64*, i64 } %"69_193", { i64*, i64 }* %"182", align 8 - br label %cond_exit_61 - -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"083" = load i64, i64* %"081", align 4 - %"184" = load { i64*, i64 }, { i64*, i64 }* %"182", align 8 - store i64 %"083", i64* %"61_0", align 4 - store { i64*, i64 } %"184", { i64*, i64 }* %"61_1", align 8 - %"61_194" = load { i64*, i64 }, { i64*, i64 }* %"61_1", align 8 - %array_ptr95 = extractvalue { i64*, i64 } %"61_194", 0 - %array_offset96 = extractvalue { i64*, i64 } %"61_194", 1 - %93 = bitcast i64* %array_ptr95 to i8* - call void @free(i8* %93) + store i64 0, i64* %"70_0", align 4 + store { i64*, i64 } zeroinitializer, { i64*, i64 }* %"70_1", align 8 + %"70_091" = load i64, i64* %"70_0", align 4 + %"70_192" = load { i64*, i64 }, { i64*, i64 }* %"70_1", align 8 + store i64 %"70_091", i64* %"086", align 4 + store { i64*, i64 } %"70_192", { i64*, i64 }* %"187", align 8 + br label %cond_exit_64 + +cond_64_case_1: ; preds = %99 + %"095" = load i64, i64* %"093", align 4 + %"196" = load { i64*, i64 }, { i64*, i64 }* %"194", align 8 + store i64 %"095", i64* %"72_0", align 4 + store { i64*, i64 } %"196", { i64*, i64 }* %"72_1", align 8 + %"72_097" = load i64, i64* %"72_0", align 4 + %"72_198" = load { i64*, i64 }, { i64*, i64 }* %"72_1", align 8 + store i64 %"72_097", i64* %"086", align 4 + store { i64*, i64 } %"72_198", { i64*, i64 }* %"187", align 8 + br label %cond_exit_64 + +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"088" = load i64, i64* %"086", align 4 + %"189" = load { i64*, i64 }, { i64*, i64 }* %"187", align 8 + store i64 %"088", i64* %"64_0", align 4 + store { i64*, i64 } %"189", { i64*, i64 }* %"64_1", align 8 + %"64_199" = load { i64*, i64 }, { i64*, i64 }* %"64_1", align 8 + %array_ptr100 = extractvalue { i64*, i64 } %"64_199", 0 + %array_offset101 = extractvalue { i64*, i64 } %"64_199", 1 + %105 = bitcast i64* %array_ptr100 to i8* + call void @free(i8* %105) ret void } diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap index 0bab21f5d3..6798b9ab55 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@llvm14.snap @@ -23,216 +23,220 @@ alloca_block: entry_block: ; preds = %alloca_block %0 = insertvalue [2 x i64] undef, i64 1, 0 %1 = insertvalue [2 x i64] %0, i64 2, 1 - %2 = icmp ult i64 0, 2 - %3 = icmp ult i64 1, 2 - %4 = and i1 %2, %3 - br i1 %4, label %7, label %5 - -5: ; preds = %entry_block - %6 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %1, 1 - br label %17 + %2 = insertvalue [2 x i64] undef, i64 1, 0 + %3 = insertvalue [2 x i64] %2, i64 2, 1 + %extract = extractvalue [2 x i64] %3, 0 + %extract6 = extractvalue [2 x i64] %3, 1 + %4 = icmp ult i64 0, 2 + %5 = icmp ult i64 1, 2 + %6 = and i1 %4, %5 + br i1 %6, label %9, label %7 7: ; preds = %entry_block - %8 = alloca i64, i32 2, align 8 - %9 = bitcast i64* %8 to [2 x i64]* - store [2 x i64] %1, [2 x i64]* %9, align 4 - %10 = getelementptr inbounds i64, i64* %8, i64 0 - %11 = load i64, i64* %10, align 4 - %12 = getelementptr inbounds i64, i64* %8, i64 1 + %8 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %1, 1 + br label %19 + +9: ; preds = %entry_block + %10 = alloca i64, i32 2, align 8 + %11 = bitcast i64* %10 to [2 x i64]* + store [2 x i64] %1, [2 x i64]* %11, align 4 + %12 = getelementptr inbounds i64, i64* %10, i64 0 %13 = load i64, i64* %12, align 4 - store i64 %13, i64* %10, align 4 - store i64 %11, i64* %12, align 4 - %14 = bitcast i64* %8 to [2 x i64]* - %15 = load [2 x i64], [2 x i64]* %14, align 4 - %16 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %15, 1 - br label %17 - -17: ; preds = %5, %7 - %"0.0" = phi { i1, [2 x i64] } [ %16, %7 ], [ %6, %5 ] - %18 = extractvalue { i1, [2 x i64] } %"0.0", 0 - switch i1 %18, label %19 [ - i1 true, label %21 + %14 = getelementptr inbounds i64, i64* %10, i64 1 + %15 = load i64, i64* %14, align 4 + store i64 %15, i64* %12, align 4 + store i64 %13, i64* %14, align 4 + %16 = bitcast i64* %10 to [2 x i64]* + %17 = load [2 x i64], [2 x i64]* %16, align 4 + %18 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %17, 1 + br label %19 + +19: ; preds = %7, %9 + %"0.0" = phi { i1, [2 x i64] } [ %18, %9 ], [ %8, %7 ] + %20 = extractvalue { i1, [2 x i64] } %"0.0", 0 + switch i1 %20, label %21 [ + i1 true, label %23 ] -19: ; preds = %17 - %20 = extractvalue { i1, [2 x i64] } %"0.0", 1 - br label %cond_16_case_0 - -21: ; preds = %17 +21: ; preds = %19 %22 = extractvalue { i1, [2 x i64] } %"0.0", 1 - br label %cond_16_case_1 + br label %cond_19_case_0 + +23: ; preds = %19 + %24 = extractvalue { i1, [2 x i64] } %"0.0", 1 + br label %cond_19_case_1 -cond_16_case_0: ; preds = %19 - %23 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 - %24 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 - %25 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %23, i8* %24) +cond_19_case_0: ; preds = %21 + %25 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 0 + %26 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, 1 + %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) call void @abort() - br label %cond_exit_16 - -cond_16_case_1: ; preds = %21 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"08.0" = phi [2 x i64] [ zeroinitializer, %cond_16_case_0 ], [ %22, %cond_16_case_1 ] - %26 = icmp ult i64 0, 2 - br i1 %26, label %28, label %27 - -27: ; preds = %cond_exit_16 - br label %34 - -28: ; preds = %cond_exit_16 - %29 = alloca i64, i32 2, align 8 - %30 = bitcast i64* %29 to [2 x i64]* - store [2 x i64] %"08.0", [2 x i64]* %30, align 4 - %31 = getelementptr inbounds i64, i64* %29, i64 0 - %32 = load i64, i64* %31, align 4 - %33 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %32, 1 - br label %34 - -34: ; preds = %27, %28 - %"020.0" = phi { i1, i64 } [ %33, %28 ], [ { i1 false, i64 poison }, %27 ] - %35 = extractvalue { i1, i64 } %"020.0", 0 - switch i1 %35, label %36 [ - i1 true, label %37 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %23 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"012.0" = phi [2 x i64] [ zeroinitializer, %cond_19_case_0 ], [ %24, %cond_19_case_1 ] + %28 = icmp ult i64 0, 2 + br i1 %28, label %30, label %29 + +29: ; preds = %cond_exit_19 + br label %36 + +30: ; preds = %cond_exit_19 + %31 = alloca i64, i32 2, align 8 + %32 = bitcast i64* %31 to [2 x i64]* + store [2 x i64] %"012.0", [2 x i64]* %32, align 4 + %33 = getelementptr inbounds i64, i64* %31, i64 0 + %34 = load i64, i64* %33, align 4 + %35 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %34, 1 + br label %36 + +36: ; preds = %29, %30 + %"024.0" = phi { i1, i64 } [ %35, %30 ], [ { i1 false, i64 poison }, %29 ] + %37 = extractvalue { i1, i64 } %"024.0", 0 + switch i1 %37, label %38 [ + i1 true, label %39 ] -36: ; preds = %34 - br label %cond_28_case_0 +38: ; preds = %36 + br label %cond_31_case_0 -37: ; preds = %34 - %38 = extractvalue { i1, i64 } %"020.0", 1 - br label %cond_28_case_1 +39: ; preds = %36 + %40 = extractvalue { i1, i64 } %"024.0", 1 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %36 - %39 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 - %40 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 - %41 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %39, i8* %40) +cond_31_case_0: ; preds = %38 + %41 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 0 + %42 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, 1 + %43 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %41, i8* %42) call void @abort() - br label %cond_exit_28 - -cond_28_case_1: ; preds = %37 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"024.0" = phi i64 [ 0, %cond_28_case_0 ], [ %38, %cond_28_case_1 ] - %42 = icmp ult i64 1, 2 - br i1 %42, label %46, label %43 - -43: ; preds = %cond_exit_28 - %44 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"024.0", 1 - %45 = insertvalue { i1, i64, [2 x i64] } %44, [2 x i64] %"08.0", 2 - br label %55 - -46: ; preds = %cond_exit_28 - %47 = alloca i64, i32 2, align 8 - %48 = bitcast i64* %47 to [2 x i64]* - store [2 x i64] %"08.0", [2 x i64]* %48, align 4 - %49 = getelementptr inbounds i64, i64* %47, i64 1 - %50 = load i64, i64* %49, align 4 - store i64 %"024.0", i64* %49, align 4 - %51 = bitcast i64* %47 to [2 x i64]* - %52 = load [2 x i64], [2 x i64]* %51, align 4 - %53 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %50, 1 - %54 = insertvalue { i1, i64, [2 x i64] } %53, [2 x i64] %52, 2 - br label %55 - -55: ; preds = %43, %46 - %"034.0" = phi { i1, i64, [2 x i64] } [ %54, %46 ], [ %45, %43 ] - %56 = extractvalue { i1, i64, [2 x i64] } %"034.0", 0 - switch i1 %56, label %57 [ - i1 true, label %60 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %39 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 + %"028.0" = phi i64 [ 0, %cond_31_case_0 ], [ %40, %cond_31_case_1 ] + %44 = icmp ult i64 1, 2 + br i1 %44, label %48, label %45 + +45: ; preds = %cond_exit_31 + %46 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"028.0", 1 + %47 = insertvalue { i1, i64, [2 x i64] } %46, [2 x i64] %"012.0", 2 + br label %57 + +48: ; preds = %cond_exit_31 + %49 = alloca i64, i32 2, align 8 + %50 = bitcast i64* %49 to [2 x i64]* + store [2 x i64] %"012.0", [2 x i64]* %50, align 4 + %51 = getelementptr inbounds i64, i64* %49, i64 1 + %52 = load i64, i64* %51, align 4 + store i64 %"028.0", i64* %51, align 4 + %53 = bitcast i64* %49 to [2 x i64]* + %54 = load [2 x i64], [2 x i64]* %53, align 4 + %55 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %52, 1 + %56 = insertvalue { i1, i64, [2 x i64] } %55, [2 x i64] %54, 2 + br label %57 + +57: ; preds = %45, %48 + %"038.0" = phi { i1, i64, [2 x i64] } [ %56, %48 ], [ %47, %45 ] + %58 = extractvalue { i1, i64, [2 x i64] } %"038.0", 0 + switch i1 %58, label %59 [ + i1 true, label %62 ] -57: ; preds = %55 - %58 = extractvalue { i1, i64, [2 x i64] } %"034.0", 1 - %59 = extractvalue { i1, i64, [2 x i64] } %"034.0", 2 - br label %cond_39_case_0 +59: ; preds = %57 + %60 = extractvalue { i1, i64, [2 x i64] } %"038.0", 1 + %61 = extractvalue { i1, i64, [2 x i64] } %"038.0", 2 + br label %cond_42_case_0 -60: ; preds = %55 - %61 = extractvalue { i1, i64, [2 x i64] } %"034.0", 1 - %62 = extractvalue { i1, i64, [2 x i64] } %"034.0", 2 - br label %cond_39_case_1 +62: ; preds = %57 + %63 = extractvalue { i1, i64, [2 x i64] } %"038.0", 1 + %64 = extractvalue { i1, i64, [2 x i64] } %"038.0", 2 + br label %cond_42_case_1 -cond_39_case_0: ; preds = %57 - %63 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 - %64 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 - %65 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %63, i8* %64) +cond_42_case_0: ; preds = %59 + %65 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 0 + %66 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, 1 + %67 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %65, i8* %66) call void @abort() - br label %cond_exit_39 - -cond_39_case_1: ; preds = %60 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"037.0" = phi i64 [ 0, %cond_39_case_0 ], [ %61, %cond_39_case_1 ] - %"138.0" = phi [2 x i64] [ zeroinitializer, %cond_39_case_0 ], [ %62, %cond_39_case_1 ] - %66 = alloca i64, i32 2, align 8 - %67 = bitcast i64* %66 to [2 x i64]* - store [2 x i64] %"138.0", [2 x i64]* %67, align 4 - %68 = getelementptr i64, i64* %66, i32 1 - %69 = load i64, i64* %66, align 4 - %70 = bitcast i64* %68 to [1 x i64]* - %71 = load [1 x i64], [1 x i64]* %70, align 4 - %72 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %69, 1 - %73 = insertvalue { i1, i64, [1 x i64] } %72, [1 x i64] %71, 2 - %74 = extractvalue { i1, i64, [1 x i64] } %73, 0 - switch i1 %74, label %75 [ - i1 true, label %76 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %62 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 + %"041.0" = phi i64 [ 0, %cond_42_case_0 ], [ %63, %cond_42_case_1 ] + %"142.0" = phi [2 x i64] [ zeroinitializer, %cond_42_case_0 ], [ %64, %cond_42_case_1 ] + %68 = alloca i64, i32 2, align 8 + %69 = bitcast i64* %68 to [2 x i64]* + store [2 x i64] %"142.0", [2 x i64]* %69, align 4 + %70 = getelementptr i64, i64* %68, i32 1 + %71 = load i64, i64* %68, align 4 + %72 = bitcast i64* %70 to [1 x i64]* + %73 = load [1 x i64], [1 x i64]* %72, align 4 + %74 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %71, 1 + %75 = insertvalue { i1, i64, [1 x i64] } %74, [1 x i64] %73, 2 + %76 = extractvalue { i1, i64, [1 x i64] } %75, 0 + switch i1 %76, label %77 [ + i1 true, label %78 ] -75: ; preds = %cond_exit_39 - br label %cond_50_case_0 +77: ; preds = %cond_exit_42 + br label %cond_53_case_0 -76: ; preds = %cond_exit_39 - %77 = extractvalue { i1, i64, [1 x i64] } %73, 1 - %78 = extractvalue { i1, i64, [1 x i64] } %73, 2 - br label %cond_50_case_1 +78: ; preds = %cond_exit_42 + %79 = extractvalue { i1, i64, [1 x i64] } %75, 1 + %80 = extractvalue { i1, i64, [1 x i64] } %75, 2 + br label %cond_53_case_1 -cond_50_case_0: ; preds = %75 - %79 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 - %80 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 - %81 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %79, i8* %80) +cond_53_case_0: ; preds = %77 + %81 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 0 + %82 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, 1 + %83 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %81, i8* %82) call void @abort() - br label %cond_exit_50 - -cond_50_case_1: ; preds = %76 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"058.0" = phi i64 [ 0, %cond_50_case_0 ], [ %77, %cond_50_case_1 ] - %"159.0" = phi [1 x i64] [ zeroinitializer, %cond_50_case_0 ], [ %78, %cond_50_case_1 ] - %82 = alloca i64, align 8 - %83 = bitcast i64* %82 to [1 x i64]* - store [1 x i64] %"159.0", [1 x i64]* %83, align 4 - %84 = getelementptr i64, i64* %82, i32 0 - %85 = load i64, i64* %84, align 4 - %86 = bitcast i64* %82 to [0 x i64]* - %87 = load [0 x i64], [0 x i64]* %86, align 4 - %88 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %85, 1 - %89 = extractvalue { i1, i64 } %88, 0 - switch i1 %89, label %90 [ - i1 true, label %91 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %78 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"062.0" = phi i64 [ 0, %cond_53_case_0 ], [ %79, %cond_53_case_1 ] + %"163.0" = phi [1 x i64] [ zeroinitializer, %cond_53_case_0 ], [ %80, %cond_53_case_1 ] + %84 = alloca i64, align 8 + %85 = bitcast i64* %84 to [1 x i64]* + store [1 x i64] %"163.0", [1 x i64]* %85, align 4 + %86 = getelementptr i64, i64* %84, i32 0 + %87 = load i64, i64* %86, align 4 + %88 = bitcast i64* %84 to [0 x i64]* + %89 = load [0 x i64], [0 x i64]* %88, align 4 + %90 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %87, 1 + %91 = extractvalue { i1, i64 } %90, 0 + switch i1 %91, label %92 [ + i1 true, label %93 ] -90: ; preds = %cond_exit_50 - br label %cond_61_case_0 +92: ; preds = %cond_exit_53 + br label %cond_64_case_0 -91: ; preds = %cond_exit_50 - %92 = extractvalue { i1, i64 } %88, 1 - br label %cond_61_case_1 +93: ; preds = %cond_exit_53 + %94 = extractvalue { i1, i64 } %90, 1 + br label %cond_64_case_1 -cond_61_case_0: ; preds = %90 - %93 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 - %94 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 - %95 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %93, i8* %94) +cond_64_case_0: ; preds = %92 + %95 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 0 + %96 = extractvalue { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, 1 + %97 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %95, i8* %96) call void @abort() - br label %cond_exit_61 + br label %cond_exit_64 -cond_61_case_1: ; preds = %91 - br label %cond_exit_61 +cond_64_case_1: ; preds = %93 + br label %cond_exit_64 -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"073.0" = phi i64 [ 0, %cond_61_case_0 ], [ %92, %cond_61_case_1 ] +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"077.0" = phi i64 [ 0, %cond_64_case_0 ], [ %94, %cond_64_case_1 ] ret void } diff --git a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap index d8aaf790b8..c2cb8ed5a4 100644 --- a/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap +++ b/hugr-llvm/src/extension/collections/snapshots/hugr_llvm__extension__collections__stack_array__test__emit_all_ops@pre-mem2reg@llvm14.snap @@ -20,69 +20,72 @@ define void @_hl.main.1() { alloca_block: %"12_0" = alloca i64, align 8 %"10_0" = alloca i64, align 8 + %"15_0" = alloca [2 x i64], align 8 %"13_0" = alloca [2 x i64], align 8 + %"14_0" = alloca i64, align 8 + %"14_1" = alloca i64, align 8 %"8_0" = alloca i64, align 8 - %"14_0" = alloca { i1, [2 x i64] }, align 8 + %"18_0" = alloca { i1, [2 x i64] }, align 8 %"0" = alloca { i1, [2 x i64] }, align 8 - %"16_0" = alloca [2 x i64], align 8 - %"08" = alloca [2 x i64], align 8 - %"010" = alloca [2 x i64], align 8 - %"21_0" = alloca { i32, i8* }, align 8 - %"18_0" = alloca [2 x i64], align 8 - %"22_0" = alloca [2 x i64], align 8 - %"015" = alloca [2 x i64], align 8 - %"24_0" = alloca [2 x i64], align 8 - %"26_0" = alloca { i1, i64 }, align 8 - %"26_1" = alloca [2 x i64], align 8 - %"020" = alloca { i1, i64 }, align 8 + %"19_0" = alloca [2 x i64], align 8 + %"012" = alloca [2 x i64], align 8 + %"014" = alloca [2 x i64], align 8 + %"24_0" = alloca { i32, i8* }, align 8 + %"21_0" = alloca [2 x i64], align 8 + %"25_0" = alloca [2 x i64], align 8 + %"019" = alloca [2 x i64], align 8 + %"27_0" = alloca [2 x i64], align 8 + %"29_0" = alloca { i1, i64 }, align 8 + %"29_1" = alloca [2 x i64], align 8 + %"024" = alloca { i1, i64 }, align 8 %"1" = alloca [2 x i64], align 8 - %"28_0" = alloca i64, align 8 - %"024" = alloca i64, align 8 - %"33_0" = alloca { i32, i8* }, align 8 - %"34_0" = alloca i64, align 8 + %"31_0" = alloca i64, align 8 %"028" = alloca i64, align 8 - %"36_0" = alloca i64, align 8 - %"38_0" = alloca { i1, i64, [2 x i64] }, align 8 - %"034" = alloca { i1, i64, [2 x i64] }, align 8 + %"36_0" = alloca { i32, i8* }, align 8 + %"37_0" = alloca i64, align 8 + %"032" = alloca i64, align 8 %"39_0" = alloca i64, align 8 - %"39_1" = alloca [2 x i64], align 8 - %"037" = alloca i64, align 8 - %"138" = alloca [2 x i64], align 8 + %"41_0" = alloca { i1, i64, [2 x i64] }, align 8 + %"038" = alloca { i1, i64, [2 x i64] }, align 8 + %"42_0" = alloca i64, align 8 + %"42_1" = alloca [2 x i64], align 8 %"041" = alloca i64, align 8 %"142" = alloca [2 x i64], align 8 - %"44_0" = alloca { i32, i8* }, align 8 - %"41_0" = alloca i64, align 8 - %"41_1" = alloca [2 x i64], align 8 - %"45_0" = alloca i64, align 8 - %"45_1" = alloca [2 x i64], align 8 - %"050" = alloca i64, align 8 - %"151" = alloca [2 x i64], align 8 - %"47_0" = alloca i64, align 8 - %"47_1" = alloca [2 x i64], align 8 - %"49_0" = alloca { i1, i64, [1 x i64] }, align 8 + %"045" = alloca i64, align 8 + %"146" = alloca [2 x i64], align 8 + %"47_0" = alloca { i32, i8* }, align 8 + %"44_0" = alloca i64, align 8 + %"44_1" = alloca [2 x i64], align 8 + %"48_0" = alloca i64, align 8 + %"48_1" = alloca [2 x i64], align 8 + %"054" = alloca i64, align 8 + %"155" = alloca [2 x i64], align 8 %"50_0" = alloca i64, align 8 - %"50_1" = alloca [1 x i64], align 8 - %"058" = alloca i64, align 8 - %"159" = alloca [1 x i64], align 8 - %"55_0" = alloca { i32, i8* }, align 8 - %"56_0" = alloca i64, align 8 - %"56_1" = alloca [1 x i64], align 8 - %"065" = alloca i64, align 8 - %"166" = alloca [1 x i64], align 8 - %"58_0" = alloca i64, align 8 - %"58_1" = alloca [1 x i64], align 8 - %"60_0" = alloca { i1, i64 }, align 8 + %"50_1" = alloca [2 x i64], align 8 + %"52_0" = alloca { i1, i64, [1 x i64] }, align 8 + %"53_0" = alloca i64, align 8 + %"53_1" = alloca [1 x i64], align 8 + %"062" = alloca i64, align 8 + %"163" = alloca [1 x i64], align 8 + %"58_0" = alloca { i32, i8* }, align 8 + %"59_0" = alloca i64, align 8 + %"59_1" = alloca [1 x i64], align 8 + %"069" = alloca i64, align 8 + %"170" = alloca [1 x i64], align 8 %"61_0" = alloca i64, align 8 - %"61_1" = alloca [0 x i64], align 8 - %"073" = alloca i64, align 8 - %"174" = alloca [0 x i64], align 8 - %"66_0" = alloca { i32, i8* }, align 8 - %"67_0" = alloca i64, align 8 - %"67_1" = alloca [0 x i64], align 8 - %"080" = alloca i64, align 8 - %"181" = alloca [0 x i64], align 8 - %"69_0" = alloca i64, align 8 - %"69_1" = alloca [0 x i64], align 8 + %"61_1" = alloca [1 x i64], align 8 + %"63_0" = alloca { i1, i64 }, align 8 + %"64_0" = alloca i64, align 8 + %"64_1" = alloca [0 x i64], align 8 + %"077" = alloca i64, align 8 + %"178" = alloca [0 x i64], align 8 + %"69_0" = alloca { i32, i8* }, align 8 + %"70_0" = alloca i64, align 8 + %"70_1" = alloca [0 x i64], align 8 + %"084" = alloca i64, align 8 + %"185" = alloca [0 x i64], align 8 + %"72_0" = alloca i64, align 8 + %"72_1" = alloca [0 x i64], align 8 br label %entry_block entry_block: ; preds = %alloca_block @@ -92,344 +95,354 @@ entry_block: ; preds = %alloca_block %"12_02" = load i64, i64* %"12_0", align 4 %0 = insertvalue [2 x i64] undef, i64 %"10_01", 0 %1 = insertvalue [2 x i64] %0, i64 %"12_02", 1 - store [2 x i64] %1, [2 x i64]* %"13_0", align 4 + store [2 x i64] %1, [2 x i64]* %"15_0", align 4 + %"10_03" = load i64, i64* %"10_0", align 4 + %"12_04" = load i64, i64* %"12_0", align 4 + %2 = insertvalue [2 x i64] undef, i64 %"10_03", 0 + %3 = insertvalue [2 x i64] %2, i64 %"12_04", 1 + store [2 x i64] %3, [2 x i64]* %"13_0", align 4 + %"13_05" = load [2 x i64], [2 x i64]* %"13_0", align 4 + %extract = extractvalue [2 x i64] %"13_05", 0 + %extract6 = extractvalue [2 x i64] %"13_05", 1 + store i64 %extract, i64* %"14_0", align 4 + store i64 %extract6, i64* %"14_1", align 4 store i64 0, i64* %"8_0", align 4 - %"13_03" = load [2 x i64], [2 x i64]* %"13_0", align 4 - %"8_04" = load i64, i64* %"8_0", align 4 - %"10_05" = load i64, i64* %"10_0", align 4 - %2 = icmp ult i64 %"8_04", 2 - %3 = icmp ult i64 %"10_05", 2 - %4 = and i1 %2, %3 - br i1 %4, label %7, label %5 - -5: ; preds = %entry_block - %6 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %"13_03", 1 - store { i1, [2 x i64] } %6, { i1, [2 x i64] }* %"0", align 4 - br label %17 + %"15_07" = load [2 x i64], [2 x i64]* %"15_0", align 4 + %"8_08" = load i64, i64* %"8_0", align 4 + %"10_09" = load i64, i64* %"10_0", align 4 + %4 = icmp ult i64 %"8_08", 2 + %5 = icmp ult i64 %"10_09", 2 + %6 = and i1 %4, %5 + br i1 %6, label %9, label %7 7: ; preds = %entry_block - %8 = alloca i64, i32 2, align 8 - %9 = bitcast i64* %8 to [2 x i64]* - store [2 x i64] %"13_03", [2 x i64]* %9, align 4 - %10 = getelementptr inbounds i64, i64* %8, i64 %"8_04" - %11 = load i64, i64* %10, align 4 - %12 = getelementptr inbounds i64, i64* %8, i64 %"10_05" + %8 = insertvalue { i1, [2 x i64] } { i1 false, [2 x i64] poison }, [2 x i64] %"15_07", 1 + store { i1, [2 x i64] } %8, { i1, [2 x i64] }* %"0", align 4 + br label %19 + +9: ; preds = %entry_block + %10 = alloca i64, i32 2, align 8 + %11 = bitcast i64* %10 to [2 x i64]* + store [2 x i64] %"15_07", [2 x i64]* %11, align 4 + %12 = getelementptr inbounds i64, i64* %10, i64 %"8_08" %13 = load i64, i64* %12, align 4 - store i64 %13, i64* %10, align 4 - store i64 %11, i64* %12, align 4 - %14 = bitcast i64* %8 to [2 x i64]* - %15 = load [2 x i64], [2 x i64]* %14, align 4 - %16 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %15, 1 - store { i1, [2 x i64] } %16, { i1, [2 x i64] }* %"0", align 4 - br label %17 - -17: ; preds = %5, %7 - %"06" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"0", align 4 - store { i1, [2 x i64] } %"06", { i1, [2 x i64] }* %"14_0", align 4 - %"14_07" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"14_0", align 4 - %18 = extractvalue { i1, [2 x i64] } %"14_07", 0 - switch i1 %18, label %19 [ - i1 true, label %21 + %14 = getelementptr inbounds i64, i64* %10, i64 %"10_09" + %15 = load i64, i64* %14, align 4 + store i64 %15, i64* %12, align 4 + store i64 %13, i64* %14, align 4 + %16 = bitcast i64* %10 to [2 x i64]* + %17 = load [2 x i64], [2 x i64]* %16, align 4 + %18 = insertvalue { i1, [2 x i64] } { i1 true, [2 x i64] poison }, [2 x i64] %17, 1 + store { i1, [2 x i64] } %18, { i1, [2 x i64] }* %"0", align 4 + br label %19 + +19: ; preds = %7, %9 + %"010" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"0", align 4 + store { i1, [2 x i64] } %"010", { i1, [2 x i64] }* %"18_0", align 4 + %"18_011" = load { i1, [2 x i64] }, { i1, [2 x i64] }* %"18_0", align 4 + %20 = extractvalue { i1, [2 x i64] } %"18_011", 0 + switch i1 %20, label %21 [ + i1 true, label %23 ] -19: ; preds = %17 - %20 = extractvalue { i1, [2 x i64] } %"14_07", 1 - store [2 x i64] %20, [2 x i64]* %"010", align 4 - br label %cond_16_case_0 - -21: ; preds = %17 - %22 = extractvalue { i1, [2 x i64] } %"14_07", 1 - store [2 x i64] %22, [2 x i64]* %"015", align 4 - br label %cond_16_case_1 - -cond_16_case_0: ; preds = %19 - %"011" = load [2 x i64], [2 x i64]* %"010", align 4 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"21_0", align 8 - store [2 x i64] %"011", [2 x i64]* %"18_0", align 4 - %"21_012" = load { i32, i8* }, { i32, i8* }* %"21_0", align 8 - %"18_013" = load [2 x i64], [2 x i64]* %"18_0", align 4 - %23 = extractvalue { i32, i8* } %"21_012", 0 - %24 = extractvalue { i32, i8* } %"21_012", 1 - %25 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %23, i8* %24) +21: ; preds = %19 + %22 = extractvalue { i1, [2 x i64] } %"18_011", 1 + store [2 x i64] %22, [2 x i64]* %"014", align 4 + br label %cond_19_case_0 + +23: ; preds = %19 + %24 = extractvalue { i1, [2 x i64] } %"18_011", 1 + store [2 x i64] %24, [2 x i64]* %"019", align 4 + br label %cond_19_case_1 + +cond_19_case_0: ; preds = %21 + %"015" = load [2 x i64], [2 x i64]* %"014", align 4 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @0, i32 0, i32 0) }, { i32, i8* }* %"24_0", align 8 + store [2 x i64] %"015", [2 x i64]* %"21_0", align 4 + %"24_016" = load { i32, i8* }, { i32, i8* }* %"24_0", align 8 + %"21_017" = load [2 x i64], [2 x i64]* %"21_0", align 4 + %25 = extractvalue { i32, i8* } %"24_016", 0 + %26 = extractvalue { i32, i8* } %"24_016", 1 + %27 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template, i32 0, i32 0), i32 %25, i8* %26) call void @abort() - store [2 x i64] zeroinitializer, [2 x i64]* %"22_0", align 4 - %"22_014" = load [2 x i64], [2 x i64]* %"22_0", align 4 - store [2 x i64] %"22_014", [2 x i64]* %"08", align 4 - br label %cond_exit_16 - -cond_16_case_1: ; preds = %21 - %"016" = load [2 x i64], [2 x i64]* %"015", align 4 - store [2 x i64] %"016", [2 x i64]* %"24_0", align 4 - %"24_017" = load [2 x i64], [2 x i64]* %"24_0", align 4 - store [2 x i64] %"24_017", [2 x i64]* %"08", align 4 - br label %cond_exit_16 - -cond_exit_16: ; preds = %cond_16_case_1, %cond_16_case_0 - %"09" = load [2 x i64], [2 x i64]* %"08", align 4 - store [2 x i64] %"09", [2 x i64]* %"16_0", align 4 - %"16_018" = load [2 x i64], [2 x i64]* %"16_0", align 4 - %"8_019" = load i64, i64* %"8_0", align 4 - %26 = icmp ult i64 %"8_019", 2 - br i1 %26, label %28, label %27 - -27: ; preds = %cond_exit_16 - store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"020", align 4 - store [2 x i64] %"16_018", [2 x i64]* %"1", align 4 - br label %34 - -28: ; preds = %cond_exit_16 - %29 = alloca i64, i32 2, align 8 - %30 = bitcast i64* %29 to [2 x i64]* - store [2 x i64] %"16_018", [2 x i64]* %30, align 4 - %31 = getelementptr inbounds i64, i64* %29, i64 %"8_019" - %32 = load i64, i64* %31, align 4 - %33 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %32, 1 - store { i1, i64 } %33, { i1, i64 }* %"020", align 4 - store [2 x i64] %"16_018", [2 x i64]* %"1", align 4 - br label %34 - -34: ; preds = %27, %28 - %"021" = load { i1, i64 }, { i1, i64 }* %"020", align 4 - %"122" = load [2 x i64], [2 x i64]* %"1", align 4 - store { i1, i64 } %"021", { i1, i64 }* %"26_0", align 4 - store [2 x i64] %"122", [2 x i64]* %"26_1", align 4 - %"26_023" = load { i1, i64 }, { i1, i64 }* %"26_0", align 4 - %35 = extractvalue { i1, i64 } %"26_023", 0 - switch i1 %35, label %36 [ - i1 true, label %37 + store [2 x i64] zeroinitializer, [2 x i64]* %"25_0", align 4 + %"25_018" = load [2 x i64], [2 x i64]* %"25_0", align 4 + store [2 x i64] %"25_018", [2 x i64]* %"012", align 4 + br label %cond_exit_19 + +cond_19_case_1: ; preds = %23 + %"020" = load [2 x i64], [2 x i64]* %"019", align 4 + store [2 x i64] %"020", [2 x i64]* %"27_0", align 4 + %"27_021" = load [2 x i64], [2 x i64]* %"27_0", align 4 + store [2 x i64] %"27_021", [2 x i64]* %"012", align 4 + br label %cond_exit_19 + +cond_exit_19: ; preds = %cond_19_case_1, %cond_19_case_0 + %"013" = load [2 x i64], [2 x i64]* %"012", align 4 + store [2 x i64] %"013", [2 x i64]* %"19_0", align 4 + %"19_022" = load [2 x i64], [2 x i64]* %"19_0", align 4 + %"8_023" = load i64, i64* %"8_0", align 4 + %28 = icmp ult i64 %"8_023", 2 + br i1 %28, label %30, label %29 + +29: ; preds = %cond_exit_19 + store { i1, i64 } { i1 false, i64 poison }, { i1, i64 }* %"024", align 4 + store [2 x i64] %"19_022", [2 x i64]* %"1", align 4 + br label %36 + +30: ; preds = %cond_exit_19 + %31 = alloca i64, i32 2, align 8 + %32 = bitcast i64* %31 to [2 x i64]* + store [2 x i64] %"19_022", [2 x i64]* %32, align 4 + %33 = getelementptr inbounds i64, i64* %31, i64 %"8_023" + %34 = load i64, i64* %33, align 4 + %35 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %34, 1 + store { i1, i64 } %35, { i1, i64 }* %"024", align 4 + store [2 x i64] %"19_022", [2 x i64]* %"1", align 4 + br label %36 + +36: ; preds = %29, %30 + %"025" = load { i1, i64 }, { i1, i64 }* %"024", align 4 + %"126" = load [2 x i64], [2 x i64]* %"1", align 4 + store { i1, i64 } %"025", { i1, i64 }* %"29_0", align 4 + store [2 x i64] %"126", [2 x i64]* %"29_1", align 4 + %"29_027" = load { i1, i64 }, { i1, i64 }* %"29_0", align 4 + %37 = extractvalue { i1, i64 } %"29_027", 0 + switch i1 %37, label %38 [ + i1 true, label %39 ] -36: ; preds = %34 - br label %cond_28_case_0 +38: ; preds = %36 + br label %cond_31_case_0 -37: ; preds = %34 - %38 = extractvalue { i1, i64 } %"26_023", 1 - store i64 %38, i64* %"028", align 4 - br label %cond_28_case_1 +39: ; preds = %36 + %40 = extractvalue { i1, i64 } %"29_027", 1 + store i64 %40, i64* %"032", align 4 + br label %cond_31_case_1 -cond_28_case_0: ; preds = %36 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"33_0", align 8 - %"33_026" = load { i32, i8* }, { i32, i8* }* %"33_0", align 8 - %39 = extractvalue { i32, i8* } %"33_026", 0 - %40 = extractvalue { i32, i8* } %"33_026", 1 - %41 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %39, i8* %40) +cond_31_case_0: ; preds = %38 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @1, i32 0, i32 0) }, { i32, i8* }* %"36_0", align 8 + %"36_030" = load { i32, i8* }, { i32, i8* }* %"36_0", align 8 + %41 = extractvalue { i32, i8* } %"36_030", 0 + %42 = extractvalue { i32, i8* } %"36_030", 1 + %43 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.1, i32 0, i32 0), i32 %41, i8* %42) call void @abort() - store i64 0, i64* %"34_0", align 4 - %"34_027" = load i64, i64* %"34_0", align 4 - store i64 %"34_027", i64* %"024", align 4 - br label %cond_exit_28 - -cond_28_case_1: ; preds = %37 + store i64 0, i64* %"37_0", align 4 + %"37_031" = load i64, i64* %"37_0", align 4 + store i64 %"37_031", i64* %"028", align 4 + br label %cond_exit_31 + +cond_31_case_1: ; preds = %39 + %"033" = load i64, i64* %"032", align 4 + store i64 %"033", i64* %"39_0", align 4 + %"39_034" = load i64, i64* %"39_0", align 4 + store i64 %"39_034", i64* %"028", align 4 + br label %cond_exit_31 + +cond_exit_31: ; preds = %cond_31_case_1, %cond_31_case_0 %"029" = load i64, i64* %"028", align 4 - store i64 %"029", i64* %"36_0", align 4 - %"36_030" = load i64, i64* %"36_0", align 4 - store i64 %"36_030", i64* %"024", align 4 - br label %cond_exit_28 - -cond_exit_28: ; preds = %cond_28_case_1, %cond_28_case_0 - %"025" = load i64, i64* %"024", align 4 - store i64 %"025", i64* %"28_0", align 4 - %"26_131" = load [2 x i64], [2 x i64]* %"26_1", align 4 - %"10_032" = load i64, i64* %"10_0", align 4 - %"28_033" = load i64, i64* %"28_0", align 4 - %42 = icmp ult i64 %"10_032", 2 - br i1 %42, label %46, label %43 - -43: ; preds = %cond_exit_28 - %44 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"28_033", 1 - %45 = insertvalue { i1, i64, [2 x i64] } %44, [2 x i64] %"26_131", 2 - store { i1, i64, [2 x i64] } %45, { i1, i64, [2 x i64] }* %"034", align 4 - br label %55 - -46: ; preds = %cond_exit_28 - %47 = alloca i64, i32 2, align 8 - %48 = bitcast i64* %47 to [2 x i64]* - store [2 x i64] %"26_131", [2 x i64]* %48, align 4 - %49 = getelementptr inbounds i64, i64* %47, i64 %"10_032" - %50 = load i64, i64* %49, align 4 - store i64 %"28_033", i64* %49, align 4 - %51 = bitcast i64* %47 to [2 x i64]* - %52 = load [2 x i64], [2 x i64]* %51, align 4 - %53 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %50, 1 - %54 = insertvalue { i1, i64, [2 x i64] } %53, [2 x i64] %52, 2 - store { i1, i64, [2 x i64] } %54, { i1, i64, [2 x i64] }* %"034", align 4 - br label %55 - -55: ; preds = %43, %46 - %"035" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"034", align 4 - store { i1, i64, [2 x i64] } %"035", { i1, i64, [2 x i64] }* %"38_0", align 4 - %"38_036" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"38_0", align 4 - %56 = extractvalue { i1, i64, [2 x i64] } %"38_036", 0 - switch i1 %56, label %57 [ - i1 true, label %60 + store i64 %"029", i64* %"31_0", align 4 + %"29_135" = load [2 x i64], [2 x i64]* %"29_1", align 4 + %"10_036" = load i64, i64* %"10_0", align 4 + %"31_037" = load i64, i64* %"31_0", align 4 + %44 = icmp ult i64 %"10_036", 2 + br i1 %44, label %48, label %45 + +45: ; preds = %cond_exit_31 + %46 = insertvalue { i1, i64, [2 x i64] } { i1 false, i64 poison, [2 x i64] poison }, i64 %"31_037", 1 + %47 = insertvalue { i1, i64, [2 x i64] } %46, [2 x i64] %"29_135", 2 + store { i1, i64, [2 x i64] } %47, { i1, i64, [2 x i64] }* %"038", align 4 + br label %57 + +48: ; preds = %cond_exit_31 + %49 = alloca i64, i32 2, align 8 + %50 = bitcast i64* %49 to [2 x i64]* + store [2 x i64] %"29_135", [2 x i64]* %50, align 4 + %51 = getelementptr inbounds i64, i64* %49, i64 %"10_036" + %52 = load i64, i64* %51, align 4 + store i64 %"31_037", i64* %51, align 4 + %53 = bitcast i64* %49 to [2 x i64]* + %54 = load [2 x i64], [2 x i64]* %53, align 4 + %55 = insertvalue { i1, i64, [2 x i64] } { i1 true, i64 poison, [2 x i64] poison }, i64 %52, 1 + %56 = insertvalue { i1, i64, [2 x i64] } %55, [2 x i64] %54, 2 + store { i1, i64, [2 x i64] } %56, { i1, i64, [2 x i64] }* %"038", align 4 + br label %57 + +57: ; preds = %45, %48 + %"039" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"038", align 4 + store { i1, i64, [2 x i64] } %"039", { i1, i64, [2 x i64] }* %"41_0", align 4 + %"41_040" = load { i1, i64, [2 x i64] }, { i1, i64, [2 x i64] }* %"41_0", align 4 + %58 = extractvalue { i1, i64, [2 x i64] } %"41_040", 0 + switch i1 %58, label %59 [ + i1 true, label %62 ] -57: ; preds = %55 - %58 = extractvalue { i1, i64, [2 x i64] } %"38_036", 1 - %59 = extractvalue { i1, i64, [2 x i64] } %"38_036", 2 - store i64 %58, i64* %"041", align 4 - store [2 x i64] %59, [2 x i64]* %"142", align 4 - br label %cond_39_case_0 - -60: ; preds = %55 - %61 = extractvalue { i1, i64, [2 x i64] } %"38_036", 1 - %62 = extractvalue { i1, i64, [2 x i64] } %"38_036", 2 - store i64 %61, i64* %"050", align 4 - store [2 x i64] %62, [2 x i64]* %"151", align 4 - br label %cond_39_case_1 - -cond_39_case_0: ; preds = %57 +59: ; preds = %57 + %60 = extractvalue { i1, i64, [2 x i64] } %"41_040", 1 + %61 = extractvalue { i1, i64, [2 x i64] } %"41_040", 2 + store i64 %60, i64* %"045", align 4 + store [2 x i64] %61, [2 x i64]* %"146", align 4 + br label %cond_42_case_0 + +62: ; preds = %57 + %63 = extractvalue { i1, i64, [2 x i64] } %"41_040", 1 + %64 = extractvalue { i1, i64, [2 x i64] } %"41_040", 2 + store i64 %63, i64* %"054", align 4 + store [2 x i64] %64, [2 x i64]* %"155", align 4 + br label %cond_42_case_1 + +cond_42_case_0: ; preds = %59 + %"047" = load i64, i64* %"045", align 4 + %"148" = load [2 x i64], [2 x i64]* %"146", align 4 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"47_0", align 8 + store i64 %"047", i64* %"44_0", align 4 + store [2 x i64] %"148", [2 x i64]* %"44_1", align 4 + %"47_049" = load { i32, i8* }, { i32, i8* }* %"47_0", align 8 + %"44_050" = load i64, i64* %"44_0", align 4 + %"44_151" = load [2 x i64], [2 x i64]* %"44_1", align 4 + %65 = extractvalue { i32, i8* } %"47_049", 0 + %66 = extractvalue { i32, i8* } %"47_049", 1 + %67 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %65, i8* %66) + call void @abort() + store i64 0, i64* %"48_0", align 4 + store [2 x i64] zeroinitializer, [2 x i64]* %"48_1", align 4 + %"48_052" = load i64, i64* %"48_0", align 4 + %"48_153" = load [2 x i64], [2 x i64]* %"48_1", align 4 + store i64 %"48_052", i64* %"041", align 4 + store [2 x i64] %"48_153", [2 x i64]* %"142", align 4 + br label %cond_exit_42 + +cond_42_case_1: ; preds = %62 + %"056" = load i64, i64* %"054", align 4 + %"157" = load [2 x i64], [2 x i64]* %"155", align 4 + store i64 %"056", i64* %"50_0", align 4 + store [2 x i64] %"157", [2 x i64]* %"50_1", align 4 + %"50_058" = load i64, i64* %"50_0", align 4 + %"50_159" = load [2 x i64], [2 x i64]* %"50_1", align 4 + store i64 %"50_058", i64* %"041", align 4 + store [2 x i64] %"50_159", [2 x i64]* %"142", align 4 + br label %cond_exit_42 + +cond_exit_42: ; preds = %cond_42_case_1, %cond_42_case_0 %"043" = load i64, i64* %"041", align 4 %"144" = load [2 x i64], [2 x i64]* %"142", align 4 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @2, i32 0, i32 0) }, { i32, i8* }* %"44_0", align 8 - store i64 %"043", i64* %"41_0", align 4 - store [2 x i64] %"144", [2 x i64]* %"41_1", align 4 - %"44_045" = load { i32, i8* }, { i32, i8* }* %"44_0", align 8 - %"41_046" = load i64, i64* %"41_0", align 4 - %"41_147" = load [2 x i64], [2 x i64]* %"41_1", align 4 - %63 = extractvalue { i32, i8* } %"44_045", 0 - %64 = extractvalue { i32, i8* } %"44_045", 1 - %65 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.2, i32 0, i32 0), i32 %63, i8* %64) - call void @abort() - store i64 0, i64* %"45_0", align 4 - store [2 x i64] zeroinitializer, [2 x i64]* %"45_1", align 4 - %"45_048" = load i64, i64* %"45_0", align 4 - %"45_149" = load [2 x i64], [2 x i64]* %"45_1", align 4 - store i64 %"45_048", i64* %"037", align 4 - store [2 x i64] %"45_149", [2 x i64]* %"138", align 4 - br label %cond_exit_39 - -cond_39_case_1: ; preds = %60 - %"052" = load i64, i64* %"050", align 4 - %"153" = load [2 x i64], [2 x i64]* %"151", align 4 - store i64 %"052", i64* %"47_0", align 4 - store [2 x i64] %"153", [2 x i64]* %"47_1", align 4 - %"47_054" = load i64, i64* %"47_0", align 4 - %"47_155" = load [2 x i64], [2 x i64]* %"47_1", align 4 - store i64 %"47_054", i64* %"037", align 4 - store [2 x i64] %"47_155", [2 x i64]* %"138", align 4 - br label %cond_exit_39 - -cond_exit_39: ; preds = %cond_39_case_1, %cond_39_case_0 - %"039" = load i64, i64* %"037", align 4 - %"140" = load [2 x i64], [2 x i64]* %"138", align 4 - store i64 %"039", i64* %"39_0", align 4 - store [2 x i64] %"140", [2 x i64]* %"39_1", align 4 - %"39_156" = load [2 x i64], [2 x i64]* %"39_1", align 4 - %66 = alloca i64, i32 2, align 8 - %67 = bitcast i64* %66 to [2 x i64]* - store [2 x i64] %"39_156", [2 x i64]* %67, align 4 - %68 = getelementptr i64, i64* %66, i32 1 - %69 = load i64, i64* %66, align 4 - %70 = bitcast i64* %68 to [1 x i64]* - %71 = load [1 x i64], [1 x i64]* %70, align 4 - %72 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %69, 1 - %73 = insertvalue { i1, i64, [1 x i64] } %72, [1 x i64] %71, 2 - store { i1, i64, [1 x i64] } %73, { i1, i64, [1 x i64] }* %"49_0", align 4 - %"49_057" = load { i1, i64, [1 x i64] }, { i1, i64, [1 x i64] }* %"49_0", align 4 - %74 = extractvalue { i1, i64, [1 x i64] } %"49_057", 0 - switch i1 %74, label %75 [ - i1 true, label %76 + store i64 %"043", i64* %"42_0", align 4 + store [2 x i64] %"144", [2 x i64]* %"42_1", align 4 + %"42_160" = load [2 x i64], [2 x i64]* %"42_1", align 4 + %68 = alloca i64, i32 2, align 8 + %69 = bitcast i64* %68 to [2 x i64]* + store [2 x i64] %"42_160", [2 x i64]* %69, align 4 + %70 = getelementptr i64, i64* %68, i32 1 + %71 = load i64, i64* %68, align 4 + %72 = bitcast i64* %70 to [1 x i64]* + %73 = load [1 x i64], [1 x i64]* %72, align 4 + %74 = insertvalue { i1, i64, [1 x i64] } { i1 true, i64 poison, [1 x i64] poison }, i64 %71, 1 + %75 = insertvalue { i1, i64, [1 x i64] } %74, [1 x i64] %73, 2 + store { i1, i64, [1 x i64] } %75, { i1, i64, [1 x i64] }* %"52_0", align 4 + %"52_061" = load { i1, i64, [1 x i64] }, { i1, i64, [1 x i64] }* %"52_0", align 4 + %76 = extractvalue { i1, i64, [1 x i64] } %"52_061", 0 + switch i1 %76, label %77 [ + i1 true, label %78 ] -75: ; preds = %cond_exit_39 - br label %cond_50_case_0 - -76: ; preds = %cond_exit_39 - %77 = extractvalue { i1, i64, [1 x i64] } %"49_057", 1 - %78 = extractvalue { i1, i64, [1 x i64] } %"49_057", 2 - store i64 %77, i64* %"065", align 4 - store [1 x i64] %78, [1 x i64]* %"166", align 4 - br label %cond_50_case_1 - -cond_50_case_0: ; preds = %75 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"55_0", align 8 - %"55_062" = load { i32, i8* }, { i32, i8* }* %"55_0", align 8 - %79 = extractvalue { i32, i8* } %"55_062", 0 - %80 = extractvalue { i32, i8* } %"55_062", 1 - %81 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %79, i8* %80) +77: ; preds = %cond_exit_42 + br label %cond_53_case_0 + +78: ; preds = %cond_exit_42 + %79 = extractvalue { i1, i64, [1 x i64] } %"52_061", 1 + %80 = extractvalue { i1, i64, [1 x i64] } %"52_061", 2 + store i64 %79, i64* %"069", align 4 + store [1 x i64] %80, [1 x i64]* %"170", align 4 + br label %cond_53_case_1 + +cond_53_case_0: ; preds = %77 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @3, i32 0, i32 0) }, { i32, i8* }* %"58_0", align 8 + %"58_066" = load { i32, i8* }, { i32, i8* }* %"58_0", align 8 + %81 = extractvalue { i32, i8* } %"58_066", 0 + %82 = extractvalue { i32, i8* } %"58_066", 1 + %83 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.3, i32 0, i32 0), i32 %81, i8* %82) call void @abort() - store i64 0, i64* %"56_0", align 4 - store [1 x i64] zeroinitializer, [1 x i64]* %"56_1", align 4 - %"56_063" = load i64, i64* %"56_0", align 4 - %"56_164" = load [1 x i64], [1 x i64]* %"56_1", align 4 - store i64 %"56_063", i64* %"058", align 4 - store [1 x i64] %"56_164", [1 x i64]* %"159", align 4 - br label %cond_exit_50 - -cond_50_case_1: ; preds = %76 - %"067" = load i64, i64* %"065", align 4 - %"168" = load [1 x i64], [1 x i64]* %"166", align 4 - store i64 %"067", i64* %"58_0", align 4 - store [1 x i64] %"168", [1 x i64]* %"58_1", align 4 - %"58_069" = load i64, i64* %"58_0", align 4 - %"58_170" = load [1 x i64], [1 x i64]* %"58_1", align 4 - store i64 %"58_069", i64* %"058", align 4 - store [1 x i64] %"58_170", [1 x i64]* %"159", align 4 - br label %cond_exit_50 - -cond_exit_50: ; preds = %cond_50_case_1, %cond_50_case_0 - %"060" = load i64, i64* %"058", align 4 - %"161" = load [1 x i64], [1 x i64]* %"159", align 4 - store i64 %"060", i64* %"50_0", align 4 - store [1 x i64] %"161", [1 x i64]* %"50_1", align 4 - %"50_171" = load [1 x i64], [1 x i64]* %"50_1", align 4 - %82 = alloca i64, align 8 - %83 = bitcast i64* %82 to [1 x i64]* - store [1 x i64] %"50_171", [1 x i64]* %83, align 4 - %84 = getelementptr i64, i64* %82, i32 0 - %85 = load i64, i64* %84, align 4 - %86 = bitcast i64* %82 to [0 x i64]* - %87 = load [0 x i64], [0 x i64]* %86, align 4 - %88 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %85, 1 - store { i1, i64 } %88, { i1, i64 }* %"60_0", align 4 - %"60_072" = load { i1, i64 }, { i1, i64 }* %"60_0", align 4 - %89 = extractvalue { i1, i64 } %"60_072", 0 - switch i1 %89, label %90 [ - i1 true, label %91 + store i64 0, i64* %"59_0", align 4 + store [1 x i64] zeroinitializer, [1 x i64]* %"59_1", align 4 + %"59_067" = load i64, i64* %"59_0", align 4 + %"59_168" = load [1 x i64], [1 x i64]* %"59_1", align 4 + store i64 %"59_067", i64* %"062", align 4 + store [1 x i64] %"59_168", [1 x i64]* %"163", align 4 + br label %cond_exit_53 + +cond_53_case_1: ; preds = %78 + %"071" = load i64, i64* %"069", align 4 + %"172" = load [1 x i64], [1 x i64]* %"170", align 4 + store i64 %"071", i64* %"61_0", align 4 + store [1 x i64] %"172", [1 x i64]* %"61_1", align 4 + %"61_073" = load i64, i64* %"61_0", align 4 + %"61_174" = load [1 x i64], [1 x i64]* %"61_1", align 4 + store i64 %"61_073", i64* %"062", align 4 + store [1 x i64] %"61_174", [1 x i64]* %"163", align 4 + br label %cond_exit_53 + +cond_exit_53: ; preds = %cond_53_case_1, %cond_53_case_0 + %"064" = load i64, i64* %"062", align 4 + %"165" = load [1 x i64], [1 x i64]* %"163", align 4 + store i64 %"064", i64* %"53_0", align 4 + store [1 x i64] %"165", [1 x i64]* %"53_1", align 4 + %"53_175" = load [1 x i64], [1 x i64]* %"53_1", align 4 + %84 = alloca i64, align 8 + %85 = bitcast i64* %84 to [1 x i64]* + store [1 x i64] %"53_175", [1 x i64]* %85, align 4 + %86 = getelementptr i64, i64* %84, i32 0 + %87 = load i64, i64* %86, align 4 + %88 = bitcast i64* %84 to [0 x i64]* + %89 = load [0 x i64], [0 x i64]* %88, align 4 + %90 = insertvalue { i1, i64 } { i1 true, i64 poison }, i64 %87, 1 + store { i1, i64 } %90, { i1, i64 }* %"63_0", align 4 + %"63_076" = load { i1, i64 }, { i1, i64 }* %"63_0", align 4 + %91 = extractvalue { i1, i64 } %"63_076", 0 + switch i1 %91, label %92 [ + i1 true, label %93 ] -90: ; preds = %cond_exit_50 - br label %cond_61_case_0 - -91: ; preds = %cond_exit_50 - %92 = extractvalue { i1, i64 } %"60_072", 1 - store i64 %92, i64* %"080", align 4 - store [0 x i64] undef, [0 x i64]* %"181", align 4 - br label %cond_61_case_1 - -cond_61_case_0: ; preds = %90 - store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"66_0", align 8 - %"66_077" = load { i32, i8* }, { i32, i8* }* %"66_0", align 8 - %93 = extractvalue { i32, i8* } %"66_077", 0 - %94 = extractvalue { i32, i8* } %"66_077", 1 - %95 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %93, i8* %94) +92: ; preds = %cond_exit_53 + br label %cond_64_case_0 + +93: ; preds = %cond_exit_53 + %94 = extractvalue { i1, i64 } %"63_076", 1 + store i64 %94, i64* %"084", align 4 + store [0 x i64] undef, [0 x i64]* %"185", align 4 + br label %cond_64_case_1 + +cond_64_case_0: ; preds = %92 + store { i32, i8* } { i32 1, i8* getelementptr inbounds ([37 x i8], [37 x i8]* @4, i32 0, i32 0) }, { i32, i8* }* %"69_0", align 8 + %"69_081" = load { i32, i8* }, { i32, i8* }* %"69_0", align 8 + %95 = extractvalue { i32, i8* } %"69_081", 0 + %96 = extractvalue { i32, i8* } %"69_081", 1 + %97 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @prelude.panic_template.4, i32 0, i32 0), i32 %95, i8* %96) call void @abort() - store i64 0, i64* %"67_0", align 4 - store [0 x i64] zeroinitializer, [0 x i64]* %"67_1", align 4 - %"67_078" = load i64, i64* %"67_0", align 4 - %"67_179" = load [0 x i64], [0 x i64]* %"67_1", align 4 - store i64 %"67_078", i64* %"073", align 4 - store [0 x i64] %"67_179", [0 x i64]* %"174", align 4 - br label %cond_exit_61 - -cond_61_case_1: ; preds = %91 - %"082" = load i64, i64* %"080", align 4 - %"183" = load [0 x i64], [0 x i64]* %"181", align 4 - store i64 %"082", i64* %"69_0", align 4 - store [0 x i64] %"183", [0 x i64]* %"69_1", align 4 - %"69_084" = load i64, i64* %"69_0", align 4 - %"69_185" = load [0 x i64], [0 x i64]* %"69_1", align 4 - store i64 %"69_084", i64* %"073", align 4 - store [0 x i64] %"69_185", [0 x i64]* %"174", align 4 - br label %cond_exit_61 - -cond_exit_61: ; preds = %cond_61_case_1, %cond_61_case_0 - %"075" = load i64, i64* %"073", align 4 - %"176" = load [0 x i64], [0 x i64]* %"174", align 4 - store i64 %"075", i64* %"61_0", align 4 - store [0 x i64] %"176", [0 x i64]* %"61_1", align 4 - %"61_186" = load [0 x i64], [0 x i64]* %"61_1", align 4 + store i64 0, i64* %"70_0", align 4 + store [0 x i64] zeroinitializer, [0 x i64]* %"70_1", align 4 + %"70_082" = load i64, i64* %"70_0", align 4 + %"70_183" = load [0 x i64], [0 x i64]* %"70_1", align 4 + store i64 %"70_082", i64* %"077", align 4 + store [0 x i64] %"70_183", [0 x i64]* %"178", align 4 + br label %cond_exit_64 + +cond_64_case_1: ; preds = %93 + %"086" = load i64, i64* %"084", align 4 + %"187" = load [0 x i64], [0 x i64]* %"185", align 4 + store i64 %"086", i64* %"72_0", align 4 + store [0 x i64] %"187", [0 x i64]* %"72_1", align 4 + %"72_088" = load i64, i64* %"72_0", align 4 + %"72_189" = load [0 x i64], [0 x i64]* %"72_1", align 4 + store i64 %"72_088", i64* %"077", align 4 + store [0 x i64] %"72_189", [0 x i64]* %"178", align 4 + br label %cond_exit_64 + +cond_exit_64: ; preds = %cond_64_case_1, %cond_64_case_0 + %"079" = load i64, i64* %"077", align 4 + %"180" = load [0 x i64], [0 x i64]* %"178", align 4 + store i64 %"079", i64* %"64_0", align 4 + store [0 x i64] %"180", [0 x i64]* %"64_1", align 4 + %"64_190" = load [0 x i64], [0 x i64]* %"64_1", align 4 ret void } diff --git a/hugr-llvm/src/extension/collections/stack_array.rs b/hugr-llvm/src/extension/collections/stack_array.rs index 50410d9630..9361a298cd 100644 --- a/hugr-llvm/src/extension/collections/stack_array.rs +++ b/hugr-llvm/src/extension/collections/stack_array.rs @@ -324,6 +324,18 @@ fn emit_array_op<'c, H: HugrView>( } outputs.finish(builder, [array_v.as_basic_value_enum()]) } + ArrayOpDef::unpack => { + let [array_v] = inputs + .try_into() + .map_err(|_| anyhow!("ArrayOpDef::unpack expects one argument"))?; + let array_v = array_v.into_array_value(); + + let result = (0..size) + .map(|i| builder.build_extract_value(array_v, i as u32, "extract")) + .collect::, _>>()?; + + outputs.finish(builder, result) + } ArrayOpDef::get => { let [array_v, index_v] = inputs .try_into() @@ -1222,6 +1234,51 @@ mod test { assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); } + #[rstest] + #[case(&[], 0)] + #[case(&[1, 2], 3)] + #[case(&[6, 6, 6], 18)] + fn exec_unpack( + mut exec_ctx: TestContext, + #[case] array_contents: &[u64], + #[case] expected: u64, + ) { + // We build a HUGR that: + // - Loads an array with the given contents + // - Unpacks all the elements + // - Returns the sum of the elements + + let int_ty = int_type(6); + let hugr = SimpleHugrConfig::new() + .with_outs(int_ty.clone()) + .with_extensions(exec_registry()) + .finish(|mut builder| { + let array = array::ArrayValue::new( + int_ty.clone(), + array_contents + .iter() + .map(|&i| ConstInt::new_u(6, i).unwrap().into()) + .collect_vec(), + ); + let array = builder.add_load_value(array); + let unpacked = builder + .add_array_unpack(int_ty.clone(), array_contents.len() as u64, array) + .unwrap(); + let mut r = builder.add_load_value(ConstInt::new_u(6, 0).unwrap()); + for elem in unpacked { + r = builder.add_iadd(6, r, elem).unwrap(); + } + + builder.finish_with_outputs([r]).unwrap() + }); + exec_ctx.add_extensions(|cge| { + cge.add_default_prelude_extensions() + .add_extension(ArrayCodegenExtension::new(DefaultArrayCodegen)) + .add_default_int_extensions() + }); + assert_eq!(expected, exec_ctx.exec_hugr_u64(hugr, "main")); + } + #[rstest] #[case(5, 42, 0)] #[case(5, 42, 1)] diff --git a/hugr-py/src/hugr/std/_json_defs/collections/array.json b/hugr-py/src/hugr/std/_json_defs/collections/array.json index 641ed9a6a9..3d4e5c3829 100644 --- a/hugr-py/src/hugr/std/_json_defs/collections/array.json +++ b/hugr-py/src/hugr/std/_json_defs/collections/array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.array", "types": { "array": { @@ -729,6 +729,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } } diff --git a/hugr-py/src/hugr/std/_json_defs/collections/value_array.json b/hugr-py/src/hugr/std/_json_defs/collections/value_array.json index 064a3bf9c3..e2904d0330 100644 --- a/hugr-py/src/hugr/std/_json_defs/collections/value_array.json +++ b/hugr-py/src/hugr/std/_json_defs/collections/value_array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.value_array", "types": { "value_array": { @@ -732,6 +732,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.value_array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } } diff --git a/specification/std_extensions/collections/array.json b/specification/std_extensions/collections/array.json index 641ed9a6a9..3d4e5c3829 100644 --- a/specification/std_extensions/collections/array.json +++ b/specification/std_extensions/collections/array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.array", "types": { "array": { @@ -729,6 +729,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } } diff --git a/specification/std_extensions/collections/value_array.json b/specification/std_extensions/collections/value_array.json index 064a3bf9c3..e2904d0330 100644 --- a/specification/std_extensions/collections/value_array.json +++ b/specification/std_extensions/collections/value_array.json @@ -1,5 +1,5 @@ { - "version": "0.1.0", + "version": "0.1.1", "name": "collections.value_array", "types": { "value_array": { @@ -732,6 +732,13 @@ } }, "binary": false + }, + "unpack": { + "extension": "collections.value_array", + "name": "unpack", + "description": "Unpack an array into its elements", + "signature": null, + "binary": true } } } From 394af2f5b5d20d5476ae766d07dba614a891d7f0 Mon Sep 17 00:00:00 2001 From: Lukas Heidemann Date: Mon, 16 Jun 2025 14:47:11 +0100 Subject: [PATCH 12/22] fix: Export metadata in Python (#2342) This PR fixes two bugs in the export to model in Python: - `order_hint` metadata was generated for `dfg` regions but not included in the result. - JSON metadata was not generated for module regions. --- hugr-py/src/hugr/model/export.py | 34 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/hugr-py/src/hugr/model/export.py b/hugr-py/src/hugr/model/export.py index a79ba4f8e3..d93713d2a9 100644 --- a/hugr-py/src/hugr/model/export.py +++ b/hugr-py/src/hugr/model/export.py @@ -62,18 +62,7 @@ def export_node(self, node: Node) -> model.Node | None: inputs = [self.link_name(InPort(node, i)) for i in range(node_data._num_inps)] outputs = [self.link_name(OutPort(node, i)) for i in range(node_data._num_outs)] - meta = [] - - # Export JSON metadata - for meta_name, meta_value in node_data.metadata.items(): - # TODO: Is this the correct way to convert the metadata as JSON? - meta_json = json.dumps(meta_value) - meta.append( - model.Apply( - "compat.meta_json", - [model.Literal(meta_name), model.Literal(meta_json)], - ) - ) + meta = self.export_json_meta(node) # Add an order hint key to the node if necessary if _needs_order_key(self.hugr, node): @@ -373,9 +362,27 @@ def export_node(self, node: Node) -> model.Node | None: error = f"Unknown operation: {op}" raise ValueError(error) + def export_json_meta(self, node: Node) -> list[model.Term]: + """Export the metadata of the node via the JSON compatibility constructor.""" + node_data = self.hugr[node] + meta: list[model.Term] = [] + + for meta_name, meta_value in node_data.metadata.items(): + # TODO: Is this the correct way to convert the metadata as JSON? + meta_json = json.dumps(meta_value) + meta.append( + model.Apply( + "compat.meta_json", + [model.Literal(meta_name), model.Literal(meta_json)], + ) + ) + + return meta + def export_region_module(self, node: Node) -> model.Region: """Export a module node as a module region.""" node_data = self.hugr[node] + meta = self.export_json_meta(node) children = [] for child in node_data.children: @@ -384,7 +391,7 @@ def export_region_module(self, node: Node) -> model.Region: if child_node is not None: children.append(child_node) - return model.Region(kind=model.RegionKind.MODULE, children=children) + return model.Region(kind=model.RegionKind.MODULE, children=children, meta=meta) def export_region_dfg(self, node: Node) -> model.Region: """Export the children of a node as a dataflow region.""" @@ -439,6 +446,7 @@ def export_region_dfg(self, node: Node) -> model.Region: children=children, sources=sources, targets=targets, + meta=meta, ) def export_region_cfg(self, node: Node) -> model.Region: From e0dc98cec01812c218685f3555cd95bf3dc6b724 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 08:03:13 +0200 Subject: [PATCH 13/22] chore(deps-rs): bump the minor group across 1 directory with 3 updates (#2347) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the minor group with 3 updates in the / directory: [proptest](https://github.com/proptest-rs/proptest), [serde_with](https://github.com/jonasbb/serde_with) and [bumpalo](https://github.com/fitzgen/bumpalo). Updates `proptest` from 1.6.0 to 1.7.0
Commits

Updates `serde_with` from 3.12.0 to 3.13.0
Release notes

Sourced from serde_with's releases.

serde_with v3.13.0

Added

  • Added support for schemars v0.9.0 under the schemars_0_9 feature flag by @​swlynch99 (#849)
  • Introduce SerializeDisplayAlt derive macro (#833) An alternative to the SerializeDisplay macro except instead of using the plain formatting like format!("{}", ...), it serializes with the Formatter::alternate flag set to true, like format!("{:#}", ...)

Changed

  • Generalize serde_with::rust::unwrap_or_skip to support deserializing references by @​beroal (#832)
  • Bump MSRV to 1.71, since that is required for the jsonschema dev-dependency.
  • Make serde_conv available without the std feature by @​arilou (#839)
  • Bump MSRV to 1.74, since that is required for schemars v0.9.0 by @​swlynch99 (#849)

Fixed

  • Make the DurationSeconds types and other variants more accessible even without std (#845)
Commits

Updates `bumpalo` from 3.17.0 to 3.18.1
Changelog

Sourced from bumpalo's changelog.

3.18.1

Released 2025-06-05.

Removed

  • Removed the allocator-api2 version bump from 3.18.0, as it was not actually semver compatible.

3.18.0 (yanked)

Released 2025-06-05.

Added

  • Added support for enforcing a minimum alignment on all allocations inside a Bump arena, which can provide speed ups when allocating objects whose alignment is less than or equal to that minimum.
  • Added serde serialization support for bumpalo::collections::String.
  • Added some missing fallible slice allocation function variants.

Changed

  • Replaced extend_from_slice implementation with a formally-verified version that is also faster and more-optimizable for LLVM.
  • Updated allocator-api2 support to version 0.3.*.

Fixed

  • Fixed a bug where the allocated_bytes metrics helper was accidentally including the size of bumpalo's footer, rather than just reporting the user-allocated bytes.

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 66 +++++++++++++++++++++++++++++++++++++----------------- Cargo.toml | 6 ++--- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 466d271f8c..9094eb2209 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,9 +327,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "bytecount" @@ -802,6 +802,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97af9b5f014e228b33e77d75ee0e6e87960124f0f4b16337b586a6bec91867b1" +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + [[package]] name = "either" version = "1.15.0" @@ -1580,7 +1586,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core", + "rand_core 0.6.4", "rand_xoshiro", "sized-chunks", "typenum", @@ -2252,9 +2258,9 @@ dependencies = [ [[package]] name = "proptest" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", @@ -2383,23 +2389,22 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ - "libc", "rand_chacha", - "rand_core", + "rand_core 0.9.3", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.3", ] [[package]] @@ -2407,17 +2412,23 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.3.2", ] [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core", + "rand_core 0.9.3", ] [[package]] @@ -2426,7 +2437,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2676,6 +2687,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2737,15 +2760,16 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", "indexmap 2.9.0", + "schemars", "serde", "serde_derive", "serde_json", @@ -2755,9 +2779,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" dependencies = [ "darling", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 289eb9dd13..7cf3b61730 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ jsonschema = "0.29.1" lazy_static = "1.4.0" num-rational = "0.4.1" paste = "1.0" -proptest = "1.4.0" +proptest = "1.7.0" proptest-derive = "0.5.0" regex = "1.10.6" regex-syntax = "0.8.3" @@ -66,7 +66,7 @@ rstest = "0.24.0" semver = "1.0.26" serde = "1.0.219" serde_json = "1.0.140" -serde_with = "3.12.0" +serde_with = "3.13.0" serde_yaml = "0.9.34" smol_str = "0.3.1" static_assertions = "1.1.0" @@ -82,7 +82,7 @@ assert_fs = "1.1.3" predicates = "3.1.0" indexmap = "2.9.0" fxhash = "0.2.1" -bumpalo = "3.16.0" +bumpalo = "3.18.1" pathsearch = "0.2.0" base64 = "0.22.1" ordered-float = "5.0.0" From 554022ca9f139d252ce32de4d3f627eaf43000ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:38:35 +0100 Subject: [PATCH 14/22] chore(deps-rs): bump the patch group across 1 directory with 4 updates (#2348) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the patch group with 4 updates in the / directory: [clap](https://github.com/clap-rs/clap), [pest](https://github.com/pest-parser/pest), [pest_derive](https://github.com/pest-parser/pest) and [petgraph](https://github.com/petgraph/petgraph). Updates `clap` from 4.5.39 to 4.5.40
Changelog

Sourced from clap's changelog.

[4.5.40] - 2025-06-09

Features

  • Support quoted ids in arg!() macro (e.g. arg!("check-config": ...))
Commits

Updates `pest` from 2.8.0 to 2.8.1
Release notes

Sourced from pest's releases.

v2.8.1

What's Changed

The Minimum Supported Rust Version (MSRV) for default features is 1.80.0 now.

New Contributors

Full Changelog: https://github.com/pest-parser/pest/compare/v2.8.0...v2.8.1

Warning: Semantic Versioning

Note that the node tag feature in 2.6.0 was a technically semver-breaking change even though it is a backwards-compatible / non-breaking change in the meta-grammar. There may be similar non-breaking changes to the meta-grammar between minor versions in the future. These non-breaking changes, however, may translate into semver-breaking changes due to the additional variants propagated from the generated Rule enum.

This new feature caused issues in some Cargo version resolution situations where Cargo mixed different versions of pest dependencies. For this reason, these "grammar non-breaking but semver-breaking" changes are now available only under the "grammar-extras" feature flag. If you would like to use node tags (or other future grammar features), you can do so by enabling this flag on the pest_derive crate in your Cargo.toml:

...
pest_derive = { version = "2.8", features =
["grammar-extras"] }
Commits

Updates `pest_derive` from 2.8.0 to 2.8.1
Release notes

Sourced from pest_derive's releases.

v2.8.1

What's Changed

The Minimum Supported Rust Version (MSRV) for default features is 1.80.0 now.

New Contributors

Full Changelog: https://github.com/pest-parser/pest/compare/v2.8.0...v2.8.1

Warning: Semantic Versioning

Note that the node tag feature in 2.6.0 was a technically semver-breaking change even though it is a backwards-compatible / non-breaking change in the meta-grammar. There may be similar non-breaking changes to the meta-grammar between minor versions in the future. These non-breaking changes, however, may translate into semver-breaking changes due to the additional variants propagated from the generated Rule enum.

This new feature caused issues in some Cargo version resolution situations where Cargo mixed different versions of pest dependencies. For this reason, these "grammar non-breaking but semver-breaking" changes are now available only under the "grammar-extras" feature flag. If you would like to use node tags (or other future grammar features), you can do so by enabling this flag on the pest_derive crate in your Cargo.toml:

...
pest_derive = { version = "2.8", features =
["grammar-extras"] }
Commits

Updates `petgraph` from 0.8.1 to 0.8.2
Release notes

Sourced from petgraph's releases.

petgraph-v0.8.2

This minor release fixes several bugs, adds two new algorithms, slightly improves the performance of maximum_matching, adds a tool for parsing graphs from Dot/Graphviz files, and improves the documentation, making it more complete and uniform, as well as clarifying several points.

Bug Fixes

  • Ford Fulkerson sometimes Panics on StableGraphs (#793)
  • Run Maximal Cliques Quickcheck only on Digraphs which are symmetrical (#800)
  • Run Steiner Tree Quickcheck on the connected components to properly support disconnected graphs (#801)
  • Quickcheck random01 function only outputs 0 (#798)

Documentation

  • Specify that Acyclic::try_udpate_edge may add an edge (#770)
  • Update remove_node doc comment in graphmap.rs (#663)
  • Add examples to minimum spanning tree functions (#808)
  • Minimal typo fix in comments (#803)
  • Update docs.rs (#807)
  • Add note about StableGraph::edge_indices behaviour (#812)
  • Clarification of references to nodes and V (refresh #358) (#814)
  • Fix link and mention Dfs and Bfs as special case in examples (#816)
  • Unify algo docs (#815)

New Features

  • (parser) allow parsing graphs from Dot/Graphviz files (#653)
  • Implement DataMap for GraphMap graphs (#776)
  • Add Johnson's algorithm (#741)
  • Add algorithm to find bridge edges (#590)

Performance

  • Reuse queue allocation in maximum_matching main loop (#817)

Refactor

  • Fix new clippy warnings (#791)

Contributors

... (truncated)

Changelog

Sourced from petgraph's changelog.

0.8.2 - 2025-06-06

This minor release fixes several bugs, adds two new algorithms, slightly improves the performance of maximum_matching, adds a tool for parsing graphs from Dot/Graphviz files, and improves the documentation, making it more complete and uniform, as well as clarifying several points.

Bug Fixes

  • Ford Fulkerson sometimes Panics on StableGraphs (#793)
  • Run Maximal Cliques Quickcheck only on Digraphs which are symmetrical (#800)
  • Run Steiner Tree Quickcheck on the connected components to properly support disconnected graphs (#801)
  • Quickcheck random01 function only outputs 0 (#798)

Documentation

  • Specify that Acyclic::try_udpate_edge may add an edge (#770)
  • Update remove_node doc comment in graphmap.rs (#663)
  • Add examples to minimum spanning tree functions (#808)
  • Minimal typo fix in comments (#803)
  • Update docs.rs (#807)
  • Add note about StableGraph::edge_indices behaviour (#812)
  • Clarification of references to nodes and V (refresh #358) (#814)
  • Fix link and mention Dfs and Bfs as special case in examples (#816)
  • Unify algo docs (#815)

New Features

  • (parser) allow parsing graphs from Dot/Graphviz files (#653)
  • Implement DataMap for GraphMap graphs (#776)
  • Add Johnson's algorithm (#741)
  • Add algorithm to find bridge edges (#590)

Performance

  • Reuse queue allocation in maximum_matching main loop (#817)

Refactor

  • Fix new clippy warnings (#791)
Commits
  • 2f8b190 chore: release v0.8.2 (#768)
  • eeb98c4 docs: Unify algo docs (#815)
  • dd2541c docs: Fix link and mention Dfs and Bfs as special case in examples (#816)
  • 5fdd192 perf: Reuse queue allocation in maximum_matching main loop (#817)
  • d4272c1 docs: Clarification of references to nodes and V (refresh #358) (#814)
  • 90a220b docs: Add note about StableGraph::edge_indices behaviour (#812)
  • f5f791c docs: Update docs.rs (#807)
  • 04b6950 docs: Minimal typo fix in comments (#803)
  • 77ed02b docs: Add examples to minimum spanning tree functions (#808)
  • 1125c33 fix: Quickcheck random01 function only outputs 0 (#798)
  • Additional commits viewable in compare view

Most Recent Ignore Conditions Applied to This Pull Request | Dependency Name | Ignore Conditions | | --- | --- | | petgraph | [< 0.7, > 0.6.3] |
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Agustín Borgna --- Cargo.lock | 592 ++++++++++++++++++++++------------------------------- Cargo.toml | 6 +- 2 files changed, 244 insertions(+), 354 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9094eb2209..0efe26f074 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,22 +13,22 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.2.16", + "getrandom", "once_cell", "serde", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -69,9 +69,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -84,36 +84,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] @@ -222,9 +222,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -232,7 +232,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -258,9 +258,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitmaps" @@ -333,9 +333,9 @@ checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "bytecount" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "byteorder" @@ -366,9 +366,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.20" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -440,9 +440,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -460,9 +460,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -472,9 +472,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", @@ -484,9 +484,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clio" @@ -505,9 +505,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "console" @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.3.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2364b9aa47e460ce9bca6ac1777d14c98eef7e274eb077beed49f3adc94183ed" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" dependencies = [ "proc-macro2", "quote", @@ -865,9 +865,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1048,20 +1048,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -1134,9 +1123,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -1151,9 +1140,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1263,7 +1252,7 @@ dependencies = [ "jsonschema", "lazy_static", "paste", - "petgraph 0.8.1", + "petgraph 0.8.2", "portgraph", "proptest", "proptest-derive", @@ -1296,7 +1285,7 @@ dependencies = [ "insta", "itertools 0.14.0", "lazy_static", - "petgraph 0.8.1", + "petgraph 0.8.2", "portgraph", "rstest", "strum", @@ -1336,7 +1325,7 @@ dependencies = [ "itertools 0.14.0", "lazy_static", "paste", - "petgraph 0.8.1", + "petgraph 0.8.2", "portgraph", "proptest", "proptest-recurse", @@ -1376,17 +1365,21 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -1420,21 +1413,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -1443,31 +1437,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -1475,67 +1449,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1555,9 +1516,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1617,7 +1578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "serde", ] @@ -1688,6 +1649,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -1744,7 +1715,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom", "libc", ] @@ -1791,9 +1762,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" [[package]] name = "linux-raw-sys" @@ -1803,9 +1774,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "llvm-sys" @@ -1822,9 +1793,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1838,9 +1809,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memoffset" @@ -1851,30 +1822,24 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -1983,6 +1948,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "oorandom" version = "11.1.5" @@ -2006,9 +1977,9 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2016,15 +1987,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2041,9 +2012,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror 2.0.12", @@ -2052,9 +2023,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -2062,9 +2033,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", @@ -2075,11 +2046,10 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -2096,12 +2066,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" +checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" dependencies = [ "fixedbitset 0.5.7", - "hashbrown 0.15.2", + "hashbrown 0.15.4", "indexmap 2.9.0", "serde", ] @@ -2154,9 +2124,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portgraph" @@ -2167,11 +2137,20 @@ dependencies = [ "bitvec", "delegate", "itertools 0.14.0", - "petgraph 0.8.1", + "petgraph 0.8.2", "serde", "thiserror 2.0.12", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -2184,7 +2163,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.25", + "zerocopy", ] [[package]] @@ -2377,9 +2356,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -2419,7 +2398,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom", ] [[package]] @@ -2462,9 +2441,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags", ] @@ -2548,16 +2527,16 @@ dependencies = [ "derive_more 0.99.20", "fxhash", "itertools 0.13.0", - "petgraph 0.8.1", + "petgraph 0.8.2", "serde", "thiserror 1.0.69", ] [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64", "bytes", @@ -2569,11 +2548,8 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "serde", @@ -2582,12 +2558,12 @@ dependencies = [ "sync_wrapper", "tokio", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] @@ -2622,9 +2598,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -2643,9 +2619,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", @@ -2656,9 +2632,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rusty-fork" @@ -2804,9 +2780,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2837,18 +2813,15 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smol_str" @@ -2862,9 +2835,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2912,9 +2885,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", @@ -2932,9 +2905,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -2960,7 +2933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -3045,9 +3018,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -3065,9 +3038,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "libc", @@ -3079,15 +3052,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.9.0", "toml_datetime", @@ -3109,6 +3082,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -3133,9 +3124,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -3241,12 +3232,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -3267,9 +3252,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "uuid-simd" @@ -3324,9 +3313,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -3429,15 +3418,15 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -3464,44 +3453,24 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" - -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", -] +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -3527,7 +3496,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3536,7 +3505,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3548,29 +3517,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -3583,12 +3536,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3601,12 +3548,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3619,24 +3560,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3649,12 +3578,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3667,12 +3590,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3685,12 +3602,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3703,17 +3614,11 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -3727,17 +3632,11 @@ dependencies = [ "bitflags", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wyz" @@ -3756,9 +3655,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -3768,9 +3667,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -3778,33 +3677,13 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", -] - [[package]] name = "zerocopy" version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.25", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "zerocopy-derive", ] [[package]] @@ -3839,11 +3718,22 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -3852,9 +3742,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7cf3b61730..b123c9897d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ strum = "0.27.0" tempfile = "3.20" thiserror = "2.0.12" typetag = "0.2.20" -clap = { version = "4.5.39" } +clap = { version = "4.5.40" } clio = "0.3.5" clap-verbosity-flag = "3.0.3" assert_cmd = "2.0.17" @@ -86,8 +86,8 @@ bumpalo = "3.18.1" pathsearch = "0.2.0" base64 = "0.22.1" ordered-float = "5.0.0" -pest = "2.8.0" -pest_derive = "2.8.0" +pest = "2.8.1" +pest_derive = "2.8.1" pretty = "0.12.4" pretty_assertions = "1.4.1" zstd = "0.13.2" From 0060976192276025352feb51c35ea2d73b83c637 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 17 Jun 2025 17:16:40 +0100 Subject: [PATCH 15/22] refactor(types.rs): rm incorrect comment and unnecessary allow-unused (#2340) These are misleading; it's now used by hugr-core import/export, which seems fine. --- hugr-core/src/types.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 6fa0596237..2b36233133 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -25,8 +25,6 @@ use smol_str::SmolStr; pub use type_param::TypeArg; pub use type_row::{TypeRow, TypeRowRV}; -// Unused in --no-features -#[allow(unused_imports)] pub(crate) use poly_func::PolyFuncTypeBase; use itertools::FoldWhile::{Continue, Done}; From d1953400eefa9ad65ca5cbe1a6a7b4ef7d3533b3 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 18 Jun 2025 13:44:53 +0100 Subject: [PATCH 16/22] feat: Deprecate invalidation_set, add invalidated_nodes and SimpleReplacement::invalidation_set (#2358) Currently a Patch(Verification) has to report its `invalidation_set` without being able to see the Hugr on which it will act (and without being able to verify itself against that Hugr - well the set is meaningless if it doesn't verify/apply, but nonetheless). This would be really useful in #2290 for one... So, this PR adds a new method `invalidated_nodes` which is like `invalidation_set` but takes an `&impl HugrView`; and deprecates the old `invalidation_set`. Annoyingly deprecating a method in a trait doesn't flag up *implementations* of the method, only calls to it. So the breaking change follow-up to remove invalidation_set (and *require* an implementation of invalidated_nodes) will be significantly breaking. (If we didn't have the default now, this would be breaking and the follow-up still too, however). For `SimpleReplacement` I've added the method but outside the trait, for use in PersistentHugr. (It looks a little odd but the deprecation warning kicks in only if you call the trait method, and the non-trait method seems to be preferred when both are in scope.) --- hugr-core/src/hugr/patch.rs | 30 ++++++++++++++++----- hugr-core/src/hugr/patch/consts.rs | 14 +++++++--- hugr-core/src/hugr/patch/inline_call.rs | 2 +- hugr-core/src/hugr/patch/inline_dfg.rs | 7 +++-- hugr-core/src/hugr/patch/insert_cut.rs | 7 +++-- hugr-core/src/hugr/patch/insert_identity.rs | 5 +++- hugr-core/src/hugr/patch/outline_cfg.rs | 5 +++- hugr-core/src/hugr/patch/replace.rs | 5 +++- hugr-core/src/hugr/patch/simple_replace.rs | 14 +++++++--- hugr-core/src/hugr/persistent.rs | 2 +- 10 files changed, 69 insertions(+), 22 deletions(-) diff --git a/hugr-core/src/hugr/patch.rs b/hugr-core/src/hugr/patch.rs index dac266205c..6e22dda98f 100644 --- a/hugr-core/src/hugr/patch.rs +++ b/hugr-core/src/hugr/patch.rs @@ -33,12 +33,27 @@ pub trait PatchVerification { /// error. fn verify(&self, h: &impl HugrView) -> Result<(), Self::Error>; - /// Returns a set of nodes referenced by the rewrite. Modifying any of these - /// nodes will invalidate it. + /// The nodes invalidated by the rewrite. Deprecated: implement + /// [Self::invalidated_nodes] instead. The default returns the empty + /// iterator; this should be fine as there are no external calls. + #[deprecated(note = "Use/implement invalidated_nodes instead")] + fn invalidation_set(&self) -> impl Iterator { + std::iter::empty() + } + + /// Returns the nodes removed or altered by the rewrite. Modifying any of these + /// nodes will invalidate the rewrite. /// - /// Two `impl Rewrite`s can be composed if their invalidation sets are + /// Two `impl Rewrite`s can be composed if their `invalidated_nodes` are /// disjoint. - fn invalidation_set(&self) -> impl Iterator; + fn invalidated_nodes( + &self, + h: &impl HugrView, + ) -> impl Iterator { + let _ = h; + #[expect(deprecated)] + self.invalidation_set() + } } /// A patch that can be applied to a mutable Hugr of type `H`. @@ -142,8 +157,11 @@ impl PatchVerification for Transactional { } #[inline] - fn invalidation_set(&self) -> impl Iterator { - self.underlying.invalidation_set() + fn invalidated_nodes( + &self, + h: &impl HugrView, + ) -> impl Iterator { + self.underlying.invalidated_nodes(h) } } diff --git a/hugr-core/src/hugr/patch/consts.rs b/hugr-core/src/hugr/patch/consts.rs index 8e39fab906..dcdeea52f5 100644 --- a/hugr-core/src/hugr/patch/consts.rs +++ b/hugr-core/src/hugr/patch/consts.rs @@ -46,7 +46,10 @@ impl PatchVerification for RemoveLoadConstant { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.0) } } @@ -93,7 +96,10 @@ impl PatchVerification for RemoveConst { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.0) } } @@ -158,7 +164,7 @@ mod test { let remove_1 = RemoveLoadConstant(load_1_node); assert_eq!( - remove_1.invalidation_set().exactly_one().ok(), + remove_1.invalidated_nodes(&h).exactly_one().ok(), Some(load_1_node) ); @@ -166,7 +172,7 @@ mod test { let remove_con = RemoveConst(con_node); assert_eq!( - remove_con.invalidation_set().exactly_one().ok(), + remove_con.invalidated_nodes(&h).exactly_one().ok(), Some(con_node) ); diff --git a/hugr-core/src/hugr/patch/inline_call.rs b/hugr-core/src/hugr/patch/inline_call.rs index b6beb8d459..40eac06e0e 100644 --- a/hugr-core/src/hugr/patch/inline_call.rs +++ b/hugr-core/src/hugr/patch/inline_call.rs @@ -52,7 +52,7 @@ impl PatchVerification for InlineCall { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes(&self, _: &impl HugrView) -> impl Iterator { Some(self.0).into_iter() } } diff --git a/hugr-core/src/hugr/patch/inline_dfg.rs b/hugr-core/src/hugr/patch/inline_dfg.rs index a4ef76f2c5..bc14ae8af1 100644 --- a/hugr-core/src/hugr/patch/inline_dfg.rs +++ b/hugr-core/src/hugr/patch/inline_dfg.rs @@ -4,7 +4,7 @@ use super::{PatchHugrMut, PatchVerification}; use crate::ops::handle::{DfgID, NodeHandle}; -use crate::{IncomingPort, Node, OutgoingPort, PortIndex}; +use crate::{HugrView, IncomingPort, Node, OutgoingPort, PortIndex}; /// Structure identifying an `InlineDFG` rewrite from the spec pub struct InlineDFG(pub DfgID); @@ -43,7 +43,10 @@ impl PatchVerification for InlineDFG { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { [self.0.node()].into_iter() } } diff --git a/hugr-core/src/hugr/patch/insert_cut.rs b/hugr-core/src/hugr/patch/insert_cut.rs index f85348b4d8..518c75cc92 100644 --- a/hugr-core/src/hugr/patch/insert_cut.rs +++ b/hugr-core/src/hugr/patch/insert_cut.rs @@ -118,7 +118,10 @@ impl PatchVerification for InsertCut { } #[inline] - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.parent) .chain(self.targets.iter().map(|(n, _)| *n)) .unique() @@ -179,7 +182,7 @@ mod tests { let targets: Vec<_> = h.all_linked_inputs(i).collect(); let inserter = InsertCut::new(h.entrypoint(), targets, replacement); assert_eq!( - inserter.invalidation_set().collect::>(), + inserter.invalidated_nodes(&h).collect::>(), vec![h.entrypoint(), o] ); diff --git a/hugr-core/src/hugr/patch/insert_identity.rs b/hugr-core/src/hugr/patch/insert_identity.rs index f1e4048402..725a0dd3c0 100644 --- a/hugr-core/src/hugr/patch/insert_identity.rs +++ b/hugr-core/src/hugr/patch/insert_identity.rs @@ -67,7 +67,10 @@ impl PatchVerification for IdentityInsertion { } #[inline] - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { iter::once(self.post_node) } } diff --git a/hugr-core/src/hugr/patch/outline_cfg.rs b/hugr-core/src/hugr/patch/outline_cfg.rs index 0e74b4e572..e0d5a27850 100644 --- a/hugr-core/src/hugr/patch/outline_cfg.rs +++ b/hugr-core/src/hugr/patch/outline_cfg.rs @@ -97,7 +97,10 @@ impl PatchVerification for OutlineCfg { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { self.blocks.iter().copied() } } diff --git a/hugr-core/src/hugr/patch/replace.rs b/hugr-core/src/hugr/patch/replace.rs index 7f70ba2b60..ba4104ad9a 100644 --- a/hugr-core/src/hugr/patch/replace.rs +++ b/hugr-core/src/hugr/patch/replace.rs @@ -320,7 +320,10 @@ impl PatchVerification for Replacement { Ok(()) } - fn invalidation_set(&self) -> impl Iterator { + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { self.removal.iter().copied() } } diff --git a/hugr-core/src/hugr/patch/simple_replace.rs b/hugr-core/src/hugr/patch/simple_replace.rs index c2111e849d..8fb22febda 100644 --- a/hugr-core/src/hugr/patch/simple_replace.rs +++ b/hugr-core/src/hugr/patch/simple_replace.rs @@ -519,6 +519,11 @@ impl SimpleReplacement { let subgraph = subgraph.map_nodes(node_map); SimpleReplacement::new_unchecked(subgraph, replacement.clone()) } + + /// Allows to get the [Self::invalidated_nodes] without requiring a [HugrView]. + pub fn invalidation_set(&self) -> impl Iterator { + self.subgraph.nodes().iter().copied() + } } impl PatchVerification for SimpleReplacement { @@ -530,8 +535,11 @@ impl PatchVerification for SimpleReplacement { } #[inline] - fn invalidation_set(&self) -> impl Iterator { - self.subgraph.nodes().iter().copied() + fn invalidated_nodes( + &self, + _: &impl HugrView, + ) -> impl Iterator { + self.invalidation_set() } } @@ -867,7 +875,7 @@ pub(in crate::hugr::patch) mod test { // Check invalidation set assert_eq!( - HashSet::<_>::from_iter(r.invalidation_set()), + HashSet::<_>::from_iter(r.invalidated_nodes(&h)), HashSet::<_>::from_iter([h_node_cx, h_node_h0, h_node_h1]), ); diff --git a/hugr-core/src/hugr/persistent.rs b/hugr-core/src/hugr/persistent.rs index 94061300fe..09f3b26872 100644 --- a/hugr-core/src/hugr/persistent.rs +++ b/hugr-core/src/hugr/persistent.rs @@ -85,7 +85,7 @@ pub use resolver::PointerEqResolver; use crate::{ Hugr, HugrView, IncomingPort, Node, OutgoingPort, Port, SimpleReplacement, - hugr::patch::{Patch, PatchVerification, simple_replace}, + hugr::patch::{Patch, simple_replace}, }; /// A replacement operation that can be applied to a [`PersistentHugr`]. From 939c08276e3b033cb6af0b48ccc860e97c9b8fdf Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 18 Jun 2025 14:49:00 +0100 Subject: [PATCH 17/22] feat: Rewrite for peeling a TailLoop (#2290) Replaces the TailLoop with a DFG containing the body plus a Conditional, inside one Case of which there is a copy of the original TailLoop. (Uses `HugrMut::copy_descendants` to copy incoming edges without copying their sources.) Letting the new DFG take the Node of the old TailLoop allows to preserve all existing wiring (including e.g. order edges for nonlocals). closes #2107 --- hugr-core/src/hugr/patch.rs | 1 + hugr-core/src/hugr/patch/peel_loop.rs | 298 ++++++++++++++++++++++++++ hugr-core/src/ops/controlflow.rs | 8 +- 3 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 hugr-core/src/hugr/patch/peel_loop.rs diff --git a/hugr-core/src/hugr/patch.rs b/hugr-core/src/hugr/patch.rs index 6e22dda98f..4273c9c8a7 100644 --- a/hugr-core/src/hugr/patch.rs +++ b/hugr-core/src/hugr/patch.rs @@ -6,6 +6,7 @@ pub mod inline_dfg; pub mod insert_cut; pub mod insert_identity; pub mod outline_cfg; +pub mod peel_loop; mod port_types; pub mod replace; pub mod simple_replace; diff --git a/hugr-core/src/hugr/patch/peel_loop.rs b/hugr-core/src/hugr/patch/peel_loop.rs new file mode 100644 index 0000000000..ccb9218283 --- /dev/null +++ b/hugr-core/src/hugr/patch/peel_loop.rs @@ -0,0 +1,298 @@ +//! Rewrite to peel one iteration of a [TailLoop], creating a [DFG] containing a copy of +//! the loop body, and a [Conditional] containing the original `TailLoop` node. +use derive_more::{Display, Error}; + +use crate::core::HugrNode; +use crate::ops::{ + Case, Conditional, DFG, DataflowOpTrait, Input, OpTrait, OpType, Output, TailLoop, +}; +use crate::types::Signature; +use crate::{Direction, HugrView, Node}; + +use super::{HugrMut, PatchHugrMut, PatchVerification}; + +/// Rewrite that peels one iteration of a [TailLoop] by turning the +/// iteration test into a [Conditional]. +#[derive(Clone, Debug, PartialEq)] +pub struct PeelTailLoop(N); + +/// Error in performing [`PeelTailLoop`] rewrite. +#[derive(Clone, Debug, Display, Error, PartialEq)] +#[non_exhaustive] +pub enum PeelTailLoopError { + /// The specified Node was not a [`TailLoop`] + #[display("Node to peel {node} expected to be a TailLoop but actually {op}")] + NotTailLoop { + /// The node requested to peel + node: N, + /// The actual (non-tail-loop) operation + op: OpType, + }, +} + +impl PeelTailLoop { + /// Create a new instance that will peel the specified [TailLoop] node + pub fn new(node: N) -> Self { + Self(node) + } +} + +impl PatchVerification for PeelTailLoop { + type Error = PeelTailLoopError; + type Node = N; + fn verify(&self, h: &impl HugrView) -> Result<(), Self::Error> { + let opty = h.get_optype(self.0); + if !opty.is_tail_loop() { + return Err(PeelTailLoopError::NotTailLoop { + node: self.0, + op: opty.clone(), + }); + } + Ok(()) + } + + fn invalidated_nodes(&self, h: &impl HugrView) -> impl Iterator { + h.get_io(self.0) + .into_iter() + .flat_map(|[_, output]| [self.0, output].into_iter()) + } +} + +impl PatchHugrMut for PeelTailLoop { + type Outcome = (); + fn apply_hugr_mut(self, h: &mut impl HugrMut) -> Result<(), Self::Error> { + self.verify(h)?; // Now we know we have a TailLoop! + let loop_ty = h.optype_mut(self.0); + let signature = loop_ty.dataflow_signature().unwrap().into_owned(); + // Replace the TailLoop with a DFG - this maintains all external connections + let OpType::TailLoop(tl) = std::mem::replace(loop_ty, DFG { signature }.into()) else { + panic!("Wasn't a TailLoop ?!") + }; + let sum_rows = Vec::from(tl.control_variants()); + let rest = tl.rest.clone(); + let Signature { + input: loop_in, + output: loop_out, + } = tl.signature().into_owned(); + + // Copy the DFG (ex-TailLoop) children into a new TailLoop *before* we add any more + let new_loop = h.add_node_after(self.0, tl); // Temporary parent + h.copy_descendants(self.0, new_loop, None); + + // Add conditional inside DFG. + let [_, dfg_out] = h.get_io(self.0).unwrap(); + let cond = Conditional { + sum_rows, + other_inputs: rest, + outputs: loop_out.clone(), + }; + let case_in_rows = [0, 1].map(|i| cond.case_input_row(i).unwrap()); + // This preserves all edges from the end of the loop body to the conditional: + h.replace_op(dfg_out, cond); + let cond_n = dfg_out; + h.add_ports(cond_n, Direction::Outgoing, loop_out.len() as isize + 1); + let dfg_out = h.add_node_before( + cond_n, + Output { + types: loop_out.clone(), + }, + ); + for p in 0..loop_out.len() { + h.connect(cond_n, p, dfg_out, p) + } + + // Now wire up the internals of the Conditional + let cases = case_in_rows.map(|in_row| { + let signature = Signature::new(in_row.clone(), loop_out.clone()); + let n = h.add_node_with_parent(cond_n, Case { signature }); + h.add_node_with_parent(n, Input { types: in_row }); + let types = loop_out.clone(); + h.add_node_with_parent(n, Output { types }); + n + }); + + h.set_parent(new_loop, cases[TailLoop::CONTINUE_TAG]); + let [ctn_in, ctn_out] = h.get_io(cases[TailLoop::CONTINUE_TAG]).unwrap(); + let [brk_in, brk_out] = h.get_io(cases[TailLoop::BREAK_TAG]).unwrap(); + for p in 0..loop_out.len() { + h.connect(brk_in, p, brk_out, p); + h.connect(new_loop, p, ctn_out, p) + } + for p in 0..loop_in.len() { + h.connect(ctn_in, p, new_loop, p); + } + Ok(()) + } + + /// Failure only occurs if the node is not a [TailLoop]. + /// (Any later failure means an invalid Hugr and `panic`.) + const UNCHANGED_ON_FAILURE: bool = true; +} + +#[cfg(test)] +mod test { + use itertools::Itertools; + + use crate::builder::test::simple_dfg_hugr; + use crate::builder::{ + Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, + }; + use crate::extension::prelude::{bool_t, usize_t}; + use crate::ops::{OpTag, OpTrait, Tag, TailLoop, handle::NodeHandle}; + use crate::std_extensions::arithmetic::int_types::INT_TYPES; + use crate::types::{Signature, Type, TypeRow}; + use crate::{HugrView, hugr::HugrMut}; + + use super::{PeelTailLoop, PeelTailLoopError}; + + #[test] + fn bad_peel() { + let backup = simple_dfg_hugr(); + let op = backup.entrypoint_optype().clone(); + assert!(!op.is_tail_loop()); + let mut h = backup.clone(); + let r = h.apply_patch(PeelTailLoop::new(h.entrypoint())); + assert_eq!( + r, + Err(PeelTailLoopError::NotTailLoop { + node: backup.entrypoint(), + op + }) + ); + assert_eq!(h, backup); + } + + #[test] + fn peel_loop_incoming_edges() { + let i32_t = || INT_TYPES[5].clone(); + let mut fb = FunctionBuilder::new( + "main", + Signature::new(vec![bool_t(), usize_t(), i32_t()], usize_t()), + ) + .unwrap(); + let helper = fb + .module_root_builder() + .declare( + "helper", + Signature::new( + vec![bool_t(), usize_t(), i32_t()], + vec![Type::new_sum([vec![bool_t(); 2], vec![]]), usize_t()], + ) + .into(), + ) + .unwrap(); + let [b, u, i] = fb.input_wires_arr(); + let (tl, call) = { + let mut tlb = fb + .tail_loop_builder( + [(bool_t(), b), (bool_t(), b)], + [(usize_t(), u)], + TypeRow::new(), + ) + .unwrap(); + let [b, _, u] = tlb.input_wires_arr(); + // Static edge from FuncDecl, and 'ext' edge from function Input: + let c = tlb.call(&helper, &[], [b, u, i]).unwrap(); + let [pred, other] = c.outputs_arr(); + (tlb.finish_with_outputs(pred, [other]).unwrap(), c.node()) + }; + let mut h = fb.finish_hugr_with_outputs(tl.outputs()).unwrap(); + + h.apply_patch(PeelTailLoop::new(tl.node())).unwrap(); + h.validate().unwrap(); + + assert_eq!( + h.nodes() + .filter(|n| h.get_optype(*n).is_tail_loop()) + .count(), + 1 + ); + use OpTag::*; + assert_eq!(tags(&h, call), [FnCall, Dfg, FuncDefn, ModuleRoot]); + let [c1, c2] = h + .all_linked_inputs(helper.node()) + .map(|(n, _p)| n) + .collect_array() + .unwrap(); + assert!([c1, c2].contains(&call)); + let other = if call == c1 { c2 } else { c1 }; + assert_eq!( + tags(&h, other), + [ + FnCall, + TailLoop, + Case, + Conditional, + Dfg, + FuncDefn, + ModuleRoot + ] + ); + } + + fn tags(h: &H, n: H::Node) -> Vec { + let mut v = Vec::new(); + let mut o = Some(n); + while let Some(n) = o { + v.push(h.get_optype(n).tag()); + o = h.get_parent(n); + } + v + } + + #[test] + fn peel_loop_order_output() { + let i16_t = || INT_TYPES[4].clone(); + let mut fb = + FunctionBuilder::new("main", Signature::new(vec![i16_t(), bool_t()], i16_t())).unwrap(); + + let [i, b] = fb.input_wires_arr(); + let tl = { + let mut tlb = fb + .tail_loop_builder([(i16_t(), i), (bool_t(), b)], [], i16_t().into()) + .unwrap(); + let [i, _b] = tlb.input_wires_arr(); + // This loop only goes round once. However, we do not expect this to affect + // peeling: *dataflow analysis* can tell us that the conditional will always + // take one Case (that does not contain the TailLoop), we do not do that here. + let [cont] = tlb + .add_dataflow_op( + Tag::new( + TailLoop::BREAK_TAG, + tlb.loop_signature().unwrap().control_variants().into(), + ), + [i], + ) + .unwrap() + .outputs_arr(); + tlb.finish_with_outputs(cont, []).unwrap() + }; + let [i2] = tl.outputs_arr(); + // Create a DFG (no inputs, one output) that reads the result of the TailLoop via an 'ext` edge + let dfg = fb + .dfg_builder(Signature::new(vec![], i16_t()), []) + .unwrap() + .finish_with_outputs([i2]) + .unwrap(); + let mut h = fb.finish_hugr_with_outputs(dfg.outputs()).unwrap(); + let tl = tl.node(); + + h.apply_patch(PeelTailLoop::new(tl)).unwrap(); + h.validate().unwrap(); + let [tl] = h + .nodes() + .filter(|n| h.get_optype(*n).is_tail_loop()) + .collect_array() + .unwrap(); + { + use OpTag::*; + assert_eq!( + tags(&h, tl), + [TailLoop, Case, Conditional, Dfg, FuncDefn, ModuleRoot] + ); + } + let [out_n] = h.output_neighbours(tl).collect_array().unwrap(); + assert!(h.get_optype(out_n).is_output()); + assert_eq!(h.get_parent(tl), h.get_parent(out_n)); + } +} diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 9c05740a5c..ca358c624b 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -59,12 +59,16 @@ impl TailLoop { /// Build the output `TypeRow` of the child graph of a `TailLoop` node. pub(crate) fn body_output_row(&self) -> TypeRow { - let sum_type = Type::new_sum([self.just_inputs.clone(), self.just_outputs.clone()]); - let mut outputs = vec![sum_type]; + let mut outputs = vec![Type::new_sum(self.control_variants())]; outputs.extend_from_slice(&self.rest); outputs.into() } + /// The variants (continue / break) of the first output from the child graph + pub(crate) fn control_variants(&self) -> [TypeRow; 2] { + [self.just_inputs.clone(), self.just_outputs.clone()] + } + /// Build the input `TypeRow` of the child graph of a `TailLoop` node. pub(crate) fn body_input_row(&self) -> TypeRow { self.just_inputs.extend(self.rest.iter()) From 507a2557fc37dc1367c9265eebbb850392b74248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Borgna?= <121866228+aborgna-q@users.noreply.github.com> Date: Mon, 23 Jun 2025 15:05:52 +0100 Subject: [PATCH 18/22] feat: Create Module/FunctionBuilders from existing Hugrs (#2359) So we can use the builder machinery to make new definitions on an existing hugr. --- hugr-core/src/builder/dataflow.rs | 25 +++++++++++++++++++++++++ hugr-core/src/builder/module.rs | 21 +++++++++++++++++++++ hugr-core/src/hugr/patch/peel_loop.rs | 20 +++++++++++--------- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/hugr-core/src/builder/dataflow.rs b/hugr-core/src/builder/dataflow.rs index 0f7f078ec7..2a0fdf9315 100644 --- a/hugr-core/src/builder/dataflow.rs +++ b/hugr-core/src/builder/dataflow.rs @@ -259,6 +259,31 @@ impl FunctionBuilder { } } +impl + AsRef> FunctionBuilder { + /// Initialize a new function definition on the root module of an existing HUGR. + /// + /// The HUGR's entrypoint will **not** be modified. + /// + /// # Errors + /// + /// Error in adding DFG child nodes. + pub fn with_hugr( + mut hugr: B, + name: impl Into, + signature: impl Into, + ) -> Result { + let signature: PolyFuncType = signature.into(); + let body = signature.body().clone(); + let op = ops::FuncDefn::new(name, signature); + + let module = hugr.as_ref().module_root(); + let func = hugr.as_mut().add_node_with_parent(module, op); + + let db = DFGBuilder::create_with_io(hugr, func, body)?; + Ok(Self::from_dfg_builder(db)) + } +} + impl + AsRef, T> Container for DFGWrapper { #[inline] fn container_node(&self) -> Node { diff --git a/hugr-core/src/builder/module.rs b/hugr-core/src/builder/module.rs index 47bba818d2..543b9f2c1e 100644 --- a/hugr-core/src/builder/module.rs +++ b/hugr-core/src/builder/module.rs @@ -57,6 +57,12 @@ impl HugrBuilder for ModuleBuilder { } impl + AsRef> ModuleBuilder { + /// Continue building a module from an existing hugr. + #[must_use] + pub fn with_hugr(hugr: T) -> Self { + ModuleBuilder(hugr) + } + /// Replace a [`ops::FuncDecl`] with [`ops::FuncDefn`] and return a builder for /// the defining graph. /// @@ -233,4 +239,19 @@ mod test { assert_matches!(build_result, Ok(_)); Ok(()) } + + #[test] + fn builder_from_existing() -> Result<(), BuildError> { + let hugr = Hugr::new(); + + let fn_builder = FunctionBuilder::with_hugr(hugr, "main", Signature::new_endo(vec![]))?; + let mut hugr = fn_builder.finish_hugr()?; + + let mut module_builder = ModuleBuilder::with_hugr(&mut hugr); + module_builder.declare("other", Signature::new_endo(vec![]).into())?; + + hugr.validate()?; + + Ok(()) + } } diff --git a/hugr-core/src/hugr/patch/peel_loop.rs b/hugr-core/src/hugr/patch/peel_loop.rs index ccb9218283..9cf61290b6 100644 --- a/hugr-core/src/hugr/patch/peel_loop.rs +++ b/hugr-core/src/hugr/patch/peel_loop.rs @@ -135,7 +135,7 @@ mod test { use crate::builder::test::simple_dfg_hugr; use crate::builder::{ - Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, + Container, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, }; use crate::extension::prelude::{bool_t, usize_t}; use crate::ops::{OpTag, OpTrait, Tag, TailLoop, handle::NodeHandle}; @@ -165,13 +165,8 @@ mod test { #[test] fn peel_loop_incoming_edges() { let i32_t = || INT_TYPES[5].clone(); - let mut fb = FunctionBuilder::new( - "main", - Signature::new(vec![bool_t(), usize_t(), i32_t()], usize_t()), - ) - .unwrap(); - let helper = fb - .module_root_builder() + let mut mb = crate::builder::ModuleBuilder::new(); + let helper = mb .declare( "helper", Signature::new( @@ -181,6 +176,12 @@ mod test { .into(), ) .unwrap(); + let mut fb = mb + .define_function( + "main", + Signature::new(vec![bool_t(), usize_t(), i32_t()], usize_t()), + ) + .unwrap(); let [b, u, i] = fb.input_wires_arr(); let (tl, call) = { let mut tlb = fb @@ -196,7 +197,8 @@ mod test { let [pred, other] = c.outputs_arr(); (tlb.finish_with_outputs(pred, [other]).unwrap(), c.node()) }; - let mut h = fb.finish_hugr_with_outputs(tl.outputs()).unwrap(); + let _ = fb.finish_with_outputs(tl.outputs()).unwrap(); + let mut h = mb.finish_hugr().unwrap(); h.apply_patch(PeelTailLoop::new(tl.node())).unwrap(); h.validate().unwrap(); From 67c83bbb79fa3dea040f3c37b6fba4e04c3ff9ea Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Tue, 24 Jun 2025 17:12:48 +0100 Subject: [PATCH 19/22] docs: fix doc links in persistent --- hugr-core/src/hugr/persistent.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hugr-core/src/hugr/persistent.rs b/hugr-core/src/hugr/persistent.rs index 09f3b26872..d2813e11b6 100644 --- a/hugr-core/src/hugr/persistent.rs +++ b/hugr-core/src/hugr/persistent.rs @@ -60,6 +60,9 @@ //! To obtain a [`PersistentHugr`] from your state space, use //! [`CommitStateSpace::try_extract_hugr`]. A [`PersistentHugr`] can always be //! materialized into a [`Hugr`] type using [`PersistentHugr::to_hugr`]. +//! +//! +//! [`PatchVerification`]: crate::hugr::patch::PatchVerification mod parents_view; mod resolver; @@ -264,6 +267,9 @@ impl<'a> From<&'a RelRc> for &'a Commit { /// /// Currently, only patches that apply to subgraphs within dataflow regions /// are supported. +/// +/// [`PatchVerification`]: crate::hugr::patch::PatchVerification + #[derive(Clone, Debug)] pub struct PersistentHugr { /// The state space of all commits. From 562705019241a781069acb98873f0f5df3139af2 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Wed, 25 Jun 2025 13:00:05 +0100 Subject: [PATCH 20/22] feat: better errors using metadata from generator (#2368) Introduces two new metadata keys: `__generator`: encode the tooling that generated the HUGR. Used in some decoding errors to provide context if available. Currently only used for JSON loading, should be extended to model. See also https://github.com/CQCL/guppylang/pull/1039 `__used_extensions`: encode names and versions of extensions used in the HUGR, used to detect breaking version mismatch with loaded extensions. See also https://github.com/CQCL/guppylang/pull/1049 Closes #2351 Recommend reviewing individual commits. --- hugr-core/src/envelope.rs | 287 ++++++++++++++++++++++++- hugr-core/src/envelope/package_json.rs | 18 +- hugr-model/src/v0/binary/read.rs | 4 +- specification/hugr.md | 31 +++ 4 files changed, 329 insertions(+), 11 deletions(-) diff --git a/hugr-core/src/envelope.rs b/hugr-core/src/envelope.rs index 077980cc5f..aba8892cf9 100644 --- a/hugr-core/src/envelope.rs +++ b/hugr-core/src/envelope.rs @@ -47,12 +47,16 @@ pub mod serde_with; pub use header::{EnvelopeConfig, EnvelopeFormat, MAGIC_NUMBERS, ZstdConfig}; pub use package_json::PackageEncodingError; -use crate::Hugr; -use crate::{extension::ExtensionRegistry, package::Package}; +use crate::{Hugr, HugrView}; +use crate::{ + extension::{ExtensionRegistry, Version}, + package::Package, +}; use header::EnvelopeHeader; use std::io::BufRead; use std::io::Write; use std::str::FromStr; +use thiserror::Error; #[allow(unused_imports)] use itertools::Itertools as _; @@ -60,6 +64,51 @@ use itertools::Itertools as _; use crate::import::ImportError; use crate::{Extension, import::import_package}; +/// Key used to store the name of the generator that produced the envelope. +pub const GENERATOR_KEY: &str = "__generator"; + +/// Get the name of the generator from the metadata of the HUGR modules. +/// If multiple modules have different generators, a comma-separated list is returned in +/// module order. +/// If no generator is found, `None` is returned. +fn get_generator(modules: &[H]) -> Option { + let generators: Vec = modules + .iter() + .filter_map(|hugr| hugr.get_metadata(hugr.module_root(), GENERATOR_KEY)) + .map(|v| v.to_string()) + .collect(); + if generators.is_empty() { + return None; + } + + Some(generators.join(", ")) +} + +fn gen_str(generator: &Option) -> String { + match generator { + Some(g) => format!("\ngenerated by {g}"), + None => String::new(), + } +} + +/// Wrap an error with a generator string. +#[derive(Error, Debug)] +#[error("{inner}{}", gen_str(&self.generator))] +pub struct WithGenerator { + inner: E, + /// The name of the generator that produced the envelope, if any. + generator: Option, +} + +impl WithGenerator { + fn new(err: E, modules: &[impl HugrView]) -> Self { + Self { + inner: err, + generator: get_generator(modules), + } + } +} + /// Read a HUGR envelope from a reader. /// /// Returns the deserialized package and the configuration used to encode it. @@ -210,6 +259,7 @@ pub enum EnvelopeError { ModelImport { /// The source error. source: ImportError, + // TODO add generator to model import errors }, /// Error reading a HUGR model payload. ModelRead { @@ -391,7 +441,7 @@ fn encode_model<'h>( _ => unreachable!(), } - // Apend extensions for binary model. + // Append extensions for binary model. if format == EnvelopeFormat::ModelWithExtensions { serde_json::to_writer(writer, &extensions.iter().collect_vec())?; } @@ -399,6 +449,88 @@ fn encode_model<'h>( Ok(()) } +/// Key used to store the list of used extensions in the metadata of a HUGR. +pub const USED_EXTENSIONS_KEY: &str = "__used_extensions"; + +#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)] +struct UsedExtension { + name: String, + version: Version, +} + +#[derive(Debug, Error)] +#[error( + "Extension '{name}' version mismatch: registered version is {registered}, but used version is {used}" +)] +/// Error raised when the reported used version of an extension +/// does not match the registered version in the extension registry. +pub struct ExtensionVersionMismatch { + name: String, + registered: Version, + used: Version, +} + +#[derive(Debug, Error)] +#[non_exhaustive] +/// Error raised when checking for breaking changes in used extensions. +pub enum ExtensionBreakingError { + /// The extension version in the metadata does not match the registered version. + #[error("{0}")] + ExtensionVersionMismatch(ExtensionVersionMismatch), + + /// Error deserializing the used extensions metadata. + #[error("Failed to deserialize used extensions metadata")] + Deserialization(#[from] serde_json::Error), +} +/// If HUGR metadata contains a list of used extensions, under the key [`USED_EXTENSIONS_KEY`], +/// and extension is registered in the given registry, check that the +/// version of the extension in the metadata matches the registered version (up to +/// MAJOR.MINOR). +fn check_breaking_extensions( + hugr: impl crate::HugrView, + registry: &ExtensionRegistry, +) -> Result<(), ExtensionBreakingError> { + let Some(exts) = hugr.get_metadata(hugr.module_root(), USED_EXTENSIONS_KEY) else { + return Ok(()); // No used extensions metadata, nothing to check + }; + let used_exts: Vec = serde_json::from_value(exts.clone())?; // TODO handle errors properly + + for ext in used_exts { + let Some(registered) = registry.get(ext.name.as_str()) else { + continue; // Extension not registered, ignore + }; + if !compatible_versions(registered.version(), &ext.version) { + // This is a breaking change, raise an error. + + return Err(ExtensionBreakingError::ExtensionVersionMismatch( + ExtensionVersionMismatch { + name: ext.name, + registered: registered.version().clone(), + used: ext.version, + }, + )); + } + } + + Ok(()) +} + +/// Check if two versions are compatible according to: +/// - Major version must match. +/// - If major version is 0, minor version must match. +fn compatible_versions(v1: &Version, v2: &Version) -> bool { + if v1.major != v2.major { + return false; // Major version mismatch + } + + if v1.major == 0 { + // For major version 0, we only allow minor version matches + return v1.minor == v2.minor; + } + + true +} + #[cfg(test)] pub(crate) mod test { use super::*; @@ -409,9 +541,13 @@ pub(crate) mod test { use crate::HugrView; use crate::builder::test::{multi_module_package, simple_package}; - use crate::extension::PRELUDE_REGISTRY; + use crate::extension::{Extension, ExtensionRegistry, Version}; + use crate::extension::{ExtensionId, PRELUDE_REGISTRY}; + use crate::hugr::HugrMut; use crate::hugr::test::check_hugr_equality; use crate::std_extensions::STD_REG; + use serde_json::json; + use std::sync::Arc; /// Returns an `ExtensionRegistry` with the extensions from both /// sets. Avoids cloning if the first one already contains all @@ -527,4 +663,147 @@ pub(crate) mod test { assert_eq!(package, new_package); } + + #[rstest] + #[case::simple(simple_package())] + fn test_check_breaking_extensions(#[case] mut package: Package) { + // extension with major version 0 + let test_ext_v0 = + Extension::new(ExtensionId::new_unchecked("test-v0"), Version::new(0, 2, 3)); + // extension with major version > 0 + let test_ext_v1 = + Extension::new(ExtensionId::new_unchecked("test-v1"), Version::new(1, 2, 3)); + + // Create a registry with the test extensions + let registry = + ExtensionRegistry::new([Arc::new(test_ext_v0.clone()), Arc::new(test_ext_v1.clone())]); + let mut hugr = package.modules.remove(0); + + // No metadata - should pass + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Matching version for v0 - should pass + let used_exts = json!([{ "name": "test-v0", "version": "0.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Matching major/minor but different patch for v0 - should pass + let used_exts = json!([{ "name": "test-v0", "version": "0.2.4" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + //Different minor version for v0 - should fail + let used_exts = json!([{ "name": "test-v0", "version": "0.3.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v0" && registered == Version::new(0, 2, 3) && used == Version::new(0, 3, 3) + ); + + // Different major version for v0 - should fail + let used_exts = json!([{ "name": "test-v0", "version": "1.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v0" && registered == Version::new(0, 2, 3) && used == Version::new(1, 2, 3) + ); + + // Matching version for v1 - should pass + let used_exts = json!([{ "name": "test-v1", "version": "1.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Different minor version for v1 - should pass + let used_exts = json!([{ "name": "test-v1", "version": "1.3.0" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Different patch for v1 - should pass + let used_exts = json!([{ "name": "test-v1", "version": "1.2.4" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Different major version for v1 - should fail + let used_exts = json!([{ "name": "test-v1", "version": "2.2.3" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v1" && registered == Version::new(1, 2, 3) && used == Version::new(2, 2, 3) + ); + + // Non-registered extension - should pass + let used_exts = json!([{ "name": "unknown", "version": "1.0.0" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + + // Multiple extensions - one mismatch should fail + let used_exts = json!([ + { "name": "unknown", "version": "1.0.0" }, + { "name": "test-v1", "version": "2.0.0" } + ]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::ExtensionVersionMismatch(ExtensionVersionMismatch { + name, + registered, + used + })) if name == "test-v1" && registered == Version::new(1, 2, 3) && used == Version::new(2, 0, 0) + ); + + // Invalid metadata format - should fail with deserialization error + hugr.set_metadata( + hugr.module_root(), + USED_EXTENSIONS_KEY, + json!("not an array"), + ); + assert_matches!( + check_breaking_extensions(&hugr, ®istry), + Err(ExtensionBreakingError::Deserialization(_)) + ); + + // Multiple extensions with all compatible versions - should pass + let used_exts = json!([ + { "name": "test-v0", "version": "0.2.5" }, + { "name": "test-v1", "version": "1.9.9" } + ]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + assert_matches!(check_breaking_extensions(&hugr, ®istry), Ok(())); + } + + #[test] + fn test_with_generator_error_message() { + let test_ext = Extension::new(ExtensionId::new_unchecked("test"), Version::new(1, 0, 0)); + let registry = ExtensionRegistry::new([Arc::new(test_ext)]); + + let mut hugr = simple_package().modules.remove(0); + + // Set a generator name in the metadata + let generator_name = json!({ "name": "TestGenerator", "version": "1.2.3" }); + hugr.set_metadata(hugr.module_root(), GENERATOR_KEY, generator_name.clone()); + + // Set incompatible extension version in metadata + let used_exts = json!([{ "name": "test", "version": "2.0.0" }]); + hugr.set_metadata(hugr.module_root(), USED_EXTENSIONS_KEY, used_exts); + + // Create the error and wrap it with WithGenerator + let err = check_breaking_extensions(&hugr, ®istry).unwrap_err(); + let with_gen = WithGenerator::new(err, &[&hugr]); + + let err_msg = with_gen.to_string(); + assert!(err_msg.contains("Extension 'test' version mismatch")); + assert!(err_msg.contains(generator_name.to_string().as_str())); + } } diff --git a/hugr-core/src/envelope/package_json.rs b/hugr-core/src/envelope/package_json.rs index 6a2b62b75e..bbdf19d26e 100644 --- a/hugr-core/src/envelope/package_json.rs +++ b/hugr-core/src/envelope/package_json.rs @@ -3,6 +3,7 @@ use derive_more::{Display, Error, From}; use itertools::Itertools; use std::io; +use super::{ExtensionBreakingError, WithGenerator, check_breaking_extensions}; use crate::extension::ExtensionRegistry; use crate::extension::resolution::ExtensionResolutionError; use crate::hugr::ExtensionError; @@ -21,19 +22,24 @@ pub(super) fn from_json_reader( extensions: pkg_extensions, } = serde_json::from_value::(val.clone())?; let mut modules = modules.into_iter().map(|h| h.0).collect_vec(); - let pkg_extensions = ExtensionRegistry::new_with_extension_resolution( pkg_extensions, &extension_registry.into(), - )?; + ) + .map_err(|err| WithGenerator::new(err, &modules))?; // Resolve the operations in the modules using the defined registries. let mut combined_registry = extension_registry.clone(); combined_registry.extend(&pkg_extensions); - for module in &mut modules { - module.resolve_extension_defs(&combined_registry)?; + for module in &modules { + check_breaking_extensions(module, &combined_registry) + .map_err(|err| WithGenerator::new(err, &modules))?; } + modules + .iter_mut() + .try_for_each(|module| module.resolve_extension_defs(&combined_registry)) + .map_err(|err| WithGenerator::new(err, &modules))?; Ok(Package { modules, @@ -64,7 +70,9 @@ pub enum PackageEncodingError { /// Error raised while reading from a file. IOError(io::Error), /// Could not resolve the extension needed to encode the hugr. - ExtensionResolution(ExtensionResolutionError), + ExtensionResolution(WithGenerator), + /// Error raised while checking for breaking extension version mismatch. + ExtensionVersion(WithGenerator), /// Could not resolve the runtime extensions for the hugr. RuntimeExtensionResolution(ExtensionError), } diff --git a/hugr-model/src/v0/binary/read.rs b/hugr-model/src/v0/binary/read.rs index 5d16089947..001a805cc9 100644 --- a/hugr-model/src/v0/binary/read.rs +++ b/hugr-model/src/v0/binary/read.rs @@ -5,12 +5,12 @@ use bumpalo::Bump; use bumpalo::collections::Vec as BumpVec; use std::io::BufRead; -/// An error encounted while deserialising a model. +/// An error encountered while deserialising a model. #[derive(Debug, derive_more::From, derive_more::Display, derive_more::Error)] #[non_exhaustive] pub enum ReadError { #[from(forward)] - /// An error encounted while decoding a model from a `capnproto` buffer. + /// An error encountered while decoding a model from a `capnproto` buffer. DecodingError(capnp::Error), } diff --git a/specification/hugr.md b/specification/hugr.md index 3bd22b8efd..80b1ce76b6 100644 --- a/specification/hugr.md +++ b/specification/hugr.md @@ -778,6 +778,37 @@ existing metadata, given the node ID. `Ports` (for port metadata) or `History` (for use by the rewrite engine)? +#### Generator Metadata +Tooling generating HUGR can specify some reserved metadata keys to be used for debugging +purposes. + +The key `__generator` when used on the module root node is +used to specify the tooling used to generate the module. +The associated value must be an object/dictionary containing the fields `name` +and `version`, each with string values. Extra fields may be used to include +additional data about generating tooling that may be useful for debugging. Example: + +```json +{ + "__generator": { "name": "my_compiler", "version": "1.0.0" } +} +``` + +The key `__used_extensions` when used on the module root node is +used to specify the names and versions of all the extensions used in the module. +Some of these may correspond to extensions packaged with the module, but they +may also be extensions the consuming tooling has pre-loaded. They can be used by the +tooling to check for extension version mismatches. The value associated with the key +must be an array of objects/dictionaries containing the keys `name` and `version`, each +with string values. Example: +```json +{ + "__used_extensions": [{ "name": "my_ext", "version": "2.2.3" }] +} +``` + + + **TODO** Do we allow per-port metadata (using the same mechanism?) **TODO** What about references to ports? Should we add a list of port From 0a919616ff084b49407dfcd65654a6976e29281f Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Wed, 25 Jun 2025 13:47:41 +0100 Subject: [PATCH 21/22] feat: use `core.` prefixes for generator metadata keys (#2371) --- hugr-core/src/envelope.rs | 7 +++---- specification/hugr.md | 11 +++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hugr-core/src/envelope.rs b/hugr-core/src/envelope.rs index aba8892cf9..0223267b85 100644 --- a/hugr-core/src/envelope.rs +++ b/hugr-core/src/envelope.rs @@ -65,7 +65,9 @@ use crate::import::ImportError; use crate::{Extension, import::import_package}; /// Key used to store the name of the generator that produced the envelope. -pub const GENERATOR_KEY: &str = "__generator"; +pub const GENERATOR_KEY: &str = "core.generator"; +/// Key used to store the list of used extensions in the metadata of a HUGR. +pub const USED_EXTENSIONS_KEY: &str = "core.used_extensions"; /// Get the name of the generator from the metadata of the HUGR modules. /// If multiple modules have different generators, a comma-separated list is returned in @@ -449,9 +451,6 @@ fn encode_model<'h>( Ok(()) } -/// Key used to store the list of used extensions in the metadata of a HUGR. -pub const USED_EXTENSIONS_KEY: &str = "__used_extensions"; - #[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)] struct UsedExtension { name: String, diff --git a/specification/hugr.md b/specification/hugr.md index 80b1ce76b6..dc8251f30a 100644 --- a/specification/hugr.md +++ b/specification/hugr.md @@ -778,11 +778,14 @@ existing metadata, given the node ID. `Ports` (for port metadata) or `History` (for use by the rewrite engine)? +Reserved metadata keys used by the HUGR tooling are prefixed with `core.`. +Use of this prefix by external tooling may cause issues. + #### Generator Metadata Tooling generating HUGR can specify some reserved metadata keys to be used for debugging purposes. -The key `__generator` when used on the module root node is +The key `core.generator` when used on the module root node is used to specify the tooling used to generate the module. The associated value must be an object/dictionary containing the fields `name` and `version`, each with string values. Extra fields may be used to include @@ -790,11 +793,11 @@ additional data about generating tooling that may be useful for debugging. Examp ```json { - "__generator": { "name": "my_compiler", "version": "1.0.0" } + "core.generator": { "name": "my_compiler", "version": "1.0.0" } } ``` -The key `__used_extensions` when used on the module root node is +The key `core.used_extensions` when used on the module root node is used to specify the names and versions of all the extensions used in the module. Some of these may correspond to extensions packaged with the module, but they may also be extensions the consuming tooling has pre-loaded. They can be used by the @@ -803,7 +806,7 @@ must be an array of objects/dictionaries containing the keys `name` and `version with string values. Example: ```json { - "__used_extensions": [{ "name": "my_ext", "version": "2.2.3" }] + "core.used_extensions": [{ "name": "my_ext", "version": "2.2.3" }] } ``` From 59b013cd7a16402d8aa4c85df90036c951d574c7 Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Wed, 25 Jun 2025 14:28:35 +0100 Subject: [PATCH 22/22] chore: release-plz update --- Cargo.lock | 12 ++++++------ hugr-cli/CHANGELOG.md | 6 ++++++ hugr-cli/Cargo.toml | 4 ++-- hugr-core/CHANGELOG.md | 22 ++++++++++++++++++++++ hugr-core/Cargo.toml | 4 ++-- hugr-llvm/CHANGELOG.md | 10 ++++++++++ hugr-llvm/Cargo.toml | 4 ++-- hugr-model/CHANGELOG.md | 6 ++++++ hugr-model/Cargo.toml | 2 +- hugr-passes/CHANGELOG.md | 6 ++++++ hugr-passes/Cargo.toml | 4 ++-- hugr-py/Cargo.toml | 2 +- hugr/CHANGELOG.md | 26 ++++++++++++++++++++++++++ hugr/Cargo.toml | 10 +++++----- 14 files changed, 97 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0efe26f074..4981ad7340 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1201,7 +1201,7 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hugr" -version = "0.20.1" +version = "0.20.2" dependencies = [ "bumpalo", "criterion", @@ -1215,7 +1215,7 @@ dependencies = [ [[package]] name = "hugr-cli" -version = "0.20.1" +version = "0.20.2" dependencies = [ "anyhow", "assert_cmd", @@ -1234,7 +1234,7 @@ dependencies = [ [[package]] name = "hugr-core" -version = "0.20.1" +version = "0.20.2" dependencies = [ "cgmath", "cool_asserts", @@ -1274,7 +1274,7 @@ dependencies = [ [[package]] name = "hugr-llvm" -version = "0.20.1" +version = "0.20.2" dependencies = [ "anyhow", "delegate", @@ -1293,7 +1293,7 @@ dependencies = [ [[package]] name = "hugr-model" -version = "0.20.1" +version = "0.20.2" dependencies = [ "base64", "bumpalo", @@ -1317,7 +1317,7 @@ dependencies = [ [[package]] name = "hugr-passes" -version = "0.20.1" +version = "0.20.2" dependencies = [ "ascent", "derive_more 1.0.0", diff --git a/hugr-cli/CHANGELOG.md b/hugr-cli/CHANGELOG.md index 141b60f73e..5bbc26a608 100644 --- a/hugr-cli/CHANGELOG.md +++ b/hugr-cli/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-cli-v0.20.1...hugr-cli-v0.20.2) - 2025-06-25 + +### New Features + +- *(cli)* convert sub-command for converting envelope formats ([#2331](https://github.com/CQCL/hugr/pull/2331)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-cli-v0.20.0...hugr-cli-v0.20.1) - 2025-06-03 ### New Features diff --git a/hugr-cli/Cargo.toml b/hugr-cli/Cargo.toml index 43f44b040b..a05666cc0a 100644 --- a/hugr-cli/Cargo.toml +++ b/hugr-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-cli" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } license = { workspace = true } @@ -19,7 +19,7 @@ bench = false clap = { workspace = true, features = ["derive", "cargo"] } clap-verbosity-flag.workspace = true derive_more = { workspace = true, features = ["display", "error", "from"] } -hugr = { path = "../hugr", version = "0.20.1" } +hugr = { path = "../hugr", version = "0.20.2" } serde_json.workspace = true clio = { workspace = true, features = ["clap-parse"] } anyhow.workspace = true diff --git a/hugr-core/CHANGELOG.md b/hugr-core/CHANGELOG.md index f712d16633..9026431a7a 100644 --- a/hugr-core/CHANGELOG.md +++ b/hugr-core/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-core-v0.20.1...hugr-core-v0.20.2) - 2025-06-25 + +### Documentation + +- fix doc links in persistent + +### New Features + +- Add serial data types for SimpleReplacement and PersistentHugr ([#2300](https://github.com/CQCL/hugr/pull/2300)) +- Add MermaidFormatter to replace RenderConfig ([#2275](https://github.com/CQCL/hugr/pull/2275)) +- *(core)* builder pattern for EnvelopeConfig ([#2330](https://github.com/CQCL/hugr/pull/2330)) +- *(core, llvm)* add array unpack operations ([#2339](https://github.com/CQCL/hugr/pull/2339)) +- Deprecate invalidation_set, add invalidated_nodes and SimpleReplacement::invalidation_set ([#2358](https://github.com/CQCL/hugr/pull/2358)) +- Rewrite for peeling a TailLoop ([#2290](https://github.com/CQCL/hugr/pull/2290)) +- Create Module/FunctionBuilders from existing Hugrs ([#2359](https://github.com/CQCL/hugr/pull/2359)) +- better errors using metadata from generator ([#2368](https://github.com/CQCL/hugr/pull/2368)) +- use `core.` prefixes for generator metadata keys ([#2371](https://github.com/CQCL/hugr/pull/2371)) + +### Refactor + +- *(types.rs)* rm incorrect comment and unnecessary allow-unused ([#2340](https://github.com/CQCL/hugr/pull/2340)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-core-v0.20.0...hugr-core-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr-core/Cargo.toml b/hugr-core/Cargo.toml index 3e6279fbd7..a365c73e19 100644 --- a/hugr-core/Cargo.toml +++ b/hugr-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-core" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } @@ -30,7 +30,7 @@ name = "model" name = "persistent_walker_example" [dependencies] -hugr-model = { version = "0.20.1", path = "../hugr-model" } +hugr-model = { version = "0.20.2", path = "../hugr-model" } cgmath = { workspace = true, features = ["serde"] } delegate = { workspace = true } diff --git a/hugr-llvm/CHANGELOG.md b/hugr-llvm/CHANGELOG.md index 4584d7246d..0ed47be85a 100644 --- a/hugr-llvm/CHANGELOG.md +++ b/hugr-llvm/CHANGELOG.md @@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-llvm-v0.20.1...hugr-llvm-v0.20.2) - 2025-06-25 + +### New Features + +- *(core, llvm)* add array unpack operations ([#2339](https://github.com/CQCL/hugr/pull/2339)) + +### Refactor + +- *(llvm)* replace HashMap with BTreeMap ([#2313](https://github.com/CQCL/hugr/pull/2313)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-llvm-v0.20.0...hugr-llvm-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr-llvm/Cargo.toml b/hugr-llvm/Cargo.toml index ac8dcbd958..31bd533960 100644 --- a/hugr-llvm/Cargo.toml +++ b/hugr-llvm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-llvm" -version = "0.20.1" +version = "0.20.2" description = "A general and extensible crate for lowering HUGRs into LLVM IR" edition.workspace = true @@ -26,7 +26,7 @@ workspace = true [dependencies] inkwell = { version = "0.6.0", default-features = false } -hugr-core = { path = "../hugr-core", version = "0.20.1" } +hugr-core = { path = "../hugr-core", version = "0.20.2" } anyhow = "1.0.98" itertools.workspace = true delegate.workspace = true diff --git a/hugr-model/CHANGELOG.md b/hugr-model/CHANGELOG.md index b17ebd1774..901f077d14 100644 --- a/hugr-model/CHANGELOG.md +++ b/hugr-model/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-model-v0.20.1...hugr-model-v0.20.2) - 2025-06-25 + +### New Features + +- better errors using metadata from generator ([#2368](https://github.com/CQCL/hugr/pull/2368)) + ## [0.20.0](https://github.com/CQCL/hugr/compare/hugr-model-v0.19.0...hugr-model-v0.20.0) - 2025-05-14 ### New Features diff --git a/hugr-model/Cargo.toml b/hugr-model/Cargo.toml index 9f9b046b5e..24c1f9e42e 100644 --- a/hugr-model/Cargo.toml +++ b/hugr-model/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-model" -version = "0.20.1" +version = "0.20.2" readme = "README.md" documentation = "https://docs.rs/hugr-model/" description = "Data model for Quantinuum's HUGR intermediate representation" diff --git a/hugr-passes/CHANGELOG.md b/hugr-passes/CHANGELOG.md index 44af68ad29..d5f2921845 100644 --- a/hugr-passes/CHANGELOG.md +++ b/hugr-passes/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-passes-v0.20.1...hugr-passes-v0.20.2) - 2025-06-25 + +### Bug Fixes + +- update CallGraph and remove_dead_funcs for module-only FuncDefns ([#2336](https://github.com/CQCL/hugr/pull/2336)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-passes-v0.20.0...hugr-passes-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr-passes/Cargo.toml b/hugr-passes/Cargo.toml index d751d82d57..8c1daafbcd 100644 --- a/hugr-passes/Cargo.toml +++ b/hugr-passes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr-passes" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } license = { workspace = true } @@ -19,7 +19,7 @@ workspace = true bench = false [dependencies] -hugr-core = { path = "../hugr-core", version = "0.20.1" } +hugr-core = { path = "../hugr-core", version = "0.20.2" } portgraph = { workspace = true } ascent = { version = "0.8.0" } derive_more = { workspace = true, features = ["display", "error", "from"] } diff --git a/hugr-py/Cargo.toml b/hugr-py/Cargo.toml index b47f054641..27020c8122 100644 --- a/hugr-py/Cargo.toml +++ b/hugr-py/Cargo.toml @@ -21,6 +21,6 @@ bench = false [dependencies] bumpalo = { workspace = true, features = ["collections"] } -hugr-model = { version = "0.20.1", path = "../hugr-model", features = ["pyo3"] } +hugr-model = { version = "0.20.2", path = "../hugr-model", features = ["pyo3"] } paste.workspace = true pyo3 = { workspace = true, features = ["extension-module", "abi3-py310"] } diff --git a/hugr/CHANGELOG.md b/hugr/CHANGELOG.md index 412a04cf7d..46439ca695 100644 --- a/hugr/CHANGELOG.md +++ b/hugr/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## [0.20.2](https://github.com/CQCL/hugr/compare/hugr-v0.20.1...hugr-v0.20.2) - 2025-06-25 + +### Bug Fixes + +- update CallGraph and remove_dead_funcs for module-only FuncDefns ([#2336](https://github.com/CQCL/hugr/pull/2336)) + +### Documentation + +- fix doc links in persistent + +### New Features + +- Add serial data types for SimpleReplacement and PersistentHugr ([#2300](https://github.com/CQCL/hugr/pull/2300)) +- Add MermaidFormatter to replace RenderConfig ([#2275](https://github.com/CQCL/hugr/pull/2275)) +- *(core, llvm)* add array unpack operations ([#2339](https://github.com/CQCL/hugr/pull/2339)) +- Deprecate invalidation_set, add invalidated_nodes and SimpleReplacement::invalidation_set ([#2358](https://github.com/CQCL/hugr/pull/2358)) +- Rewrite for peeling a TailLoop ([#2290](https://github.com/CQCL/hugr/pull/2290)) +- Create Module/FunctionBuilders from existing Hugrs ([#2359](https://github.com/CQCL/hugr/pull/2359)) +- better errors using metadata from generator ([#2368](https://github.com/CQCL/hugr/pull/2368)) +- use `core.` prefixes for generator metadata keys ([#2371](https://github.com/CQCL/hugr/pull/2371)) +- *(core)* builder pattern for EnvelopeConfig ([#2330](https://github.com/CQCL/hugr/pull/2330)) + +### Refactor + +- *(types.rs)* rm incorrect comment and unnecessary allow-unused ([#2340](https://github.com/CQCL/hugr/pull/2340)) + ## [0.20.1](https://github.com/CQCL/hugr/compare/hugr-v0.20.0...hugr-v0.20.1) - 2025-06-03 ### Bug Fixes diff --git a/hugr/Cargo.toml b/hugr/Cargo.toml index 5cd388586d..d43ee4ae90 100644 --- a/hugr/Cargo.toml +++ b/hugr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hugr" -version = "0.20.1" +version = "0.20.2" edition = { workspace = true } rust-version = { workspace = true } @@ -30,10 +30,10 @@ llvm-test = ["hugr-llvm/llvm14-0", "hugr-llvm/test-utils"] zstd = ["hugr-core/zstd"] [dependencies] -hugr-model = { path = "../hugr-model", version = "0.20.1" } -hugr-core = { path = "../hugr-core", version = "0.20.1" } -hugr-passes = { path = "../hugr-passes", version = "0.20.1" } -hugr-llvm = { path = "../hugr-llvm", version = "0.20.1", optional = true } +hugr-model = { path = "../hugr-model", version = "0.20.2" } +hugr-core = { path = "../hugr-core", version = "0.20.2" } +hugr-passes = { path = "../hugr-passes", version = "0.20.2" } +hugr-llvm = { path = "../hugr-llvm", version = "0.20.2", optional = true } [dev-dependencies] lazy_static = { workspace = true }