Skip to content
17 changes: 2 additions & 15 deletions hugr-passes/src/composable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,8 @@ pub trait ComposablePass<H: HugrMut>: Sized {
///
/// See [`PassScope`] for more details.
///
/// In `hugr 0.25.*`, this configuration is only a guidance, and may be
/// ignored by the pass by using the default implementation.
///
/// 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_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
}
/// 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.
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
7 changes: 6 additions & 1 deletion hugr-passes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ 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",
since = "0.26.0"
)]
#[expect(deprecated)] // Remove at same time
pub use non_local::ensure_no_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