Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1f05f19
feat: Define pass application scopes
aborgna-q Dec 19, 2025
aba6477
Stub implementation for all passes
aborgna-q Dec 19, 2025
3e0c6ac
Python definition for PassScope
aborgna-q Dec 19, 2025
02d2886
Add default with_scope impl, make this non-breaking
aborgna-q Dec 22, 2025
e3a63b4
Move scope to a submodule of ::composable
aborgna-q Dec 22, 2025
0b730bf
wip
acl-cqc Jan 23, 2026
e71125c
All=>Preserve (make global), add PreserveEntrypoint, enum InScope
acl-cqc Jan 23, 2026
11a643e
docs, re-export InScope
acl-cqc Jan 23, 2026
51bfb59
Add PassScope::from_entrypoint
acl-cqc Feb 11, 2026
c1a1898
note idempotency in composablepass/::then
acl-cqc Feb 18, 2026
90172c5
redefine, make tests pass
acl-cqc Feb 19, 2026
d678210
test renaming
acl-cqc Feb 19, 2026
d6eba57
docs, re-export
acl-cqc Feb 19, 2026
3295c8e
Merge remote-tracking branch 'origin/main' into ab/pass-scopes
acl-cqc Feb 20, 2026
f98093d
Various doc improvements, and module root is PreserveInterface
acl-cqc Feb 20, 2026
14c5b9b
reimplement in python (no tests)
acl-cqc Feb 21, 2026
a3e07c9
PassScopeBase has regions/in_scope meths+dedup docs, metaclass fun
acl-cqc Feb 22, 2026
540dab7
scope.rs: unless it is set to --> unless the entrypoint is
acl-cqc Feb 26, 2026
a6ebb96
doc re. EntrypointFlat
acl-cqc Feb 26, 2026
85a094d
with_scope takes impl Into<PassScope>
acl-cqc Feb 26, 2026
834edd9
update python docs to mirror Rust
acl-cqc Feb 26, 2026
22e951a
fmt-py
acl-cqc Feb 26, 2026
6a4332d
python: don't preserve all non-function module-children
acl-cqc Feb 26, 2026
968c12e
Remove note to reviewers
acl-cqc Feb 26, 2026
332bc84
remove from_entrypoint, for next PR
acl-cqc Feb 26, 2026
1c38f50
python tests, extend builder to allow setting visibility
acl-cqc Feb 26, 2026
6b05449
test_hug -> test_hugr
acl-cqc Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions hugr-passes/src/composable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use hugr_core::core::HugrNode;
use hugr_core::hugr::{ValidationError, hugrmut::HugrMut};
use itertools::Either;

use crate::PassScope;

/// An optimization pass that can be sequenced with another and/or wrapped
/// e.g. by [`ValidatingPass`]
pub trait ComposablePass<H: HugrMut>: Sized {
Expand All @@ -17,6 +19,18 @@ pub trait ComposablePass<H: HugrMut>: Sized {
/// Run the pass on the given HUGR.
fn run(&self, hugr: &mut H) -> Result<Self::Result, Self::Error>;

/// Set the scope configuration used to run the pass.
///
/// See [`PassScope`] for more details.
///
/// In `hugr 0.25.*`, this configuration is only a guidance, and may be
/// ignored by the pass.
///
/// From `hugr >=0.26.0`, passes must respect the scope configuration.
//
// For hugr passes, this is tracked by <https://github.com/Quantinuum/hugr/issues/2771>
fn with_scope(self, scope: &PassScope) -> Self;

/// Apply a function to the error type of this pass, returning a new
/// [`ComposablePass`] that has the same result type.
fn map_err<E2: Error>(
Expand All @@ -28,6 +42,10 @@ pub trait ComposablePass<H: HugrMut>: Sized {

/// Returns a [`ComposablePass`] that does "`self` then `other`", so long as
/// `other::Err` can be combined with ours.
///
/// Composed passes may have different configured [`PassScope`]s. Use
/// [`ComposablePass::with_scope`] after the composition to override all the
/// scope configurations if needed.
fn then<P: ComposablePass<H>, E: ErrorCombiner<Self::Error, P::Error>>(
self,
other: P,
Expand All @@ -48,6 +66,14 @@ pub trait ComposablePass<H: HugrMut>: Sized {
let res2 = self.1.run(hugr).map_err(E::from_second)?;
Ok((res1, res2))
}

fn with_scope(self, scope: &PassScope) -> Self {
Self(
self.0.with_scope(scope),
self.1.with_scope(scope),
PhantomData,
)
}
}

Sequence(self, other, PhantomData)
Expand Down Expand Up @@ -110,6 +136,10 @@ impl<P: ComposablePass<H>, H: HugrMut, E: Error, F: Fn(P::Error) -> E> Composabl
fn run(&self, hugr: &mut H) -> Result<P::Result, Self::Error> {
self.0.run(hugr).map_err(&self.1)
}

fn with_scope(self, scope: &PassScope) -> Self {
Self(self.0.with_scope(scope), self.1, PhantomData)
}
}

// ValidatingPass ------------------------------
Expand Down Expand Up @@ -188,6 +218,10 @@ where
})?;
Ok(res)
}

fn with_scope(self, scope: &PassScope) -> Self {
Self(self.0.with_scope(scope), self.1)
}
}

// IfThen ------------------------------
Expand Down Expand Up @@ -225,6 +259,14 @@ impl<
res.then(|| self.1.run(hugr).map_err(ErrorCombiner::from_second))
.transpose()
}

fn with_scope(self, scope: &PassScope) -> Self {
Self(
self.0.with_scope(scope),
self.1.with_scope(scope),
PhantomData,
)
}
}

pub(crate) fn validate_if_test<P: ComposablePass<H>, H: HugrMut>(
Expand Down
7 changes: 7 additions & 0 deletions hugr-passes/src/const_fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use hugr_core::{
};
use value_handle::ValueHandle;

use crate::PassScope;
use crate::dataflow::{
ConstLoader, ConstLocation, DFContext, Machine, PartialValue, TailLoopTermination,
partial_from_const,
Expand Down Expand Up @@ -186,6 +187,12 @@ impl<H: HugrMut<Node = Node> + 'static> ComposablePass<H> for ConstantFoldPass {
})?;
Ok(())
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

/// Exhaustively apply constant folding to a HUGR.
Expand Down
8 changes: 7 additions & 1 deletion hugr-passes/src/dead_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;

use crate::ComposablePass;
use crate::{ComposablePass, PassScope};

/// Configuration for Dead Code Elimination pass
#[derive(Clone)]
Expand Down Expand Up @@ -185,6 +185,12 @@ impl<H: HugrMut> ComposablePass<H> for DeadCodeElimPass<H> {
}
Ok(())
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}
#[cfg(test)]
mod test {
Expand Down
7 changes: 7 additions & 0 deletions hugr-passes/src/dead_funcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use hugr_core::{
};
use petgraph::visit::{Dfs, Walker};

use crate::PassScope;
use crate::{
ComposablePass,
composable::{ValidatePassError, validate_if_test},
Expand Down Expand Up @@ -107,6 +108,12 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for RemoveDeadFuncsPass {
}
Ok(())
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

/// Deletes from the Hugr any functions that are not used by either [`Call`] or
Expand Down
8 changes: 7 additions & 1 deletion hugr-passes/src/inline_dfgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use hugr_core::{
};
use itertools::Itertools;

use crate::ComposablePass;
use crate::{ComposablePass, PassScope};

/// Inlines all DFG nodes nested below the entrypoint.
///
Expand Down Expand Up @@ -43,6 +43,12 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for InlineDFGsPass {
}
Ok(())
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

#[cfg(test)]
Expand Down
30 changes: 18 additions & 12 deletions hugr-passes/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
//! Compilation passes acting on the HUGR program representation.

pub mod composable;
pub use composable::ComposablePass;
pub mod const_fold;
pub mod dataflow;
pub mod dead_code;
pub use dead_code::DeadCodeElimPass;
mod dead_funcs;
pub use dead_funcs::{RemoveDeadFuncsError, RemoveDeadFuncsPass, remove_dead_funcs};
pub mod force_order;
mod half_node;
pub mod inline_dfgs;
pub mod inline_funcs;
pub use inline_funcs::inline_acyclic;
pub mod lower;
mod monomorphize;
pub mod nest_cfgs;
pub mod non_local;
pub mod normalize_cfgs;
pub mod redundant_order_edges;
pub mod replace_types;
pub mod scope;
pub mod untuple;

pub use monomorphize::{MonomorphizePass, mangle_name, monomorphize};
pub mod replace_types;
pub use replace_types::ReplaceTypes;
pub mod nest_cfgs;
pub mod non_local;
mod dead_funcs;
mod half_node;
mod monomorphize;

// Main pass interfaces
pub use composable::ComposablePass;
pub use scope::PassScope;

// Pass re-exports
pub use dead_code::DeadCodeElimPass;
pub use dead_funcs::{RemoveDeadFuncsError, RemoveDeadFuncsPass, remove_dead_funcs};
pub use force_order::{force_order, force_order_by_key};
pub use inline_funcs::inline_acyclic;
pub use lower::{lower_ops, replace_many_ops};
pub use monomorphize::{MonomorphizePass, mangle_name, monomorphize};
pub use non_local::{ensure_no_nonlocal_edges, nonlocal_edges};
pub use replace_types::ReplaceTypes;
pub use untuple::UntuplePass;
8 changes: 7 additions & 1 deletion hugr-passes/src/monomorphize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use hugr_core::{
use hugr_core::hugr::{HugrView, OpType, hugrmut::HugrMut};
use itertools::Itertools as _;

use crate::ComposablePass;
use crate::composable::{ValidatePassError, validate_if_test};
use crate::{ComposablePass, PassScope};

/// Replaces calls to polymorphic functions with calls to new monomorphic
/// instantiations of the polymorphic ones.
Expand Down Expand Up @@ -211,6 +211,12 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for MonomorphizePass {
}
Ok(())
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

/// Helper to create mangled representations of lists of [TypeArg]s.
Expand Down
8 changes: 7 additions & 1 deletion hugr-passes/src/non_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use hugr_core::{
types::{EdgeKind, Type},
};

use crate::ComposablePass;
use crate::{ComposablePass, PassScope};

mod localize;
use localize::ExtraSourceReqs;
Expand All @@ -30,6 +30,12 @@ impl<H: HugrMut> ComposablePass<H> for LocalizeEdges {
fn run(&self, hugr: &mut H) -> Result<Self::Result, Self::Error> {
remove_nonlocal_edges(hugr)
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

/// Returns an iterator over all non local edges in a Hugr beneath the entrypoint.
Expand Down
8 changes: 7 additions & 1 deletion hugr-passes/src/normalize_cfgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use hugr_core::ops::{
};
use hugr_core::{Direction, Hugr, HugrView, Node, OutgoingPort, PortIndex};

use crate::ComposablePass;
use crate::{ComposablePass, PassScope};

/// Merge any basic blocks that are direct children of the specified [`CFG`]-entrypoint
/// Hugr.
Expand Down Expand Up @@ -143,6 +143,12 @@ impl<H: HugrMut> ComposablePass<H> for NormalizeCFGPass<H::Node> {
}
Ok(results)
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

/// Normalize a CFG in a Hugr:
Expand Down
8 changes: 7 additions & 1 deletion hugr-passes/src/redundant_order_edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use hugr_core::{HugrView, IncomingPort, Node, OutgoingPort};
use itertools::Itertools;
use petgraph::visit::Walker;

use crate::ComposablePass;
use crate::{ComposablePass, PassScope};

/// A pass for removing order edges in a Hugr region that are already implied by
/// other order or dataflow dependencies.
Expand Down Expand Up @@ -185,6 +185,12 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for RedundantOrderEdgesPass {

Ok(result)
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

#[cfg(test)]
Expand Down
8 changes: 7 additions & 1 deletion hugr-passes/src/replace_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use hugr_core::types::{
};
use hugr_core::{Direction, Hugr, HugrView, Node, PortIndex, Wire};

use crate::ComposablePass;
use crate::{ComposablePass, PassScope};

mod linearize;
pub use linearize::{CallbackHandler, DelegatingLinearizer, LinearizeError, Linearizer};
Expand Down Expand Up @@ -779,6 +779,12 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for ReplaceTypes {
}
Ok(changed)
}

fn with_scope(self, _scope: &PassScope) -> Self {
// TODO: Use the configured scope when running the pass.
// <https://github.com/Quantinuum/hugr/issues/2771>
self
}
}

pub mod handlers;
Expand Down
Loading
Loading