Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
64 changes: 40 additions & 24 deletions hugr-passes/src/composable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

mod scope;

use hugr_core::Hugr;
pub use scope::{InScope, PassScope, Preserve};

use std::{error::Error, marker::PhantomData};
Expand All @@ -23,7 +22,7 @@ use itertools::Either;
/// idempotent (i.e. such that after running a pass, rerunning it immediately has
/// no further effect). However this is *not* a requirement, e.g. a sequence of
/// idempotent passes created by [ComposablePass::then] may not be idempotent itself.
pub trait ComposablePass<H: HugrMut>: Sized {
pub trait ComposablePass<H: HugrMut>: WithScope + Sized {
/// Error thrown by this pass.
type Error: Error;
/// Result returned by this pass.
Expand All @@ -32,13 +31,6 @@ 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.
///
/// Since `hugr >=0.26.0`, passes must implement this to respect the scope configuration.
fn with_scope_internal(self, scope: impl Into<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 Down Expand Up @@ -79,12 +71,17 @@ pub trait ComposablePass<H: HugrMut>: Sized {
let res2 = self.1.run(hugr).map_err(E::from_second)?;
Ok((res1, res2))
}

fn with_scope_internal(self, scope: impl Into<PassScope>) -> Self {
}
impl<E, P1, P2> WithScope for Sequence<E, P1, P2>
where
P1: WithScope,
P2: WithScope,
{
fn with_scope(self, scope: impl Into<PassScope>) -> Self {
let scope = scope.into();
Self(
self.0.with_scope_internal(scope.clone()),
self.1.with_scope_internal(scope),
self.0.with_scope(scope.clone()),
self.1.with_scope(scope),
PhantomData,
)
}
Expand All @@ -100,12 +97,19 @@ pub trait WithScope {
/// Set the scope configuration used to run the pass.
///
/// See [`PassScope`] for more details.
///
/// Since `hugr >=0.26.0`, passes must implement this to respect the scope configuration.
fn with_scope(self, scope: impl Into<PassScope>) -> Self;
}

impl<P: ComposablePass<Hugr>> WithScope for P {
fn with_scope(self, scope: impl Into<PassScope>) -> Self {
self.with_scope_internal(scope)
/// Return a default instance of the pass with the given scope.
///
/// See [`PassScope`] for more details.
#[must_use]
fn default_scoped(scope: PassScope) -> Self
Copy link
Contributor

@acl-cqc acl-cqc Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about default_scoped - it is a bit ambiguous, it sounds like "with the default scope" whereas this is "default, with specified scope".

default_with_scope? Ideal, but hardly any shorter.
default_with?

Or perhaps something really short and mnemonic, I mean so that you'd have to look it up but at least it might stick in your mind?

where
Self: Default,
{
Self::default().with_scope(scope)
}
}

Expand Down Expand Up @@ -165,9 +169,13 @@ 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_internal(self, scope: impl Into<PassScope>) -> Self {
Self(self.0.with_scope_internal(scope), self.1, PhantomData)
impl<P: ComposablePass<H>, H: HugrMut, E: Error, F: Fn(P::Error) -> E> WithScope
for ErrMapper<P, H, E, F>
{
fn with_scope(self, scope: impl Into<PassScope>) -> Self {
Self(self.0.with_scope(scope), self.1, PhantomData)
}
}

Expand Down Expand Up @@ -247,9 +255,11 @@ where
})?;
Ok(res)
}
}

fn with_scope_internal(self, scope: impl Into<PassScope>) -> Self {
Self(self.0.with_scope_internal(scope), self.1)
impl<P: ComposablePass<H>, H: HugrMut> WithScope for ValidatingPass<P, H> {
fn with_scope(self, scope: impl Into<PassScope>) -> Self {
Self(self.0.with_scope(scope), self.1)
}
}

Expand Down Expand Up @@ -288,12 +298,18 @@ impl<
res.then(|| self.1.run(hugr).map_err(ErrorCombiner::from_second))
.transpose()
}
}

fn with_scope_internal(self, scope: impl Into<PassScope>) -> Self {
impl<E, H, A, B> WithScope for IfThen<E, H, A, B>
where
A: WithScope,
B: WithScope,
{
fn with_scope(self, scope: impl Into<PassScope>) -> Self {
let scope = scope.into();
Self(
self.0.with_scope_internal(scope.clone()),
self.1.with_scope_internal(scope),
self.0.with_scope(scope.clone()),
self.1.with_scope(scope),
PhantomData,
)
}
Expand Down
9 changes: 9 additions & 0 deletions hugr-passes/src/const_fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use hugr_core::{
};
use value_handle::ValueHandle;

use crate::PassScope;
use crate::composable::WithScope;
use crate::dataflow::{
ConstLoader, ConstLocation, DFContext, Machine, PartialValue, TailLoopTermination,
partial_from_const,
Expand Down Expand Up @@ -215,6 +217,13 @@ impl<H: HugrMut<Node = Node> + 'static> ComposablePass<H> for ConstantFoldPass {
}
}

impl WithScope for ConstantFoldPass {
fn with_scope(self, _scope: impl Into<PassScope>) -> Self {
// TODO: Set the scope configuration
Copy link
Contributor

@acl-cqc acl-cqc Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs merging, with_scope_internal is still defined above

self
}
}

/// Exhaustively apply constant folding to a HUGR.
/// If the Hugr's entrypoint is its [`Module`], assumes all [`FuncDefn`] children are reachable.
///
Expand Down
6 changes: 5 additions & 1 deletion hugr-passes/src/dead_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::collections::{HashMap, HashSet, VecDeque};
use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;

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

/// Configuration for Dead Code Elimination pass
Expand Down Expand Up @@ -212,12 +213,15 @@ impl<H: HugrMut> ComposablePass<H> for DeadCodeElimPass<H> {
}
Ok(())
}
}

fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
impl<H: HugrMut> WithScope for DeadCodeElimPass<H> {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = Some(scope.into());
self
}
}

#[cfg(test)]
mod test {
use std::sync::Arc;
Expand Down
15 changes: 9 additions & 6 deletions hugr-passes/src/dead_funcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use hugr_core::{
use itertools::Either;
use petgraph::visit::{Dfs, Walker};

use crate::PassScope;
use crate::composable::WithScope;
use crate::{
ComposablePass, PassScope,
composable::{Preserve, ValidatePassError, validate_if_test},
Expand Down Expand Up @@ -92,12 +94,6 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for RemoveDeadFuncsPass {
type Error = RemoveDeadFuncsError;
type Result = ();

/// Overrides any entrypoints set by a call to [Self::with_module_entry_points].
fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
self.entry_points = Either::Right(scope.into());
self
}

fn run(&self, hugr: &mut H) -> Result<(), RemoveDeadFuncsError> {
let mut entry_points = Vec::new();
match &self.entry_points {
Expand Down Expand Up @@ -161,6 +157,13 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for RemoveDeadFuncsPass {
}
}

impl WithScope for RemoveDeadFuncsPass {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.entry_points = Either::Right(scope.into());
self
}
}

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

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

/// Inlines all DFG nodes nested below the entrypoint.
Expand Down Expand Up @@ -44,8 +45,10 @@ impl<H: HugrMut> ComposablePass<H> for InlineDFGsPass {
}
Ok(())
}
}

fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
impl WithScope for InlineDFGsPass {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = scope.into();
self
}
Expand Down
6 changes: 4 additions & 2 deletions hugr-passes/src/monomorphize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use hugr_core::{
use hugr_core::hugr::{HugrView, OpType, hugrmut::HugrMut};
use itertools::Itertools as _;

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

/// Replaces calls to polymorphic functions with calls to new monomorphic
Expand Down Expand Up @@ -223,8 +223,10 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for MonomorphizePass {
};
Ok(())
}
}

fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
impl WithScope for MonomorphizePass {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = scope.into();
self
}
Expand Down
5 changes: 4 additions & 1 deletion hugr-passes/src/non_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use hugr_core::{
types::{EdgeKind, Type},
};

use crate::composable::WithScope;
use crate::{ComposablePass, PassScope, composable::Preserve};

mod localize;
Expand Down Expand Up @@ -78,8 +79,10 @@ impl<H: HugrMut> ComposablePass<H> for LocalizeEdges {

Ok(())
}
}

fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
impl WithScope for LocalizeEdges {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = scope.into();
self
}
Expand Down
6 changes: 4 additions & 2 deletions hugr-passes/src/normalize_cfgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use hugr_core::ops::{
};
use hugr_core::{Direction, Hugr, HugrView, Node, OutgoingPort, PortIndex};

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

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

/// Overrides any previous call to [Self::cfgs]
fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
impl<N> WithScope for NormalizeCFGPass<N> {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = Either::Right(scope.into());
self
}
Expand Down
13 changes: 8 additions & 5 deletions hugr-passes/src/redundant_order_edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use hugr_core::{HugrView, IncomingPort, Node, OutgoingPort};
use itertools::Itertools;
use petgraph::visit::Walker;

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

/// A pass for removing order edges in a Hugr region that are already implied by
Expand Down Expand Up @@ -156,11 +157,6 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for RedundantOrderEdgesPass {
type Error = HugrError;
type Result = RedundantOrderEdgesResult;

fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = scope.into();
self
}

fn run(&self, hugr: &mut H) -> Result<Self::Result, Self::Error> {
// Nodes to explore in the hugr.
let mut region_candidates = VecDeque::from_iter(self.scope.root(hugr));
Expand All @@ -180,6 +176,13 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for RedundantOrderEdgesPass {
}
}

impl WithScope for RedundantOrderEdgesPass {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = scope.into();
self
}
}

#[cfg(test)]
mod tests {
use hugr_core::builder::{Dataflow, DataflowHugr, FunctionBuilder, SubContainer};
Expand Down
25 changes: 14 additions & 11 deletions hugr-passes/src/replace_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use hugr_core::types::{
};
use hugr_core::{Direction, Hugr, HugrView, Node, PortIndex, Visibility, Wire};

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

mod linearize;
Expand Down Expand Up @@ -912,17 +913,6 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for ReplaceTypes {
type Error = ReplaceTypesError;
type Result = bool;

/// Sets the scope within which the pass will operate. Note that this pass ignores
/// * [PassScope::preserve_interface], as this is a lowering pass: its purpose is to
/// change node signatures.
/// * [PassScope::recursive], as non-recursion generally leads to invalid Hugrs.
///
/// Hence, really only the [PassScope::root] affects the pass.
fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = Either::Left(scope.into());
self
}

fn run(&self, hugr: &mut H) -> Result<bool, ReplaceTypesError> {
let temp: Vec<Node>; // keep alive
let regions = match &self.scope {
Expand All @@ -940,6 +930,19 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for ReplaceTypes {
}
}

impl WithScope for ReplaceTypes {
/// Sets the scope within which the pass will operate. Note that this pass ignores
/// * [PassScope::preserve_interface], as this is a lowering pass: its purpose is to
/// change node signatures.
/// * [PassScope::recursive], as non-recursion generally leads to invalid Hugrs.
///
/// Hence, really only the [PassScope::root] affects the pass.
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = Either::Left(scope.into());
self
}
}

pub mod handlers;

#[derive(Clone, Hash, PartialEq, Eq)]
Expand Down
5 changes: 4 additions & 1 deletion hugr-passes/src/untuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use hugr_core::types::Type;
use hugr_core::{HugrView, Node, PortIndex, SimpleReplacement};
use itertools::{Either, Itertools};

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

/// Configuration enum for the untuple rewrite pass.
Expand Down Expand Up @@ -222,9 +223,11 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for UntuplePass {
}
Ok(UntupleResult { rewrites_applied })
}
}

impl WithScope for UntuplePass {
/// Overrides any [Self::set_parent] or [Self::recursive]
fn with_scope_internal(mut self, scope: impl Into<PassScope>) -> Self {
fn with_scope(mut self, scope: impl Into<PassScope>) -> Self {
self.scope = Either::Left(scope.into());
self
}
Expand Down
Loading