Skip to content
10 changes: 1 addition & 9 deletions hugr-passes/src/composable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,7 @@ pub trait ComposablePass<H: HugrMut>: Sized {
/// 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>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Replace with with a must?

    /// Passes must always respect the scope configuration set here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to since 0.26.0, passes must.... and removed reference to earlier versions

fn with_scope_internal(self, scope: impl Into<PassScope>) -> Self {
// Currently passes are not required to respect the scope configuration.
// <https://github.com/Quantinuum/hugr/issues/2771>
//
// deprecated: Remove default implementation in hugr 0.26.0,
// ensure all passes follow the scope configuration.
let _ = scope;
self
}
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.
Expand Down
34 changes: 20 additions & 14 deletions hugr-passes/src/inline_dfgs.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
//! Provides [`InlineDFGsPass`], a pass for inlining all DFGs in a Hugr.
use std::convert::Infallible;

use hugr_core::{
Node,
hugr::{
hugrmut::HugrMut,
patch::inline_dfg::{InlineDFG, InlineDFGError},
},
use hugr_core::hugr::{
hugrmut::HugrMut,
patch::inline_dfg::{InlineDFG, InlineDFGError},
};
use itertools::Itertools;

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

/// Inlines all DFG nodes nested below the entrypoint.
///
/// See [InlineDFG] for a rewrite to inline single DFGs.
#[derive(Debug, Clone)]
pub struct InlineDFGsPass;
#[derive(Debug, Default, Clone)]
pub struct InlineDFGsPass {
scope: PassScope,
}

impl<H: HugrMut<Node = Node>> ComposablePass<H> for InlineDFGsPass {
impl<H: HugrMut> ComposablePass<H> for InlineDFGsPass {
type Error = Infallible;
type Result = ();

fn run(&self, h: &mut H) -> Result<(), Self::Error> {
let Some(r) = self.scope.root(h) else {
return Ok(());
};
let dfgs = h
.entry_descendants()
.skip(1) // Skip the entrypoint itself
.filter(|&n| h.get_optype(n).is_dfg())
.descendants(r)
.filter(|&n| n != h.entrypoint() && h.get_optype(n).is_dfg())
.collect_vec();
for dfg in dfgs {
h.apply_patch(InlineDFG(dfg.into()))
Expand All @@ -43,6 +44,11 @@ impl<H: HugrMut<Node = Node>> ComposablePass<H> for InlineDFGsPass {
}
Ok(())
}

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

#[cfg(test)]
Expand Down Expand Up @@ -84,7 +90,7 @@ mod test {

let mut h = outer.finish_hugr_with_outputs([a, b])?;
assert_eq!(h.num_nodes(), 5 * 3 + 4); // 5 DFGs with I/O + 4 nodes for module/func roots
InlineDFGsPass.run(&mut h).unwrap();
InlineDFGsPass::default().run(&mut h).unwrap();

// Root should be the only remaining DFG
assert!(h.get_optype(h.entrypoint()).is_dfg());
Expand Down
5 changes: 4 additions & 1 deletion hugr-passes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ pub use lower::{lower_ops, replace_many_ops};
#[expect(deprecated)] // Remove together
pub use monomorphize::monomorphize;
pub use monomorphize::{MonomorphizePass, mangle_name};
pub use non_local::{ensure_no_nonlocal_edges, nonlocal_edges};
#[deprecated(note = "Use LocalizeEdgesPass::check_no_nonlocal_edges")]
#[expect(deprecated)] // Remove at same time
pub use non_local::ensure_no_nonlocal_edges;
pub use non_local::nonlocal_edges;
pub use replace_types::ReplaceTypes;
pub use untuple::UntuplePass;
44 changes: 30 additions & 14 deletions 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 All @@ -36,7 +36,7 @@ use crate::composable::{ValidatePassError, validate_if_test};
pub fn monomorphize(
hugr: &mut impl HugrMut<Node = Node>,
) -> Result<(), ValidatePassError<Node, Infallible>> {
validate_if_test(MonomorphizePass, hugr)
validate_if_test(MonomorphizePass::default(), hugr)
}

fn is_polymorphic(fd: &FuncDefn) -> bool {
Expand Down Expand Up @@ -197,21 +197,37 @@ fn instantiate(
/// children of the root node. We make best effort to ensure that names (derived
/// from parent function names and concrete type args) of new functions are unique
/// whenever the names of their parents are unique, but this is not guaranteed.
#[derive(Debug, Clone)]
pub struct MonomorphizePass;
#[derive(Debug, Default, Clone)]
pub struct MonomorphizePass {
scope: PassScope,
}

impl<H: HugrMut<Node = Node>> ComposablePass<H> for MonomorphizePass {
type Error = Infallible;
type Result = ();

fn run(&self, h: &mut H) -> Result<(), Self::Error> {
let root = h.entrypoint();
// If the root is a polymorphic function, then there are no external calls, so nothing to do
if !is_polymorphic_funcdefn(h.get_optype(root)) {
mono_scan(h, root, None, &mut HashMap::new());
}
match self.scope {
PassScope::EntrypointFlat | PassScope::EntrypointRecursive => {
// for module-entrypoint, PassScope says to do nothing. (Monomorphization could.)
// for non-module-entrypoint, PassScope says not to touch Hugr outside entrypoint,
// so monomorphization cannot add any new functions --> do nothing.
// NOTE we could look to see if there are any existing instantations that
// we could use (!), but not atm.
}
PassScope::Global(_) =>
// only generates new nodes, never changes signature of any existing node.
{
mono_scan(h, h.module_root(), None, &mut HashMap::new())
}
};
Ok(())
}

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

/// Helper to create mangled representations of lists of [TypeArg]s.
Expand Down Expand Up @@ -308,7 +324,7 @@ mod test {
let [i1] = dfg_builder.input_wires_arr();
let hugr = dfg_builder.finish_hugr_with_outputs([i1]).unwrap();
let mut hugr2 = hugr.clone();
MonomorphizePass.run(&mut hugr2).unwrap();
MonomorphizePass::default().run(&mut hugr2).unwrap();
assert_eq!(hugr, hugr2);
}

Expand Down Expand Up @@ -374,7 +390,7 @@ mod test {
.count(),
3
);
MonomorphizePass.run(&mut hugr)?;
MonomorphizePass::default().run(&mut hugr)?;
let mono = hugr;
mono.validate()?;

Expand All @@ -395,7 +411,7 @@ mod test {
["double", "main", "triple"]
);
let mut mono2 = mono.clone();
MonomorphizePass.run(&mut mono2)?;
MonomorphizePass::default().run(&mut mono2)?;

assert_eq!(mono2, mono); // Idempotent

Expand Down Expand Up @@ -551,7 +567,7 @@ mod test {
let mut hugr = outer.finish_hugr_with_outputs([e1, e2]).unwrap();
hugr.set_entrypoint(hugr.module_root()); // We want to act on everything, not just `main`

MonomorphizePass.run(&mut hugr).unwrap();
MonomorphizePass::default().run(&mut hugr).unwrap();
let mono_hugr = hugr;
mono_hugr.validate().unwrap();
let funcs = list_funcs(&mono_hugr);
Expand Down Expand Up @@ -629,7 +645,7 @@ mod test {
module_builder.finish_hugr().unwrap()
};

MonomorphizePass.run(&mut hugr).unwrap();
MonomorphizePass::default().run(&mut hugr).unwrap();
RemoveDeadFuncsPass::default()
.with_scope(Preserve::Public)
.run(&mut hugr)
Expand Down
Loading
Loading