From 24bff912c890ac7c87a68e4e3576d946fb9a0160 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 26 Nov 2024 11:53:28 +0000 Subject: [PATCH 01/63] Add {Dataflow,}OpTrait::substitute - compiles, no tests --- hugr-core/src/hugr.rs | 3 +- hugr-core/src/ops.rs | 10 +++- hugr-core/src/ops/constant.rs | 2 + hugr-core/src/ops/controlflow.rs | 53 +++++++++++++++++++++ hugr-core/src/ops/custom.rs | 32 +++++++++++++ hugr-core/src/ops/dataflow.rs | 82 +++++++++++++++++++++++++++++++- hugr-core/src/ops/module.rs | 10 ++++ hugr-core/src/ops/sum.rs | 9 ++++ hugr-core/src/types.rs | 6 +-- hugr-core/src/types/type_row.rs | 2 +- 10 files changed, 200 insertions(+), 9 deletions(-) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index ed0fcca0a..07f1c0a6d 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -19,7 +19,7 @@ pub use ident::{IdentList, InvalidIdentifier}; pub use rewrite::{Rewrite, SimpleReplacement, SimpleReplacementError}; use portgraph::multiportgraph::MultiPortGraph; -use portgraph::{Hierarchy, PortMut, UnmanagedDenseMap}; +use portgraph::{Hierarchy, PortMut, PortView, UnmanagedDenseMap}; use thiserror::Error; pub use self::views::{HugrView, RootTagged}; @@ -28,6 +28,7 @@ use crate::extension::{ExtensionRegistry, ExtensionSet, TO_BE_INFERRED}; use crate::ops::custom::resolve_extension_ops; use crate::ops::{OpTag, OpTrait}; pub use crate::ops::{OpType, DEFAULT_OPTYPE}; +use crate::types::Substitution; use crate::{Direction, Node}; /// The Hugr data structure. diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index 24ce8492e..ff70fad09 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -11,7 +11,7 @@ pub mod tag; pub mod validate; use crate::extension::simple_op::MakeExtensionOp; use crate::extension::ExtensionSet; -use crate::types::{EdgeKind, Signature}; +use crate::types::{EdgeKind, Signature, Substitution}; use crate::{Direction, OutgoingPort, Port}; use crate::{IncomingPort, PortIndex}; use derive_more::Display; @@ -341,7 +341,7 @@ pub trait StaticTag { #[enum_dispatch] /// Trait implemented by all OpType variants. -pub trait OpTrait { +pub trait OpTrait: Sized { /// A human-readable description of the operation. fn description(&self) -> &str; @@ -405,6 +405,12 @@ pub trait OpTrait { } .is_some() as usize } + + /// Apply a type-level substitution to this OpType, i.e. replace + /// [type variables](Type::Variable) with new types. + fn substitute(self, _subst: &Substitution) -> Self { + self + } } /// Properties of child graphs of ops, if the op has children. diff --git a/hugr-core/src/ops/constant.rs b/hugr-core/src/ops/constant.rs index 3522c61b3..818f7a655 100644 --- a/hugr-core/src/ops/constant.rs +++ b/hugr-core/src/ops/constant.rs @@ -91,6 +91,8 @@ impl OpTrait for Const { fn static_output(&self) -> Option { Some(EdgeKind::Const(self.get_type())) } + + // Constants cannot refer to TypeArgs of the enclosing Hugr, so no substitute(). } impl From for Value { diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 19b0f167a..0a139cbd1 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -36,6 +36,15 @@ impl DataflowOpTrait for TailLoop { [&self.just_inputs, &self.just_outputs].map(|row| row.extend(self.rest.iter())); Signature::new(inputs, outputs).with_extension_delta(self.extension_delta.clone()) } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + Self { + just_inputs: self.just_inputs.substitute(subst), + just_outputs: self.just_outputs.substitute(subst), + rest: self.rest.substitute(subst), + extension_delta: self.extension_delta.substitute(subst), + } + } } impl TailLoop { @@ -100,6 +109,19 @@ impl DataflowOpTrait for Conditional { Signature::new(inputs, self.outputs.clone()) .with_extension_delta(self.extension_delta.clone()) } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + Self { + sum_rows: self + .sum_rows + .into_iter() + .map(|r| r.substitute(subst)) + .collect(), + other_inputs: self.other_inputs.substitute(subst), + outputs: self.outputs.substitute(subst), + extension_delta: self.extension_delta.substitute(subst), + } + } } impl Conditional { @@ -129,6 +151,12 @@ impl DataflowOpTrait for CFG { fn signature(&self) -> Signature { self.signature.clone() } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } + } } #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -209,6 +237,19 @@ impl OpTrait for DataflowBlock { Direction::Outgoing => self.sum_rows.len(), } } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + Self { + inputs: self.inputs.substitute(subst), + other_outputs: self.other_outputs.substitute(subst), + sum_rows: self + .sum_rows + .into_iter() + .map(|r| r.substitute(subst)) + .collect(), + extension_delta: self.extension_delta.substitute(subst), + } + } } impl OpTrait for ExitBlock { @@ -234,6 +275,12 @@ impl OpTrait for ExitBlock { Direction::Outgoing => 0, } } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + Self { + cfg_outputs: self.cfg_outputs.substitute(subst), + } + } } /// Functionality shared by DataflowBlock and Exit CFG block types. @@ -297,6 +344,12 @@ impl OpTrait for Case { fn tag(&self) -> OpTag { ::TAG } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } + } } impl Case { diff --git a/hugr-core/src/ops/custom.rs b/hugr-core/src/ops/custom.rs index d7f1c2c57..c6f98e908 100644 --- a/hugr-core/src/ops/custom.rs +++ b/hugr-core/src/ops/custom.rs @@ -155,6 +155,26 @@ impl DataflowOpTrait for ExtensionOp { fn signature(&self) -> Signature { self.signature.clone() } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + let args = self + .args + .into_iter() + .map(|ta| ta.substitute(subst)) + .collect::>(); + let signature = self.signature.substitute(subst); + debug_assert_eq!( + self.def + .compute_signature(&args, subst.extension_registry()) + .as_ref(), + Ok(&signature) + ); + Self { + def: self.def, + args, + signature, + } + } } /// An opaquely-serialized op that refers to an as-yet-unresolved [`OpDef`]. @@ -239,6 +259,18 @@ impl DataflowOpTrait for OpaqueOp { .clone() .with_extension_delta(self.extension().clone()) } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + Self { + args: self + .args + .into_iter() + .map(|ta| ta.substitute(subst)) + .collect(), + signature: self.signature.substitute(subst), + ..self + } + } } /// Resolve serialized names of operations into concrete implementation (OpDefs) where possible diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index 364429784..578a00c6f 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -4,14 +4,14 @@ use super::{impl_op_name, OpTag, OpTrait}; use crate::extension::{ExtensionRegistry, ExtensionSet, SignatureError}; use crate::ops::StaticTag; -use crate::types::{EdgeKind, PolyFuncType, Signature, Type, TypeArg, TypeRow}; +use crate::types::{EdgeKind, PolyFuncType, Signature, Substitution, Type, TypeArg, TypeRow}; use crate::IncomingPort; #[cfg(test)] use ::proptest_derive::Arbitrary; /// Trait implemented by all dataflow operations. -pub trait DataflowOpTrait { +pub trait DataflowOpTrait: Sized { /// Tag identifying the operation. const TAG: OpTag; @@ -49,6 +49,10 @@ pub trait DataflowOpTrait { fn static_input(&self) -> Option { None } + + /// Apply a type-level substitution to this OpType, i.e. replace + /// [type variables](Type::Variable) with new types. + fn substitute(self, _subst: &Substitution) -> Self; } /// Helpers to construct input and output nodes @@ -108,6 +112,12 @@ impl DataflowOpTrait for Input { fn signature(&self) -> Signature { Signature::new(TypeRow::new(), self.types.clone()) } + + fn substitute(self, subst: &Substitution) -> Self { + Self { + types: self.types.substitute(subst), + } + } } impl DataflowOpTrait for Output { const TAG: OpTag = OpTag::Output; @@ -125,6 +135,12 @@ impl DataflowOpTrait for Output { fn other_output(&self) -> Option { None } + + fn substitute(self, subst: &Substitution) -> Self { + Self { + types: self.types.substitute(subst), + } + } } impl OpTrait for T { @@ -151,6 +167,10 @@ impl OpTrait for T { fn static_input(&self) -> Option { DataflowOpTrait::static_input(self) } + + fn substitute(self, subst: &crate::types::Substitution) -> Self { + DataflowOpTrait::substitute(self, subst) + } } impl StaticTag for T { const TAG: OpTag = T::TAG; @@ -187,6 +207,26 @@ impl DataflowOpTrait for Call { fn static_input(&self) -> Option { Some(EdgeKind::Function(self.called_function_type().clone())) } + + fn substitute(self, subst: &Substitution) -> Self { + let type_args = self + .type_args + .into_iter() + .map(|ta| ta.substitute(subst)) + .collect::>(); + let instantiation = self.instantiation.substitute(subst); + debug_assert_eq!( + self.func_sig + .instantiate(&type_args, subst.extension_registry()) + .as_ref(), + Ok(&instantiation) + ); + Self { + type_args, + instantiation, + func_sig: self.func_sig, + } + } } impl Call { /// Try to make a new Call. Returns an error if the `type_args`` do not fit the [TypeParam]s @@ -279,6 +319,12 @@ impl DataflowOpTrait for CallIndirect { .insert(0, Type::new_function(self.signature.clone())); s } + + fn substitute(self, subst: &Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } + } } /// Load a static constant in to the local dataflow graph. @@ -303,7 +349,13 @@ impl DataflowOpTrait for LoadConstant { fn static_input(&self) -> Option { Some(EdgeKind::Const(self.constant_type().clone())) } + + fn substitute(self, _subst: &Substitution) -> Self { + // Constants cannot refer to TypeArgs, so neither can loading them + self + } } + impl LoadConstant { #[inline] /// The type of the constant loaded by this op. @@ -358,6 +410,26 @@ impl DataflowOpTrait for LoadFunction { fn static_input(&self) -> Option { Some(EdgeKind::Function(self.func_sig.clone())) } + + fn substitute(self, subst: &Substitution) -> Self { + let type_args = self + .type_args + .into_iter() + .map(|ta| ta.substitute(subst)) + .collect::>(); + let signature = self.signature.substitute(subst); + debug_assert_eq!( + self.func_sig + .instantiate(&type_args, subst.extension_registry()) + .as_ref(), + Ok(&signature) + ); + Self { + func_sig: self.func_sig, + type_args, + signature, + } + } } impl LoadFunction { /// Try to make a new LoadFunction op. Returns an error if the `type_args`` do not fit @@ -447,4 +519,10 @@ impl DataflowOpTrait for DFG { fn signature(&self) -> Signature { self.inner_signature() } + + fn substitute(self, subst: &Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } + } } diff --git a/hugr-core/src/ops/module.rs b/hugr-core/src/ops/module.rs index c4c2a1e62..6e7d34cce 100644 --- a/hugr-core/src/ops/module.rs +++ b/hugr-core/src/ops/module.rs @@ -81,6 +81,8 @@ impl OpTrait for FuncDefn { fn static_output(&self) -> Option { Some(EdgeKind::Function(self.signature.clone())) } + + // Cannot refer to TypeArgs of enclosing Hugr (it binds its own), so no substitute() } /// External function declaration, linked at runtime. @@ -111,6 +113,8 @@ impl OpTrait for FuncDecl { fn static_output(&self) -> Option { Some(EdgeKind::Function(self.signature.clone())) } + + // Cannot refer to TypeArgs of enclosing Hugr (the type binds its own), so no substitute() } /// A type alias definition, used only for debug/metadata. @@ -135,6 +139,9 @@ impl OpTrait for AliasDefn { fn tag(&self) -> OpTag { ::TAG } + + // Cannot refer to TypeArgs of enclosing Hugr (? - we planned to make this + // polymorphic so it binds its own, and we never combine binders), so no substitute() } /// A type alias declaration. Resolved at link time. @@ -175,4 +182,7 @@ impl OpTrait for AliasDecl { fn tag(&self) -> OpTag { ::TAG } + + // Cannot refer to TypeArgs of enclosing Hugr (? - we planned to make this + // polymorphic so it binds its own, and we never combine binders), so no substitute() } diff --git a/hugr-core/src/ops/sum.rs b/hugr-core/src/ops/sum.rs index aa7119104..a4a3f4e62 100644 --- a/hugr-core/src/ops/sum.rs +++ b/hugr-core/src/ops/sum.rs @@ -52,4 +52,13 @@ impl DataflowOpTrait for Tag { fn other_output(&self) -> Option { Some(EdgeKind::StateOrder) } + + fn substitute(mut self, subst: &crate::types::Substitution) -> Self { + self.variants = self + .variants + .into_iter() + .map(|r| r.substitute(subst)) + .collect(); + self + } } diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index c61149bff..d02de148a 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -538,9 +538,9 @@ impl From for TypeRV { /// Details a replacement of type variables with a finite list of known values. /// (Variables out of the range of the list will result in a panic) -pub(crate) struct Substitution<'a>(&'a [TypeArg], &'a ExtensionRegistry); +pub struct Substitution<'a>(&'a [TypeArg], &'a ExtensionRegistry); -impl Substitution<'_> { +impl<'a> Substitution<'a> { pub(crate) fn apply_var(&self, idx: usize, decl: &TypeParam) -> TypeArg { let arg = self .0 @@ -580,7 +580,7 @@ impl Substitution<'_> { } } - fn extension_registry(&self) -> &ExtensionRegistry { + pub(crate) fn extension_registry(&self) -> &ExtensionRegistry { self.1 } } diff --git a/hugr-core/src/types/type_row.rs b/hugr-core/src/types/type_row.rs index b8cb6d116..c807b872f 100644 --- a/hugr-core/src/types/type_row.rs +++ b/hugr-core/src/types/type_row.rs @@ -71,7 +71,7 @@ impl TypeRowBase { /// Applies a substitution to the row. /// For `TypeRowRV`, note this may change the length of the row. /// For `TypeRow`, guaranteed not to change the length of the row. - pub(super) fn substitute(&self, s: &Substitution) -> Self { + pub(crate) fn substitute(&self, s: &Substitution) -> Self { self.iter() .flat_map(|ty| ty.substitute(s)) .collect::>() From 341e5376b3130bc9c82b2fa06b206d39f54ee6a0 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 26 Nov 2024 15:51:12 +0000 Subject: [PATCH 02/63] WIP Add Hugr::substitute --- hugr-core/src/hugr.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 07f1c0a6d..97a0802dd 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -168,6 +168,18 @@ impl Hugr { infer(self, self.root(), remove)?; Ok(()) } + + /// Destructively substitutes [Type Variables](crate::types::Type::Variable) (also [TypeArg::Variable]) + /// given a new value for each. + pub fn substitute(mut self, subst: &Substitution) -> Self { + for n in self.graph.nodes_iter() { + let op_ty = self.op_types.get_mut(n); + let mut temp = OpType::default(); + std::mem::swap(op_ty, &mut temp); + *op_ty = temp.substitute(subst); + } + self + } } /// Internal API for HUGRs, not intended for use by users. From 6a96b2cbc3d768e492fa73e848d7738955df9f3a Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 26 Nov 2024 18:03:33 +0000 Subject: [PATCH 03/63] WIP monomorphize via mono_scan --- hugr-core/src/hugr.rs | 18 +++-- hugr-core/src/hugr/monomorphize.rs | 107 +++++++++++++++++++++++++++++ hugr-core/src/types.rs | 4 ++ 3 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 hugr-core/src/hugr/monomorphize.rs diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 97a0802dd..5019edb9c 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -4,12 +4,13 @@ pub mod hugrmut; pub(crate) mod ident; pub mod internal; +mod monomorphize; pub mod rewrite; pub mod serialize; pub mod validate; pub mod views; -use std::collections::VecDeque; +use std::collections::{HashMap, VecDeque}; use std::iter; pub(crate) use self::hugrmut::HugrMut; @@ -19,7 +20,7 @@ pub use ident::{IdentList, InvalidIdentifier}; pub use rewrite::{Rewrite, SimpleReplacement, SimpleReplacementError}; use portgraph::multiportgraph::MultiPortGraph; -use portgraph::{Hierarchy, PortMut, PortView, UnmanagedDenseMap}; +use portgraph::{Hierarchy, PortMut, UnmanagedDenseMap}; use thiserror::Error; pub use self::views::{HugrView, RootTagged}; @@ -28,9 +29,10 @@ use crate::extension::{ExtensionRegistry, ExtensionSet, TO_BE_INFERRED}; use crate::ops::custom::resolve_extension_ops; use crate::ops::{OpTag, OpTrait}; pub use crate::ops::{OpType, DEFAULT_OPTYPE}; -use crate::types::Substitution; use crate::{Direction, Node}; +use monomorphize::mono_scan; + /// The Hugr data structure. #[derive(Clone, Debug, PartialEq)] pub struct Hugr { @@ -171,13 +173,9 @@ impl Hugr { /// Destructively substitutes [Type Variables](crate::types::Type::Variable) (also [TypeArg::Variable]) /// given a new value for each. - pub fn substitute(mut self, subst: &Substitution) -> Self { - for n in self.graph.nodes_iter() { - let op_ty = self.op_types.get_mut(n); - let mut temp = OpType::default(); - std::mem::swap(op_ty, &mut temp); - *op_ty = temp.substitute(subst); - } + pub fn monomorphize(mut self, reg: &ExtensionRegistry) -> Self { + let root = self.root(); // I.e. "all monomorphic funcs" for Module-Rooted Hugrs...right? + mono_scan(&mut self, root, None, &mut HashMap::new(), reg); self } } diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs new file mode 100644 index 000000000..94c1876e1 --- /dev/null +++ b/hugr-core/src/hugr/monomorphize.rs @@ -0,0 +1,107 @@ +use std::collections::{hash_map::Entry, HashMap}; + +use crate::{ + extension::ExtensionRegistry, + ops::{Call, FuncDefn, LoadFunction, OpTrait}, + types::{Substitution, TypeArg}, + Node, +}; + +use super::{HugrMut, OpType}; + +pub(super) fn mono_scan( + h: &mut impl HugrMut, + parent: Node, + subst_into: Option<(&Substitution<'_>, Node)>, + cache: &mut HashMap<(Node, Vec), Node>, + reg: &ExtensionRegistry, +) { + if subst_into.is_some() { + // First flatten: move all FuncDefns (which do not refer to the TypeParams + // being substituted) to be siblings of the enclosing FuncDefn + for ch in h.children(parent).collect::>() { + if h.get_optype(ch).is_func_defn() { + // Lift the FuncDefn out + let enclosing_poly_func = std::iter::successors(Some(ch), |n| h.get_parent(*n)) + .find(|n| { + h.get_optype(*n) + .as_func_defn() + .is_some_and(|fd| !fd.signature.params().is_empty()) + }) + .unwrap(); + // Might need (new_parent) or some such + h.move_after_sibling(ch, enclosing_poly_func); + } + } + // Since one can only call up the hierarchy, + // that means we've moved FuncDefns before we encounter any Calls to them + } + for mut ch in h.children(parent).collect::>() { + let ch_op = h.get_optype(ch); + if let Some(fd) = ch_op.as_func_defn() { + assert!(subst_into.is_none()); + if !fd.signature.params().is_empty() { + continue; + } + } + // Perform substitution, and recurse into containers (mono_scan does nothing if no children) + if let Some((subst, new_parent)) = subst_into { + let new_ch = h.add_node_with_parent(new_parent, ch_op.clone().substitute(subst)); + mono_scan(h, ch, Some((subst, new_ch)), cache, reg); + ch = new_ch + } else { + mono_scan(h, ch, None, cache, reg) + } + let ch_op = h.get_optype(ch); + if let OpType::Call(Call { + type_args, + instantiation, + .. + }) + | OpType::LoadFunction(LoadFunction { + type_args, + signature: instantiation, + .. + }) = ch_op + { + if !type_args.is_empty() { + let fn_inp = ch_op.static_input_port().unwrap(); + let tgt = h.static_source(ch).unwrap(); + let new_tgt = match cache.entry((tgt, type_args.clone())) { + Entry::Occupied(n) => *n.get(), + Entry::Vacant(ve) => { + let type_args = type_args.clone(); // Need to mutate Hugr... + let tgt = h.add_node_after( + tgt, + FuncDefn { + name: name_mangle( + &h.get_optype(tgt).as_func_defn().unwrap().name, + &type_args, + ), + signature: instantiation.clone().into(), + }, + ); + ve.insert(tgt); + mono_scan( + h, + tgt, + Some((&Substitution::new(&type_args, reg), tgt)), + cache, + reg, + ); + tgt + } + }; + h.disconnect(ch, fn_inp); + h.connect(new_tgt, h.num_outputs(new_tgt) - 1, ch, fn_inp); + } + } + } + if subst_into.is_some() { + todo!("Copy edges!") + } +} + +fn name_mangle(name: &str, type_args: &[TypeArg]) -> String { + todo!() +} diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index d02de148a..1948d409b 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -541,6 +541,10 @@ impl From for TypeRV { pub struct Substitution<'a>(&'a [TypeArg], &'a ExtensionRegistry); impl<'a> Substitution<'a> { + pub(crate) fn new(items: &'a [TypeArg], exts: &'a ExtensionRegistry) -> Self { + Self(items, exts) + } + pub(crate) fn apply_var(&self, idx: usize, decl: &TypeParam) -> TypeArg { let arg = self .0 From 7866a62a99287d8c582377b946f63c13fa67a6c9 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 26 Nov 2024 18:22:04 +0000 Subject: [PATCH 04/63] Use continue, match instead of if let | --- hugr-core/src/hugr/monomorphize.rs | 78 ++++++++++++++---------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 94c1876e1..23f21a331 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -2,7 +2,7 @@ use std::collections::{hash_map::Entry, HashMap}; use crate::{ extension::ExtensionRegistry, - ops::{Call, FuncDefn, LoadFunction, OpTrait}, + ops::{FuncDefn, OpTrait}, types::{Substitution, TypeArg}, Node, }; @@ -53,49 +53,41 @@ pub(super) fn mono_scan( mono_scan(h, ch, None, cache, reg) } let ch_op = h.get_optype(ch); - if let OpType::Call(Call { - type_args, - instantiation, - .. - }) - | OpType::LoadFunction(LoadFunction { - type_args, - signature: instantiation, - .. - }) = ch_op - { - if !type_args.is_empty() { - let fn_inp = ch_op.static_input_port().unwrap(); - let tgt = h.static_source(ch).unwrap(); - let new_tgt = match cache.entry((tgt, type_args.clone())) { - Entry::Occupied(n) => *n.get(), - Entry::Vacant(ve) => { - let type_args = type_args.clone(); // Need to mutate Hugr... - let tgt = h.add_node_after( - tgt, - FuncDefn { - name: name_mangle( - &h.get_optype(tgt).as_func_defn().unwrap().name, - &type_args, - ), - signature: instantiation.clone().into(), - }, - ); - ve.insert(tgt); - mono_scan( - h, - tgt, - Some((&Substitution::new(&type_args, reg), tgt)), - cache, - reg, - ); - tgt - } - }; - h.disconnect(ch, fn_inp); - h.connect(new_tgt, h.num_outputs(new_tgt) - 1, ch, fn_inp); + let (type_args, instantiation) = match ch_op { + OpType::Call(c) => (&c.type_args, &c.instantiation), + OpType::LoadFunction(lf) => (&lf.type_args, &lf.signature), + _ => continue, + }; + if type_args.is_empty() { + continue; + }; + let fn_inp = ch_op.static_input_port().unwrap(); + let tgt = h.static_source(ch).unwrap(); + let new_tgt = match cache.entry((tgt, type_args.clone())) { + Entry::Occupied(n) => *n.get(), + Entry::Vacant(ve) => { + let type_args = type_args.clone(); // Need to mutate Hugr... + let name = name_mangle(&h.get_optype(tgt).as_func_defn().unwrap().name, &type_args); + let mono_tgt = h.add_node_after( + tgt, + FuncDefn { + name, + signature: instantiation.clone().into(), + }, + ); + ve.insert(mono_tgt); + mono_scan( + h, + tgt, + Some((&Substitution::new(&type_args, reg), mono_tgt)), + cache, + reg, + ); + mono_tgt } - } + }; + h.disconnect(ch, fn_inp); + h.connect(new_tgt, h.num_outputs(new_tgt) - 1, ch, fn_inp); } if subst_into.is_some() { todo!("Copy edges!") From 871e7967f3a9f180d17766116c20454e382a8edd Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 27 Nov 2024 09:04:20 +0000 Subject: [PATCH 05/63] WIP start copying edges...no not so simple --- hugr-core/src/hugr/monomorphize.rs | 39 +++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 23f21a331..fb3987589 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -18,7 +18,8 @@ pub(super) fn mono_scan( ) { if subst_into.is_some() { // First flatten: move all FuncDefns (which do not refer to the TypeParams - // being substituted) to be siblings of the enclosing FuncDefn + // being substituted) to be siblings of the enclosing FuncDefn. + // TODO/PERF: we should do this only the first time we see each polymorphic FuncDefn. for ch in h.children(parent).collect::>() { if h.get_optype(ch).is_func_defn() { // Lift the FuncDefn out @@ -36,8 +37,9 @@ pub(super) fn mono_scan( // Since one can only call up the hierarchy, // that means we've moved FuncDefns before we encounter any Calls to them } - for mut ch in h.children(parent).collect::>() { - let ch_op = h.get_optype(ch); + let mut ch_map = HashMap::new(); + for old_ch in h.children(parent).collect::>() { + let ch_op = h.get_optype(old_ch); if let Some(fd) = ch_op.as_func_defn() { assert!(subst_into.is_none()); if !fd.signature.params().is_empty() { @@ -45,13 +47,15 @@ pub(super) fn mono_scan( } } // Perform substitution, and recurse into containers (mono_scan does nothing if no children) - if let Some((subst, new_parent)) = subst_into { + let (ch, nsubst) = if let Some((subst, new_parent)) = subst_into { let new_ch = h.add_node_with_parent(new_parent, ch_op.clone().substitute(subst)); - mono_scan(h, ch, Some((subst, new_ch)), cache, reg); - ch = new_ch + ch_map.insert(old_ch, new_ch); + (new_ch, Some((subst, new_ch))) } else { - mono_scan(h, ch, None, cache, reg) - } + (old_ch, None) + }; + mono_scan(h, old_ch, nsubst, cache, reg); + let ch_op = h.get_optype(ch); let (type_args, instantiation) = match ch_op { OpType::Call(c) => (&c.type_args, &c.instantiation), @@ -90,7 +94,24 @@ pub(super) fn mono_scan( h.connect(new_tgt, h.num_outputs(new_tgt) - 1, ch, fn_inp); } if subst_into.is_some() { - todo!("Copy edges!") + // TODO This won't work for 'ext' edges as cannot guarantee predecessors have been scanned. + // Need to wait until all recursive calls made by parent have completed + // ---> build ch_map across calls, then do this edge remapping in monomorphize() + // (also, remove all the polymorphic FuncDefns, as now all at toplevel, and defunct). + for &ch in ch_map.keys() { + for inport in h.node_inputs(ch).collect::>() { + let srcs = h.linked_outputs(ch, inport).collect::>(); + h.disconnect(ch, inport); + for (src, outport) in srcs { + h.connect( + ch_map.get(&src).copied().unwrap_or(src), + outport, + ch, + inport, + ); + } + } + } } } From ac536c57a0dcf64a59f2ce364a62fe7acb067c89 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 27 Nov 2024 09:12:08 +0000 Subject: [PATCH 06/63] Break out is_polymorphic_funcdefn, tho only used once; remove obsolete comment --- hugr-core/src/hugr/monomorphize.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index fb3987589..0e14fbf29 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -7,7 +7,7 @@ use crate::{ Node, }; -use super::{HugrMut, OpType}; +use super::{HugrMut, HugrView, OpType}; pub(super) fn mono_scan( h: &mut impl HugrMut, @@ -24,13 +24,8 @@ pub(super) fn mono_scan( if h.get_optype(ch).is_func_defn() { // Lift the FuncDefn out let enclosing_poly_func = std::iter::successors(Some(ch), |n| h.get_parent(*n)) - .find(|n| { - h.get_optype(*n) - .as_func_defn() - .is_some_and(|fd| !fd.signature.params().is_empty()) - }) + .find(|n| is_polymorphic_funcdefn(h, *n)) .unwrap(); - // Might need (new_parent) or some such h.move_after_sibling(ch, enclosing_poly_func); } } @@ -115,6 +110,12 @@ pub(super) fn mono_scan( } } +fn is_polymorphic_funcdefn(h: &impl HugrView, n: Node) -> bool { + h.get_optype(n) + .as_func_defn() + .is_some_and(|fd| !fd.signature.params().is_empty()) +} + fn name_mangle(name: &str, type_args: &[TypeArg]) -> String { todo!() } From ff04f13a22ec95d564be8fce02399a22be447d75 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 27 Nov 2024 11:45:42 +0000 Subject: [PATCH 07/63] Separate 'fn instantiate' to copy edges, add struct Instantiating; mv fn monomorphize --- hugr-core/src/hugr.rs | 12 +-- hugr-core/src/hugr/monomorphize.rs | 155 +++++++++++++++++++---------- 2 files changed, 104 insertions(+), 63 deletions(-) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 5019edb9c..5decc22a2 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -10,7 +10,7 @@ pub mod serialize; pub mod validate; pub mod views; -use std::collections::{HashMap, VecDeque}; +use std::collections::VecDeque; use std::iter; pub(crate) use self::hugrmut::HugrMut; @@ -31,7 +31,7 @@ use crate::ops::{OpTag, OpTrait}; pub use crate::ops::{OpType, DEFAULT_OPTYPE}; use crate::{Direction, Node}; -use monomorphize::mono_scan; +pub use monomorphize::monomorphize; /// The Hugr data structure. #[derive(Clone, Debug, PartialEq)] @@ -170,14 +170,6 @@ impl Hugr { infer(self, self.root(), remove)?; Ok(()) } - - /// Destructively substitutes [Type Variables](crate::types::Type::Variable) (also [TypeArg::Variable]) - /// given a new value for each. - pub fn monomorphize(mut self, reg: &ExtensionRegistry) -> Self { - let root = self.root(); // I.e. "all monomorphic funcs" for Module-Rooted Hugrs...right? - mono_scan(&mut self, root, None, &mut HashMap::new(), reg); - self - } } /// Internal API for HUGRs, not intended for use by users. diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 0e14fbf29..6facf3637 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -3,17 +3,32 @@ use std::collections::{hash_map::Entry, HashMap}; use crate::{ extension::ExtensionRegistry, ops::{FuncDefn, OpTrait}, - types::{Substitution, TypeArg}, + types::{Signature, Substitution, TypeArg}, Node, }; -use super::{HugrMut, HugrView, OpType}; +use super::{Hugr, HugrMut, HugrView, OpType}; -pub(super) fn mono_scan( +pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { + let root = h.root(); // I.e. "all monomorphic funcs" for Module-Rooted Hugrs...right? + mono_scan(&mut h, root, None, &mut HashMap::new(), reg); + h +} + +struct Instantiating<'a> { + poly_func: Node, + subst: &'a Substitution<'a>, + target_container: Node, + node_map: &'a mut HashMap, +} + +type Instantiations = HashMap<(Node, Vec), Node>; + +fn mono_scan( h: &mut impl HugrMut, parent: Node, - subst_into: Option<(&Substitution<'_>, Node)>, - cache: &mut HashMap<(Node, Vec), Node>, + mut subst_into: Option<&mut Instantiating>, + cache: &mut Instantiations, reg: &ExtensionRegistry, ) { if subst_into.is_some() { @@ -32,7 +47,6 @@ pub(super) fn mono_scan( // Since one can only call up the hierarchy, // that means we've moved FuncDefns before we encounter any Calls to them } - let mut ch_map = HashMap::new(); for old_ch in h.children(parent).collect::>() { let ch_op = h.get_optype(old_ch); if let Some(fd) = ch_op.as_func_defn() { @@ -42,17 +56,30 @@ pub(super) fn mono_scan( } } // Perform substitution, and recurse into containers (mono_scan does nothing if no children) - let (ch, nsubst) = if let Some((subst, new_parent)) = subst_into { - let new_ch = h.add_node_with_parent(new_parent, ch_op.clone().substitute(subst)); - ch_map.insert(old_ch, new_ch); - (new_ch, Some((subst, new_ch))) + let ch = if let Some(ref mut inst) = subst_into { + let new_ch = + h.add_node_with_parent(inst.target_container, ch_op.clone().substitute(inst.subst)); + inst.node_map.insert(old_ch, new_ch); + mono_scan( + h, + old_ch, + Some(&mut Instantiating { + target_container: new_ch, + node_map: inst.node_map, // &mut ref, so borrow + poly_func: inst.poly_func, // Node, so copy + subst: inst.subst, // &ref, so copy + }), + cache, + reg, + ); + new_ch } else { - (old_ch, None) + mono_scan(h, old_ch, None, cache, reg); + old_ch }; - mono_scan(h, old_ch, nsubst, cache, reg); let ch_op = h.get_optype(ch); - let (type_args, instantiation) = match ch_op { + let (type_args, mono_sig) = match ch_op { OpType::Call(c) => (&c.type_args, &c.instantiation), OpType::LoadFunction(lf) => (&lf.type_args, &lf.signature), _ => continue, @@ -62,52 +89,74 @@ pub(super) fn mono_scan( }; let fn_inp = ch_op.static_input_port().unwrap(); let tgt = h.static_source(ch).unwrap(); - let new_tgt = match cache.entry((tgt, type_args.clone())) { - Entry::Occupied(n) => *n.get(), - Entry::Vacant(ve) => { - let type_args = type_args.clone(); // Need to mutate Hugr... - let name = name_mangle(&h.get_optype(tgt).as_func_defn().unwrap().name, &type_args); - let mono_tgt = h.add_node_after( - tgt, - FuncDefn { - name, - signature: instantiation.clone().into(), - }, - ); - ve.insert(mono_tgt); - mono_scan( - h, - tgt, - Some((&Substitution::new(&type_args, reg), mono_tgt)), - cache, - reg, - ); - mono_tgt - } - }; + let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); h.disconnect(ch, fn_inp); h.connect(new_tgt, h.num_outputs(new_tgt) - 1, ch, fn_inp); } - if subst_into.is_some() { - // TODO This won't work for 'ext' edges as cannot guarantee predecessors have been scanned. - // Need to wait until all recursive calls made by parent have completed - // ---> build ch_map across calls, then do this edge remapping in monomorphize() - // (also, remove all the polymorphic FuncDefns, as now all at toplevel, and defunct). - for &ch in ch_map.keys() { - for inport in h.node_inputs(ch).collect::>() { - let srcs = h.linked_outputs(ch, inport).collect::>(); - h.disconnect(ch, inport); - for (src, outport) in srcs { - h.connect( - ch_map.get(&src).copied().unwrap_or(src), - outport, - ch, - inport, - ); - } +} + +fn instantiate( + h: &mut impl HugrMut, + poly_func: Node, + type_args: Vec, + mono_sig: Signature, + cache: &mut Instantiations, + reg: &ExtensionRegistry, +) -> Node { + let ve = match cache.entry((poly_func, type_args.clone())) { + Entry::Occupied(n) => return *n.get(), + Entry::Vacant(ve) => ve, + }; + + let name = name_mangle( + &h.get_optype(poly_func).as_func_defn().unwrap().name, + &type_args, + ); + let mono_tgt = h.add_node_after( + poly_func, + FuncDefn { + name, + signature: mono_sig.into(), + }, + ); + // Insert BEFORE we scan (in case of recursion), hence we cannot use Entry::or_insert + ve.insert(mono_tgt); + // Now make the instantiation + let mut node_map = HashMap::new(); + mono_scan( + h, + poly_func, + Some(&mut Instantiating { + poly_func, + subst: &Substitution::new(&type_args, reg), + target_container: mono_tgt, + node_map: &mut node_map, + }), + cache, + reg, + ); + // Copy edges...we have built a node_map for every node in the function. + // Note we could avoid building the "large" map (smaller than the Hugr we've just created) + // by doing this during recursion, but we'd need to be careful with nonlocal edges - + // 'ext' edges by copying every node before recursing on any of them, + // 'dom' edges would *also* require recursing in dominator-tree preorder. + for &ch in node_map.keys() { + for inport in h.node_inputs(ch).collect::>() { + let srcs = h.linked_outputs(ch, inport).collect::>(); + // Sources could be a mixture of within this polymorphic FuncDefn, and Static edges from outside + h.disconnect(ch, inport); + for (src, outport) in srcs { + h.connect( + node_map.get(&src).copied().unwrap_or(src), + outport, + ch, + inport, + ); } } } + + mono_tgt } fn is_polymorphic_funcdefn(h: &impl HugrView, n: Node) -> bool { From e5f665bfac0c3e848b1e114ec23da19314aa0b84 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 27 Nov 2024 12:09:42 +0000 Subject: [PATCH 08/63] Use Instantiating::poly_func, drop is_polymorphic_funcdefn; and ..**inst --- hugr-core/src/hugr/monomorphize.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 6facf3637..4e5d51610 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -31,17 +31,14 @@ fn mono_scan( cache: &mut Instantiations, reg: &ExtensionRegistry, ) { - if subst_into.is_some() { + if let Some(Instantiating { poly_func, .. }) = subst_into { // First flatten: move all FuncDefns (which do not refer to the TypeParams // being substituted) to be siblings of the enclosing FuncDefn. // TODO/PERF: we should do this only the first time we see each polymorphic FuncDefn. for ch in h.children(parent).collect::>() { if h.get_optype(ch).is_func_defn() { // Lift the FuncDefn out - let enclosing_poly_func = std::iter::successors(Some(ch), |n| h.get_parent(*n)) - .find(|n| is_polymorphic_funcdefn(h, *n)) - .unwrap(); - h.move_after_sibling(ch, enclosing_poly_func); + h.move_after_sibling(ch, *poly_func); } } // Since one can only call up the hierarchy, @@ -66,8 +63,7 @@ fn mono_scan( Some(&mut Instantiating { target_container: new_ch, node_map: inst.node_map, // &mut ref, so borrow - poly_func: inst.poly_func, // Node, so copy - subst: inst.subst, // &ref, so copy + ..**inst }), cache, reg, @@ -159,12 +155,6 @@ fn instantiate( mono_tgt } -fn is_polymorphic_funcdefn(h: &impl HugrView, n: Node) -> bool { - h.get_optype(n) - .as_func_defn() - .is_some_and(|fd| !fd.signature.params().is_empty()) -} - fn name_mangle(name: &str, type_args: &[TypeArg]) -> String { todo!() } From 6c00ad0d4ad42d2b9a92196b4c4e3d1899f9cef0 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 27 Nov 2024 12:35:32 +0000 Subject: [PATCH 09/63] mangle inner names, switch from impl HugrMut to &mut Hugr --- hugr-core/src/hugr/monomorphize.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 4e5d51610..d4b6ec96f 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -7,7 +7,7 @@ use crate::{ Node, }; -use super::{Hugr, HugrMut, HugrView, OpType}; +use super::{internal::HugrMutInternals, Hugr, HugrMut, HugrView, OpType}; pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { let root = h.root(); // I.e. "all monomorphic funcs" for Module-Rooted Hugrs...right? @@ -25,7 +25,7 @@ struct Instantiating<'a> { type Instantiations = HashMap<(Node, Vec), Node>; fn mono_scan( - h: &mut impl HugrMut, + h: &mut Hugr, parent: Node, mut subst_into: Option<&mut Instantiating>, cache: &mut Instantiations, @@ -35,9 +35,11 @@ fn mono_scan( // First flatten: move all FuncDefns (which do not refer to the TypeParams // being substituted) to be siblings of the enclosing FuncDefn. // TODO/PERF: we should do this only the first time we see each polymorphic FuncDefn. + let outer_name = h.get_optype(*poly_func).as_func_defn().unwrap().name.clone(); for ch in h.children(parent).collect::>() { - if h.get_optype(ch).is_func_defn() { + if let OpType::FuncDefn(fd) = h.op_types.get_mut(ch.pg_index()) { // Lift the FuncDefn out + fd.name = mangle_inner_func(&outer_name, &fd.name); h.move_after_sibling(ch, *poly_func); } } @@ -92,7 +94,7 @@ fn mono_scan( } fn instantiate( - h: &mut impl HugrMut, + h: &mut Hugr, poly_func: Node, type_args: Vec, mono_sig: Signature, @@ -158,3 +160,7 @@ fn instantiate( fn name_mangle(name: &str, type_args: &[TypeArg]) -> String { todo!() } + +fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { + todo!() +} \ No newline at end of file From b666dd52c0d1a3863810b9099a87e271b1231037 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 27 Nov 2024 11:59:32 +0000 Subject: [PATCH 10/63] two-level Instantiations, flatten in instantiate() --- hugr-core/src/hugr/monomorphize.rs | 40 ++++++++++++++---------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index d4b6ec96f..c32d39bba 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -16,13 +16,12 @@ pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { } struct Instantiating<'a> { - poly_func: Node, subst: &'a Substitution<'a>, target_container: Node, node_map: &'a mut HashMap, } -type Instantiations = HashMap<(Node, Vec), Node>; +type Instantiations = HashMap, Node>>; fn mono_scan( h: &mut Hugr, @@ -31,21 +30,6 @@ fn mono_scan( cache: &mut Instantiations, reg: &ExtensionRegistry, ) { - if let Some(Instantiating { poly_func, .. }) = subst_into { - // First flatten: move all FuncDefns (which do not refer to the TypeParams - // being substituted) to be siblings of the enclosing FuncDefn. - // TODO/PERF: we should do this only the first time we see each polymorphic FuncDefn. - let outer_name = h.get_optype(*poly_func).as_func_defn().unwrap().name.clone(); - for ch in h.children(parent).collect::>() { - if let OpType::FuncDefn(fd) = h.op_types.get_mut(ch.pg_index()) { - // Lift the FuncDefn out - fd.name = mangle_inner_func(&outer_name, &fd.name); - h.move_after_sibling(ch, *poly_func); - } - } - // Since one can only call up the hierarchy, - // that means we've moved FuncDefns before we encounter any Calls to them - } for old_ch in h.children(parent).collect::>() { let ch_op = h.get_optype(old_ch); if let Some(fd) = ch_op.as_func_defn() { @@ -64,7 +48,7 @@ fn mono_scan( old_ch, Some(&mut Instantiating { target_container: new_ch, - node_map: inst.node_map, // &mut ref, so borrow + node_map: inst.node_map, // &mut ref, so borrow ..**inst }), cache, @@ -101,7 +85,22 @@ fn instantiate( cache: &mut Instantiations, reg: &ExtensionRegistry, ) -> Node { - let ve = match cache.entry((poly_func, type_args.clone())) { + let for_func = cache.entry(poly_func).or_insert_with(|| { + // First time we've instantiated poly_func. Lift any nested FuncDefn's out to the same level. + let outer_name = h.get_optype(poly_func).as_func_defn().unwrap().name.clone(); + let mut to_scan = Vec::from_iter(h.children(poly_func)); + while let Some(n) = to_scan.pop() { + if let OpType::FuncDefn(fd) = h.op_types.get_mut(n.pg_index()) { + fd.name = mangle_inner_func(&outer_name, &fd.name); + h.move_after_sibling(n, poly_func); + } else { + to_scan.extend(h.children(n)) + } + } + HashMap::new() + }); + + let ve = match for_func.entry(type_args.clone()) { Entry::Occupied(n) => return *n.get(), Entry::Vacant(ve) => ve, }; @@ -125,7 +124,6 @@ fn instantiate( h, poly_func, Some(&mut Instantiating { - poly_func, subst: &Substitution::new(&type_args, reg), target_container: mono_tgt, node_map: &mut node_map, @@ -163,4 +161,4 @@ fn name_mangle(name: &str, type_args: &[TypeArg]) -> String { fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { todo!() -} \ No newline at end of file +} From 5a4a0f91438e486c886bc36baa66fddc1220de03 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 28 Nov 2024 20:40:19 +0000 Subject: [PATCH 11/63] Add remove_polyfuncs, using remove_subtree --- hugr-core/src/hugr/monomorphize.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index c32d39bba..19c916a6d 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -15,6 +15,25 @@ pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { h } +pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { + let mut pfs_to_delete = Vec::new(); + let mut to_scan = Vec::from_iter(h.children(h.root())); + while let Some(n) = to_scan.pop() { + if h.get_optype(n) + .as_func_defn() + .is_some_and(|fd| !fd.signature.params().is_empty()) + { + pfs_to_delete.push(n) + } else { + to_scan.extend(h.children(n)); + } + } + for n in pfs_to_delete { + h.remove_subtree(n); + } + h +} + struct Instantiating<'a> { subst: &'a Substitution<'a>, target_container: Node, From 7de2c2bf7c2a0759a1bbc9650f4f7c161f71aa80 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 28 Nov 2024 20:41:41 +0000 Subject: [PATCH 12/63] comments --- hugr-core/src/hugr/monomorphize.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 19c916a6d..88f48c4f7 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -79,6 +79,7 @@ fn mono_scan( old_ch }; + // Now instantiate the target of any Call/LoadFunction to a polymorphic function... let ch_op = h.get_optype(ch); let (type_args, mono_sig) = match ch_op { OpType::Call(c) => (&c.type_args, &c.instantiation), From 5bb0c57d29bf18cc0cc0f2e88bc4ec6f4b8a060f Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 28 Nov 2024 20:42:10 +0000 Subject: [PATCH 13/63] name_mangle => mangle_name --- hugr-core/src/hugr/monomorphize.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 88f48c4f7..e2882c3ad 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -125,7 +125,7 @@ fn instantiate( Entry::Vacant(ve) => ve, }; - let name = name_mangle( + let name = mangle_name( &h.get_optype(poly_func).as_func_defn().unwrap().name, &type_args, ); @@ -175,7 +175,7 @@ fn instantiate( mono_tgt } -fn name_mangle(name: &str, type_args: &[TypeArg]) -> String { +fn mangle_name(name: &str, type_args: &[TypeArg]) -> String { todo!() } From b04e1cfe3d83f075aea98ec93a94da5516f4fe77 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 27 Nov 2024 14:20:04 +0000 Subject: [PATCH 14/63] WIP tests --- hugr-core/src/hugr/monomorphize.rs | 94 ++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index e2882c3ad..d7b8193af 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -182,3 +182,97 @@ fn mangle_name(name: &str, type_args: &[TypeArg]) -> String { fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { todo!() } + +#[cfg(test)] +mod test { + use itertools::Itertools; + use rstest::rstest; + + use crate::builder::BuildHandle; + use crate::builder::{test::simple_dfg_hugr, Container, Dataflow, DataflowSubContainer, HugrBuilder, ModuleBuilder}; + use crate::extension::{prelude::{PRELUDE, USIZE_T}, ExtensionRegistry, EMPTY_REG}; + use crate::ops::handle::FuncID; + use crate::std_extensions::collections::{self, list_type, ListOp, ListValue}; + use crate::types::{PolyFuncType, Signature, Type, TypeBound}; + use crate::{Hugr, HugrView}; + + use super::{monomorphize, mangle_name}; + + + #[rstest] + fn test_null(simple_dfg_hugr: Hugr) { + let mono = monomorphize(simple_dfg_hugr.clone(), &EMPTY_REG); + assert_eq!(simple_dfg_hugr, mono); + } + + fn add_singleton(ctr: &mut impl Container, reg: &ExtensionRegistry) -> BuildHandle> { + let elem_ty = Type::new_var_use(0, TypeBound::Any); + let mut fb = ctr.define_function("singleton", PolyFuncType::new([TypeBound::Any.into()], Signature::new( + elem_ty.clone(), list_type(elem_ty.clone()) + ))).unwrap(); + let [elem] = fb.input_wires_arr(); + let empty = fb.add_load_value(ListValue::new_empty(elem_ty.clone())); + let push = fb.add_dataflow_op(ListOp::push.with_type(elem_ty).to_extension_op(®).unwrap(), [empty, elem]).unwrap(); + fb.finish_with_outputs(push.outputs()).unwrap() + } + + #[test] + fn test_module() { + let reg = ExtensionRegistry::try_new([collections::EXTENSION.to_owned(), PRELUDE.to_owned()]).unwrap(); + let mut mb = ModuleBuilder::new(); + let sing = add_singleton(&mut mb, ®); + let dub = { + let elem_ty = Type::new_var_use(0, TypeBound::Copyable); + let mut fb = mb.define_function("doubled", PolyFuncType::new([TypeBound::Copyable.into()], Signature::new( + elem_ty.clone(), list_type(elem_ty.clone()) + ))).unwrap(); + let [elem] = fb.input_wires_arr(); + let [sing] = fb.call(sing.handle(), &[elem_ty.clone().into()], [elem], ®).unwrap().outputs_arr(); + let push = fb.add_dataflow_op(ListOp::push.with_type(elem_ty).to_extension_op(®).unwrap(), [sing, elem]).unwrap(); + fb.finish_with_outputs(push.outputs()).unwrap() + }; + { + let mut fb = mb.define_function("main", Signature::new( + USIZE_T, + vec![ + list_type(USIZE_T), + list_type(list_type(USIZE_T)) + ])).unwrap(); + let [elem] = fb.input_wires_arr(); + let [two] = fb.call(dub.handle(), &[USIZE_T.into()], [elem], ®).unwrap().outputs_arr(); + let sing = fb.call(sing.handle(), &[USIZE_T.into()], [elem], ®).unwrap(); + let [two_by_one] = fb.call(dub.handle(), &[list_type(USIZE_T).into()], sing.outputs(), ®).unwrap().outputs_arr(); + fb.finish_with_outputs([two, two_by_one]).unwrap(); + } + let hugr = mb.finish_hugr(®).unwrap(); + assert_eq!(hugr.nodes().filter(|n| hugr.get_optype(*n).is_func_defn()).count(), 3); + let mono_hugr = monomorphize(hugr, ®); + mono_hugr.validate(®).unwrap(); + let funcs = mono_hugr.nodes().filter_map(|n| mono_hugr.get_optype(n).as_func_defn()).collect_vec(); + let expected_mangled_names = [mangle_name("singleton", &[USIZE_T.into()]), + mangle_name("doubled", &[USIZE_T.into()]), mangle_name("singleton", &[list_type(USIZE_T).into()]), + mangle_name("doubled", &[list_type(USIZE_T).into()])]; + + assert_eq!(funcs.iter().map(|fd|&fd.name).sorted().collect_vec(), + ["main", "singleton", "doubled"].into_iter().chain(expected_mangled_names.iter().map(String::as_str)).sorted().collect_vec()); + for n in expected_mangled_names { + let mono_fn = funcs.iter().find(|fd|fd.name == n).unwrap(); + assert!(mono_fn.signature.params().is_empty()); + } + } + + #[test] + fn test_flattening() { + + } + + #[test] + fn test_recursive() { + + } + + #[test] + fn test_root_polyfunc() { + + } +} \ No newline at end of file From 4aa6c79659f4d2fbf8734b7036ba4bf8cd6465f8 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Sun, 1 Dec 2024 17:30:38 +0000 Subject: [PATCH 15/63] refactor: let mut = Instantiating; use static_output_port --- hugr-core/src/hugr/monomorphize.rs | 37 +++++++++++------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index d7b8193af..274090d0a 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -62,17 +62,12 @@ fn mono_scan( let new_ch = h.add_node_with_parent(inst.target_container, ch_op.clone().substitute(inst.subst)); inst.node_map.insert(old_ch, new_ch); - mono_scan( - h, - old_ch, - Some(&mut Instantiating { - target_container: new_ch, - node_map: inst.node_map, // &mut ref, so borrow - ..**inst - }), - cache, - reg, - ); + let mut inst = Instantiating { + target_container: new_ch, + node_map: inst.node_map, + ..**inst + }; + mono_scan(h, old_ch, Some(&mut inst), cache, reg); new_ch } else { mono_scan(h, old_ch, None, cache, reg); @@ -92,8 +87,9 @@ fn mono_scan( let fn_inp = ch_op.static_input_port().unwrap(); let tgt = h.static_source(ch).unwrap(); let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); + let fn_out = h.get_optype(new_tgt).static_output_port().unwrap(); h.disconnect(ch, fn_inp); - h.connect(new_tgt, h.num_outputs(new_tgt) - 1, ch, fn_inp); + h.connect(new_tgt, fn_out, ch, fn_inp); } } @@ -140,17 +136,12 @@ fn instantiate( ve.insert(mono_tgt); // Now make the instantiation let mut node_map = HashMap::new(); - mono_scan( - h, - poly_func, - Some(&mut Instantiating { - subst: &Substitution::new(&type_args, reg), - target_container: mono_tgt, - node_map: &mut node_map, - }), - cache, - reg, - ); + let mut inst = Instantiating { + subst: &Substitution::new(&type_args, reg), + target_container: mono_tgt, + node_map: &mut node_map, + }; + mono_scan(h, poly_func, Some(&mut inst), cache, reg); // Copy edges...we have built a node_map for every node in the function. // Note we could avoid building the "large" map (smaller than the Hugr we've just created) // by doing this during recursion, but we'd need to be careful with nonlocal edges - From a7d61371e4de8602a275e79bc75e89bc37b956b8 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Sun, 1 Dec 2024 17:25:17 +0000 Subject: [PATCH 16/63] Fixes: monomorphize Call op; read static input from old_ch --- hugr-core/src/hugr/monomorphize.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 274090d0a..c265e8af1 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -2,7 +2,7 @@ use std::collections::{hash_map::Entry, HashMap}; use crate::{ extension::ExtensionRegistry, - ops::{FuncDefn, OpTrait}, + ops::{Call, FuncDefn, OpTrait}, types::{Signature, Substitution, TypeArg}, Node, }; @@ -77,19 +77,21 @@ fn mono_scan( // Now instantiate the target of any Call/LoadFunction to a polymorphic function... let ch_op = h.get_optype(ch); let (type_args, mono_sig) = match ch_op { - OpType::Call(c) => (&c.type_args, &c.instantiation), - OpType::LoadFunction(lf) => (&lf.type_args, &lf.signature), + OpType::Call(c) => (&c.type_args, c.instantiation.clone()), + OpType::LoadFunction(lf) => (&lf.type_args, lf.signature.clone()), _ => continue, }; if type_args.is_empty() { continue; }; let fn_inp = ch_op.static_input_port().unwrap(); - let tgt = h.static_source(ch).unwrap(); + let tgt = h.static_source(old_ch).unwrap(); // Use old_ch as edges not copied yet let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); let fn_out = h.get_optype(new_tgt).static_output_port().unwrap(); h.disconnect(ch, fn_inp); h.connect(new_tgt, fn_out, ch, fn_inp); + *h.op_types.get_mut(ch.pg_index()) = + Call::try_new(mono_sig.into(), vec![], ®).unwrap().into(); } } From 48a6598a9f3185b297f8673f64ec009f8c3c526d Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Sun, 1 Dec 2024 17:28:06 +0000 Subject: [PATCH 17/63] Test: use pair/triple not list --- hugr-core/src/hugr/monomorphize.rs | 170 ++++++++++++++++++++--------- 1 file changed, 116 insertions(+), 54 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index c265e8af1..7b5e5a85d 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -181,16 +181,18 @@ mod test { use itertools::Itertools; use rstest::rstest; - use crate::builder::BuildHandle; - use crate::builder::{test::simple_dfg_hugr, Container, Dataflow, DataflowSubContainer, HugrBuilder, ModuleBuilder}; - use crate::extension::{prelude::{PRELUDE, USIZE_T}, ExtensionRegistry, EMPTY_REG}; + use crate::builder::test::simple_dfg_hugr; + use crate::builder::{ + BuildHandle, Container, Dataflow, DataflowSubContainer, HugrBuilder, ModuleBuilder, + }; + use crate::extension::prelude::{UnpackTuple, USIZE_T}; + use crate::extension::{EMPTY_REG, PRELUDE_REGISTRY}; use crate::ops::handle::FuncID; - use crate::std_extensions::collections::{self, list_type, ListOp, ListValue}; + use crate::ops::{OpType, Tag}; use crate::types::{PolyFuncType, Signature, Type, TypeBound}; use crate::{Hugr, HugrView}; - use super::{monomorphize, mangle_name}; - + use super::{mangle_name, monomorphize}; #[rstest] fn test_null(simple_dfg_hugr: Hugr) { @@ -198,74 +200,134 @@ mod test { assert_eq!(simple_dfg_hugr, mono); } - fn add_singleton(ctr: &mut impl Container, reg: &ExtensionRegistry) -> BuildHandle> { - let elem_ty = Type::new_var_use(0, TypeBound::Any); - let mut fb = ctr.define_function("singleton", PolyFuncType::new([TypeBound::Any.into()], Signature::new( - elem_ty.clone(), list_type(elem_ty.clone()) - ))).unwrap(); + fn pair_type(ty: Type) -> Type { + Type::new_tuple(vec![ty.clone(), ty]) + } + + fn triple_type(ty: Type) -> Type { + Type::new_tuple(vec![ty.clone(), ty.clone(), ty]) + } + + fn add_double(ctr: &mut impl Container) -> BuildHandle> { + let elem_ty = Type::new_var_use(0, TypeBound::Copyable); + let mut fb = ctr + .define_function( + "double", + PolyFuncType::new( + [TypeBound::Copyable.into()], + Signature::new(elem_ty.clone(), pair_type(elem_ty.clone())), + ), + ) + .unwrap(); let [elem] = fb.input_wires_arr(); - let empty = fb.add_load_value(ListValue::new_empty(elem_ty.clone())); - let push = fb.add_dataflow_op(ListOp::push.with_type(elem_ty).to_extension_op(®).unwrap(), [empty, elem]).unwrap(); - fb.finish_with_outputs(push.outputs()).unwrap() + let tag = Tag::new(0, vec![vec![elem_ty.clone(), elem_ty].into()]); + let tag = fb.add_dataflow_op(tag, [elem, elem]).unwrap(); + fb.finish_with_outputs(tag.outputs()).unwrap() } #[test] fn test_module() { - let reg = ExtensionRegistry::try_new([collections::EXTENSION.to_owned(), PRELUDE.to_owned()]).unwrap(); let mut mb = ModuleBuilder::new(); - let sing = add_singleton(&mut mb, ®); - let dub = { + let doub = add_double(&mut mb); + let trip = { let elem_ty = Type::new_var_use(0, TypeBound::Copyable); - let mut fb = mb.define_function("doubled", PolyFuncType::new([TypeBound::Copyable.into()], Signature::new( - elem_ty.clone(), list_type(elem_ty.clone()) - ))).unwrap(); + let mut fb = mb + .define_function( + "triple", + PolyFuncType::new( + [TypeBound::Copyable.into()], + Signature::new(elem_ty.clone(), Type::new_tuple(vec![elem_ty.clone(); 3])), + ), + ) + .unwrap(); let [elem] = fb.input_wires_arr(); - let [sing] = fb.call(sing.handle(), &[elem_ty.clone().into()], [elem], ®).unwrap().outputs_arr(); - let push = fb.add_dataflow_op(ListOp::push.with_type(elem_ty).to_extension_op(®).unwrap(), [sing, elem]).unwrap(); - fb.finish_with_outputs(push.outputs()).unwrap() + let [pair] = fb + .call( + doub.handle(), + &[elem_ty.clone().into()], + [elem], + &PRELUDE_REGISTRY, + ) + .unwrap() + .outputs_arr(); + let [elem1, elem2] = fb + .add_dataflow_op(UnpackTuple(vec![elem_ty.clone(); 2].into()), [pair]) + .unwrap() + .outputs_arr(); + let tag = Tag::new(0, vec![vec![elem_ty.clone(); 3].into()]); + let trip = fb.add_dataflow_op(tag, [elem1, elem2, elem]).unwrap(); + fb.finish_with_outputs(trip.outputs()).unwrap() }; { - let mut fb = mb.define_function("main", Signature::new( - USIZE_T, - vec![ - list_type(USIZE_T), - list_type(list_type(USIZE_T)) - ])).unwrap(); + let mut fb = mb + .define_function( + "main", + Signature::new( + USIZE_T, + vec![triple_type(USIZE_T), triple_type(pair_type(USIZE_T))], + ), + ) + .unwrap(); let [elem] = fb.input_wires_arr(); - let [two] = fb.call(dub.handle(), &[USIZE_T.into()], [elem], ®).unwrap().outputs_arr(); - let sing = fb.call(sing.handle(), &[USIZE_T.into()], [elem], ®).unwrap(); - let [two_by_one] = fb.call(dub.handle(), &[list_type(USIZE_T).into()], sing.outputs(), ®).unwrap().outputs_arr(); - fb.finish_with_outputs([two, two_by_one]).unwrap(); + let [res1] = fb + .call(trip.handle(), &[USIZE_T.into()], [elem], &PRELUDE_REGISTRY) + .unwrap() + .outputs_arr(); + let pair = fb + .call(doub.handle(), &[USIZE_T.into()], [elem], &PRELUDE_REGISTRY) + .unwrap(); + let [res2] = fb + .call( + trip.handle(), + &[pair_type(USIZE_T).into()], + pair.outputs(), + &PRELUDE_REGISTRY, + ) + .unwrap() + .outputs_arr(); + fb.finish_with_outputs([res1, res2]).unwrap(); } - let hugr = mb.finish_hugr(®).unwrap(); - assert_eq!(hugr.nodes().filter(|n| hugr.get_optype(*n).is_func_defn()).count(), 3); - let mono_hugr = monomorphize(hugr, ®); - mono_hugr.validate(®).unwrap(); - let funcs = mono_hugr.nodes().filter_map(|n| mono_hugr.get_optype(n).as_func_defn()).collect_vec(); - let expected_mangled_names = [mangle_name("singleton", &[USIZE_T.into()]), - mangle_name("doubled", &[USIZE_T.into()]), mangle_name("singleton", &[list_type(USIZE_T).into()]), - mangle_name("doubled", &[list_type(USIZE_T).into()])]; + let hugr = mb.finish_hugr(&PRELUDE_REGISTRY).unwrap(); + assert_eq!( + hugr.nodes() + .filter(|n| hugr.get_optype(*n).is_func_defn()) + .count(), + 3 + ); + let mono_hugr = monomorphize(hugr, &PRELUDE_REGISTRY); + mono_hugr.validate(&PRELUDE_REGISTRY).unwrap(); + + let funcs = mono_hugr + .nodes() + .filter_map(|n| mono_hugr.get_optype(n).as_func_defn()) + .collect_vec(); + let expected_mangled_names = [ + mangle_name("double", &[USIZE_T.into()]), + mangle_name("triple", &[USIZE_T.into()]), + mangle_name("double", &[triple_type(USIZE_T).into()]), + mangle_name("triple_type", &[triple_type(USIZE_T).into()]), + ]; - assert_eq!(funcs.iter().map(|fd|&fd.name).sorted().collect_vec(), - ["main", "singleton", "doubled"].into_iter().chain(expected_mangled_names.iter().map(String::as_str)).sorted().collect_vec()); + assert_eq!( + funcs.iter().map(|fd| &fd.name).sorted().collect_vec(), + ["main", "double", "triple"] + .into_iter() + .chain(expected_mangled_names.iter().map(String::as_str)) + .sorted() + .collect_vec() + ); for n in expected_mangled_names { - let mono_fn = funcs.iter().find(|fd|fd.name == n).unwrap(); + let mono_fn = funcs.iter().find(|fd| fd.name == n).unwrap(); assert!(mono_fn.signature.params().is_empty()); } } #[test] - fn test_flattening() { - - } + fn test_flattening() {} #[test] - fn test_recursive() { - - } + fn test_recursive() {} #[test] - fn test_root_polyfunc() { - - } -} \ No newline at end of file + fn test_root_polyfunc() {} +} From 937a6d0ea69b9d9fa8ab597f7ba104c6974205b5 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Sun, 1 Dec 2024 17:28:34 +0000 Subject: [PATCH 18/63] Implement mangle_name --- hugr-core/src/hugr/monomorphize.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 7b5e5a85d..2cd9c5800 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -169,7 +169,8 @@ fn instantiate( } fn mangle_name(name: &str, type_args: &[TypeArg]) -> String { - todo!() + let s = format!("__{name}_{type_args:?}"); + s.replace(&['[', ']', '{', '}', ' '], "_") } fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { From f15f09fd520bedda781d2fd8545e6c0eda14e2ea Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 10:05:04 +0000 Subject: [PATCH 19/63] fixup! Test: use pair --- hugr-core/src/hugr/monomorphize.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 2cd9c5800..01bc5efe8 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -305,8 +305,8 @@ mod test { let expected_mangled_names = [ mangle_name("double", &[USIZE_T.into()]), mangle_name("triple", &[USIZE_T.into()]), - mangle_name("double", &[triple_type(USIZE_T).into()]), - mangle_name("triple_type", &[triple_type(USIZE_T).into()]), + mangle_name("double", &[pair_type(USIZE_T).into()]), + mangle_name("triple", &[pair_type(USIZE_T).into()]), ]; assert_eq!( From d5f15ff8d3357cd1da94fb1102d746f764f9304a Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 10:07:09 +0000 Subject: [PATCH 20/63] fix edge handling --- hugr-core/src/hugr/monomorphize.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 01bc5efe8..3013b8341 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -87,9 +87,14 @@ fn mono_scan( let fn_inp = ch_op.static_input_port().unwrap(); let tgt = h.static_source(old_ch).unwrap(); // Use old_ch as edges not copied yet let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); - let fn_out = h.get_optype(new_tgt).static_output_port().unwrap(); - h.disconnect(ch, fn_inp); - h.connect(new_tgt, fn_out, ch, fn_inp); + if let Some(ref mut inst) = subst_into { + // Edges will be added by instantiate + inst.node_map.insert(tgt, new_tgt); + } else { + let fn_out = h.get_optype(new_tgt).static_output_port().unwrap(); + h.disconnect(ch, fn_inp); + h.connect(new_tgt, fn_out, ch, fn_inp); + } *h.op_types.get_mut(ch.pg_index()) = Call::try_new(mono_sig.into(), vec![], ®).unwrap().into(); } @@ -149,16 +154,15 @@ fn instantiate( // by doing this during recursion, but we'd need to be careful with nonlocal edges - // 'ext' edges by copying every node before recursing on any of them, // 'dom' edges would *also* require recursing in dominator-tree preorder. - for &ch in node_map.keys() { - for inport in h.node_inputs(ch).collect::>() { - let srcs = h.linked_outputs(ch, inport).collect::>(); - // Sources could be a mixture of within this polymorphic FuncDefn, and Static edges from outside - h.disconnect(ch, inport); + for (&old_ch, &new_ch) in node_map.iter() { + for inport in h.node_inputs(old_ch).collect::>() { + let srcs = h.linked_outputs(old_ch, inport).collect::>(); for (src, outport) in srcs { + // Sources could be a mixture of within this polymorphic FuncDefn, and Static edges from outside h.connect( node_map.get(&src).copied().unwrap_or(src), outport, - ch, + new_ch, inport, ); } From ad00327ca06dbe3ae4009a17bff74be5b5e55d8f Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 10:12:15 +0000 Subject: [PATCH 21/63] clippy/lint, docs --- hugr-core/src/hugr.rs | 2 +- hugr-core/src/hugr/monomorphize.rs | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 5decc22a2..9aa5d080f 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -31,7 +31,7 @@ use crate::ops::{OpTag, OpTrait}; pub use crate::ops::{OpType, DEFAULT_OPTYPE}; use crate::{Direction, Node}; -pub use monomorphize::monomorphize; +pub use monomorphize::{monomorphize, remove_polyfuncs}; /// The Hugr data structure. #[derive(Clone, Debug, PartialEq)] diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 3013b8341..43f8c65a6 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -9,12 +9,18 @@ use crate::{ use super::{internal::HugrMutInternals, Hugr, HugrMut, HugrView, OpType}; +/// Replaces calls to polymorphic functions with calls to new monomorphic +/// instantiations of the polymorphic ones. The original polymorphic [FuncDefn]s +/// are left untouched (including Calls inside them) - see [remove_polyfuncs] pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { let root = h.root(); // I.e. "all monomorphic funcs" for Module-Rooted Hugrs...right? mono_scan(&mut h, root, None, &mut HashMap::new(), reg); h } +/// Removes any polymorphic [FuncDefn]s from the Hugr. Note that if these have +/// calls from *monomorphic* code, this will make the Hugr invalid (call [monomorphize] +/// first). pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { let mut pfs_to_delete = Vec::new(); let mut to_scan = Vec::from_iter(h.children(h.root())); @@ -42,6 +48,10 @@ struct Instantiating<'a> { type Instantiations = HashMap, Node>>; +/// Scans a subtree for polymorphic calls and monomorphizes them by instantiating the +/// called functions (if instantations not already in `cache`). +/// Optionally copies the subtree into a new location whilst applying a substitution. +/// The subtree should be monomorphic after the substitution (if provided) has been applied. fn mono_scan( h: &mut Hugr, parent: Node, @@ -96,7 +106,7 @@ fn mono_scan( h.connect(new_tgt, fn_out, ch, fn_inp); } *h.op_types.get_mut(ch.pg_index()) = - Call::try_new(mono_sig.into(), vec![], ®).unwrap().into(); + Call::try_new(mono_sig.into(), vec![], reg).unwrap().into(); } } @@ -174,10 +184,10 @@ fn instantiate( fn mangle_name(name: &str, type_args: &[TypeArg]) -> String { let s = format!("__{name}_{type_args:?}"); - s.replace(&['[', ']', '{', '}', ' '], "_") + s.replace(['[', ']', '{', '}', ' '], "_") } -fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { +fn mangle_inner_func(_outer_name: &str, _inner_name: &str) -> String { todo!() } @@ -193,7 +203,7 @@ mod test { use crate::extension::prelude::{UnpackTuple, USIZE_T}; use crate::extension::{EMPTY_REG, PRELUDE_REGISTRY}; use crate::ops::handle::FuncID; - use crate::ops::{OpType, Tag}; + use crate::ops::Tag; use crate::types::{PolyFuncType, Signature, Type, TypeBound}; use crate::{Hugr, HugrView}; From c5a0f8d5a84504fccfddd1f7bd1861fe71227af0 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 10:37:28 +0000 Subject: [PATCH 22/63] test remove_polyfuncs --- hugr-core/src/hugr/monomorphize.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 43f8c65a6..3253d8b9f 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -202,6 +202,7 @@ mod test { }; use crate::extension::prelude::{UnpackTuple, USIZE_T}; use crate::extension::{EMPTY_REG, PRELUDE_REGISTRY}; + use crate::hugr::monomorphize::remove_polyfuncs; use crate::ops::handle::FuncID; use crate::ops::Tag; use crate::types::{PolyFuncType, Signature, Type, TypeBound}; @@ -331,10 +332,31 @@ mod test { .sorted() .collect_vec() ); - for n in expected_mangled_names { - let mono_fn = funcs.iter().find(|fd| fd.name == n).unwrap(); + for n in expected_mangled_names.iter() { + let mono_fn = funcs.iter().find(|fd| &fd.name == n).unwrap(); assert!(mono_fn.signature.params().is_empty()); } + + assert_eq!( + monomorphize(mono_hugr.clone(), &PRELUDE_REGISTRY), + mono_hugr + ); // Idempotent + + let nopoly = remove_polyfuncs(mono_hugr); + let funcs = nopoly + .nodes() + .filter_map(|n| nopoly.get_optype(n).as_func_defn()) + .collect_vec(); + + assert_eq!( + funcs.iter().map(|fd| &fd.name).sorted().collect_vec(), + expected_mangled_names + .iter() + .chain(std::iter::once(&"main".into())) + .sorted() + .collect_vec() + ); + assert!(funcs.into_iter().all(|fd| fd.signature.params().is_empty())); } #[test] From 04d60b18cd3a7640b6fff60f5a93d7883ca490b8 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 17:26:00 +0000 Subject: [PATCH 23/63] Shorten test a bit --- hugr-core/src/hugr/monomorphize.rs | 49 +++++++++++------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 3253d8b9f..dbcd2188a 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -226,17 +226,13 @@ mod test { fn add_double(ctr: &mut impl Container) -> BuildHandle> { let elem_ty = Type::new_var_use(0, TypeBound::Copyable); - let mut fb = ctr - .define_function( - "double", - PolyFuncType::new( - [TypeBound::Copyable.into()], - Signature::new(elem_ty.clone(), pair_type(elem_ty.clone())), - ), - ) - .unwrap(); + let pfty = PolyFuncType::new( + [TypeBound::Copyable.into()], + Signature::new(elem_ty.clone(), pair_type(elem_ty.clone())), + ); + let mut fb = ctr.define_function("double", pfty).unwrap(); let [elem] = fb.input_wires_arr(); - let tag = Tag::new(0, vec![vec![elem_ty.clone(), elem_ty].into()]); + let tag = Tag::new(0, vec![vec![elem_ty; 2].into()]); let tag = fb.add_dataflow_op(tag, [elem, elem]).unwrap(); fb.finish_with_outputs(tag.outputs()).unwrap() } @@ -246,31 +242,22 @@ mod test { let mut mb = ModuleBuilder::new(); let doub = add_double(&mut mb); let trip = { - let elem_ty = Type::new_var_use(0, TypeBound::Copyable); - let mut fb = mb - .define_function( - "triple", - PolyFuncType::new( - [TypeBound::Copyable.into()], - Signature::new(elem_ty.clone(), Type::new_tuple(vec![elem_ty.clone(); 3])), - ), - ) - .unwrap(); + let tv0 = || Type::new_var_use(0, TypeBound::Copyable); + let pfty = PolyFuncType::new( + [TypeBound::Copyable.into()], + Signature::new(tv0(), Type::new_tuple(vec![tv0(); 3])), + ); + let mut fb = mb.define_function("triple", pfty).unwrap(); let [elem] = fb.input_wires_arr(); - let [pair] = fb - .call( - doub.handle(), - &[elem_ty.clone().into()], - [elem], - &PRELUDE_REGISTRY, - ) - .unwrap() - .outputs_arr(); + let pair = fb + .call(doub.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY) + .unwrap(); + let [elem1, elem2] = fb - .add_dataflow_op(UnpackTuple(vec![elem_ty.clone(); 2].into()), [pair]) + .add_dataflow_op(UnpackTuple(vec![tv0(); 2].into()), pair.outputs()) .unwrap() .outputs_arr(); - let tag = Tag::new(0, vec![vec![elem_ty.clone(); 3].into()]); + let tag = Tag::new(0, vec![vec![tv0(); 3].into()]); let trip = fb.add_dataflow_op(tag, [elem1, elem2, elem]).unwrap(); fb.finish_with_outputs(trip.outputs()).unwrap() }; From 3be790ee0da73e64347fde4304dd328c85b36d97 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 17:43:49 +0000 Subject: [PATCH 24/63] Factor out is_polymorphic_funcdefn, avoid root --- hugr-core/src/hugr/monomorphize.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index dbcd2188a..2f00775ec 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -14,7 +14,10 @@ use super::{internal::HugrMutInternals, Hugr, HugrMut, HugrView, OpType}; /// are left untouched (including Calls inside them) - see [remove_polyfuncs] pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { let root = h.root(); // I.e. "all monomorphic funcs" for Module-Rooted Hugrs...right? - mono_scan(&mut h, root, None, &mut HashMap::new(), reg); + // 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(&mut h, root, None, &mut HashMap::new(), reg); + } h } @@ -25,10 +28,7 @@ pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { let mut pfs_to_delete = Vec::new(); let mut to_scan = Vec::from_iter(h.children(h.root())); while let Some(n) = to_scan.pop() { - if h.get_optype(n) - .as_func_defn() - .is_some_and(|fd| !fd.signature.params().is_empty()) - { + if is_polymorphic_funcdefn(h.get_optype(n)) { pfs_to_delete.push(n) } else { to_scan.extend(h.children(n)); @@ -40,6 +40,11 @@ pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { h } +fn is_polymorphic_funcdefn(t: &OpType) -> bool { + t.as_func_defn() + .is_some_and(|fd| !fd.signature.params().is_empty()) +} + struct Instantiating<'a> { subst: &'a Substitution<'a>, target_container: Node, @@ -61,12 +66,10 @@ fn mono_scan( ) { for old_ch in h.children(parent).collect::>() { let ch_op = h.get_optype(old_ch); - if let Some(fd) = ch_op.as_func_defn() { - assert!(subst_into.is_none()); - if !fd.signature.params().is_empty() { - continue; - } - } + debug_assert!(!ch_op.is_func_defn() || subst_into.is_none()); // If substituting, should have flattened already + if is_polymorphic_funcdefn(ch_op) { + continue; + }; // Perform substitution, and recurse into containers (mono_scan does nothing if no children) let ch = if let Some(ref mut inst) = subst_into { let new_ch = @@ -351,7 +354,4 @@ mod test { #[test] fn test_recursive() {} - - #[test] - fn test_root_polyfunc() {} } From 4a9f7a20a710c6b14f48f8bf7a58ba1005d480df Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 18:52:46 +0000 Subject: [PATCH 25/63] remove_polyfuncs if non-module-rooted; implement mangle_inner_func --- hugr-core/src/hugr/monomorphize.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 2f00775ec..92ee04760 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -10,13 +10,20 @@ use crate::{ use super::{internal::HugrMutInternals, Hugr, HugrMut, HugrView, OpType}; /// Replaces calls to polymorphic functions with calls to new monomorphic -/// instantiations of the polymorphic ones. The original polymorphic [FuncDefn]s -/// are left untouched (including Calls inside them) - see [remove_polyfuncs] +/// instantiations of the polymorphic ones. +/// +/// If the Hugr is [Module](OpType::Module)-rooted, +/// * then the original polymorphic [FuncDefn]s are left untouched (including Calls inside them) +/// - call [remove_polyfuncs] when no other Hugr will be linked in that might instantiate these +/// * else, the originals are removed (they are invisible from outside the Hugr). pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { - let root = h.root(); // I.e. "all monomorphic funcs" for Module-Rooted Hugrs...right? + let root = h.root(); // 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(&mut h, root, None, &mut HashMap::new(), reg); + if !h.get_optype(root).is_module() { + return remove_polyfuncs(h); + } } h } @@ -190,8 +197,8 @@ fn mangle_name(name: &str, type_args: &[TypeArg]) -> String { s.replace(['[', ']', '{', '}', ' '], "_") } -fn mangle_inner_func(_outer_name: &str, _inner_name: &str) -> String { - todo!() +fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { + format!("$_{outer_name}_$_{inner_name}") } #[cfg(test)] From 8178b5d808130f891b3febe26ea74fbf7964acca Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 18:53:30 +0000 Subject: [PATCH 26/63] Fix monomorphized func -> Call edges --- hugr-core/src/hugr/monomorphize.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 92ee04760..9c2d6e56e 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -107,14 +107,10 @@ fn mono_scan( let fn_inp = ch_op.static_input_port().unwrap(); let tgt = h.static_source(old_ch).unwrap(); // Use old_ch as edges not copied yet let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); - if let Some(ref mut inst) = subst_into { - // Edges will be added by instantiate - inst.node_map.insert(tgt, new_tgt); - } else { - let fn_out = h.get_optype(new_tgt).static_output_port().unwrap(); - h.disconnect(ch, fn_inp); - h.connect(new_tgt, fn_out, ch, fn_inp); - } + let fn_out = h.get_optype(new_tgt).static_output_port().unwrap(); + h.disconnect(ch, fn_inp); // No-op if copying+substituting + h.connect(new_tgt, fn_out, ch, fn_inp); + *h.op_types.get_mut(ch.pg_index()) = Call::try_new(mono_sig.into(), vec![], reg).unwrap().into(); } @@ -176,6 +172,11 @@ fn instantiate( // 'dom' edges would *also* require recursing in dominator-tree preorder. for (&old_ch, &new_ch) in node_map.iter() { for inport in h.node_inputs(old_ch).collect::>() { + // Edges from monomorphized functions to their calls already added during mono_scan() + // as these depend not just on the original FuncDefn but also the TypeArgs + if h.linked_outputs(new_ch, inport).next().is_some() { + continue; + }; let srcs = h.linked_outputs(old_ch, inport).collect::>(); for (src, outport) in srcs { // Sources could be a mixture of within this polymorphic FuncDefn, and Static edges from outside From 2090218f02ba6c993a09f8db4b4b1fc0470b026c Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 19:00:25 +0000 Subject: [PATCH 27/63] Test flattening (and multiple calls with different typeargs from one polyfunc) --- hugr-core/src/hugr/monomorphize.rs | 110 ++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 8 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 9c2d6e56e..8a83850d4 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -209,17 +209,19 @@ mod test { use crate::builder::test::simple_dfg_hugr; use crate::builder::{ - BuildHandle, Container, Dataflow, DataflowSubContainer, HugrBuilder, ModuleBuilder, + BuildHandle, Container, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, + HugrBuilder, ModuleBuilder, }; - use crate::extension::prelude::{UnpackTuple, USIZE_T}; - use crate::extension::{EMPTY_REG, PRELUDE_REGISTRY}; - use crate::hugr::monomorphize::remove_polyfuncs; + use crate::extension::prelude::{ConstUsize, UnpackTuple, USIZE_T}; + use crate::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; + use crate::hugr::monomorphize::mangle_inner_func; use crate::ops::handle::FuncID; use crate::ops::Tag; + use crate::std_extensions::arithmetic::int_types::{self, INT_TYPES}; use crate::types::{PolyFuncType, Signature, Type, TypeBound}; - use crate::{Hugr, HugrView}; + use crate::{type_row, Hugr, HugrView}; - use super::{mangle_name, monomorphize}; + use super::{mangle_name, monomorphize, remove_polyfuncs}; #[rstest] fn test_null(simple_dfg_hugr: Hugr) { @@ -358,8 +360,100 @@ mod test { } #[test] - fn test_flattening() {} + fn test_flattening() { + //polyf1 contains polyf2 contains monof -> pf1 and pf1 share pf2's and they share monof + + let reg = ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()]) + .unwrap(); + let tv0 = || Type::new_var_use(0, TypeBound::Any); + let ity = || INT_TYPES[3].clone(); + + let mut outer = FunctionBuilder::new("mainish", Signature::new(ity(), USIZE_T)).unwrap(); + let sig = PolyFuncType::new( + [TypeBound::Any.into()], + Signature::new(tv0(), vec![tv0(), USIZE_T, USIZE_T]), + ); + let mut pf1 = outer.define_function("pf1", sig).unwrap(); + + let sig = PolyFuncType::new( + [TypeBound::Any.into()], + Signature::new(tv0(), vec![tv0(), USIZE_T]), + ); + let mut pf2 = pf1.define_function("pf2", sig).unwrap(); + + let mono_func = { + let mut mono_b = pf2 + .define_function("get_usz", Signature::new(type_row![], USIZE_T)) + .unwrap(); + let cst0 = mono_b.add_load_value(ConstUsize::new(1)); + mono_b.finish_with_outputs([cst0]).unwrap() + }; + let pf2 = { + let [inw] = pf2.input_wires_arr(); + + let [usz] = pf2 + .call(mono_func.handle(), &[], [], ®) + .unwrap() + .outputs_arr(); + + pf2.finish_with_outputs([inw, usz]).unwrap() + }; + // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not + let [a, u] = pf1 + .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®) + .unwrap() + .outputs_arr(); + let [u1, u2] = pf1 + .call(pf2.handle(), &[USIZE_T.into()], [u], ®) + .unwrap() + .outputs_arr(); + let pf1 = pf1.finish_with_outputs([a, u1, u2]).unwrap(); + // Outer: two calls to pf1 with different TypeArgs + let [_, u, _] = outer + .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®) + .unwrap() + .outputs_arr(); + let [_, u, _] = outer + .call(pf1.handle(), &[USIZE_T.into()], [u], ®) + .unwrap() + .outputs_arr(); + let hugr = outer.finish_hugr_with_outputs([u], ®).unwrap(); + + let mono_hugr = monomorphize(hugr, ®); + mono_hugr.validate(®).unwrap(); + let funcs = mono_hugr + .nodes() + .filter_map(|n| mono_hugr.get_optype(n).as_func_defn().map(|fd| (n, fd))) + .collect_vec(); + let pf2_name = mangle_inner_func("pf1", "pf2"); + assert_eq!( + funcs.iter().map(|(_, fd)| &fd.name).sorted().collect_vec(), + vec![ + &mangle_name("pf1", &[ity().into()]), + &mangle_name("pf1", &[USIZE_T.into()]), + &mangle_name(&pf2_name, &[ity().into()]), // from pf1 + &mangle_name(&pf2_name, &[USIZE_T.into()]), // from pf1 and (2*)pf1 + &mangle_inner_func(&pf2_name, "get_usz"), + "mainish" + ] + .into_iter() + .sorted() + .collect_vec() + ); + for (n, fd) in funcs { + assert!(fd.signature.params().is_empty()); + assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); + } + } #[test] - fn test_recursive() {} + fn test_not_flattened() { + //monof2 contains polyf3 (and instantiates) - not moved + //polyf4 contains polyf5 but not instantiated -> not moved + } + + #[test] + fn test_recursive() { + // make map, + } } From 4429152e7cd999b9e3f14451e0d283fe5881c9eb Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 20:31:30 +0000 Subject: [PATCH 28/63] tidy --- hugr-core/src/hugr/monomorphize.rs | 179 +++++++++++------------------ 1 file changed, 66 insertions(+), 113 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 4fa290507..1751ccc86 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -204,6 +204,8 @@ fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { #[cfg(test)] mod test { + use std::collections::HashMap; + use itertools::Itertools; use rstest::rstest; @@ -251,82 +253,57 @@ mod test { } #[test] - fn test_module() { + fn test_module() -> Result<(), Box> { let mut mb = ModuleBuilder::new(); - let doub = add_double(&mut mb); - let trip = { + let db = add_double(&mut mb); + let tr = { let tv0 = || Type::new_var_use(0, TypeBound::Copyable); let pfty = PolyFuncType::new( [TypeBound::Copyable.into()], Signature::new(tv0(), Type::new_tuple(vec![tv0(); 3])), ); - let mut fb = mb.define_function("triple", pfty).unwrap(); + let mut fb = mb.define_function("triple", pfty)?; let [elem] = fb.input_wires_arr(); - let pair = fb - .call(doub.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY) - .unwrap(); + let pair = fb.call(db.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY)?; let [elem1, elem2] = fb - .add_dataflow_op(UnpackTuple(vec![tv0(); 2].into()), pair.outputs()) - .unwrap() + .add_dataflow_op(UnpackTuple(vec![tv0(); 2].into()), pair.outputs())? .outputs_arr(); let tag = Tag::new(0, vec![vec![tv0(); 3].into()]); - let trip = fb.add_dataflow_op(tag, [elem1, elem2, elem]).unwrap(); - fb.finish_with_outputs(trip.outputs()).unwrap() + let trip = fb.add_dataflow_op(tag, [elem1, elem2, elem])?; + fb.finish_with_outputs(trip.outputs())? }; { - let mut fb = mb - .define_function( - "main", - Signature::new( - usize_t(), - vec![triple_type(usize_t()), triple_type(pair_type(usize_t()))], - ), - ) - .unwrap(); + let sig = Signature::new( + usize_t(), + vec![triple_type(usize_t()), triple_type(pair_type(usize_t()))], + ); + let mut fb = mb.define_function("main", sig)?; let [elem] = fb.input_wires_arr(); let [res1] = fb - .call( - trip.handle(), - &[usize_t().into()], - [elem], - &PRELUDE_REGISTRY, - ) - .unwrap() + .call(tr.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)? .outputs_arr(); - let pair = fb - .call( - doub.handle(), - &[usize_t().into()], - [elem], - &PRELUDE_REGISTRY, - ) - .unwrap(); + let pair = fb.call(db.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)?; + let pty = pair_type(usize_t()).into(); let [res2] = fb - .call( - trip.handle(), - &[pair_type(usize_t()).into()], - pair.outputs(), - &PRELUDE_REGISTRY, - ) - .unwrap() + .call(tr.handle(), &[pty], pair.outputs(), &PRELUDE_REGISTRY)? .outputs_arr(); - fb.finish_with_outputs([res1, res2]).unwrap(); + fb.finish_with_outputs([res1, res2])?; } - let hugr = mb.finish_hugr(&PRELUDE_REGISTRY).unwrap(); + let hugr = mb.finish_hugr(&PRELUDE_REGISTRY)?; assert_eq!( hugr.nodes() .filter(|n| hugr.get_optype(*n).is_func_defn()) .count(), 3 ); - let mono_hugr = monomorphize(hugr, &PRELUDE_REGISTRY); - mono_hugr.validate(&PRELUDE_REGISTRY).unwrap(); + let mono = monomorphize(hugr, &PRELUDE_REGISTRY); + mono.validate(&PRELUDE_REGISTRY)?; - let funcs = mono_hugr + let mut funcs = mono .nodes() - .filter_map(|n| mono_hugr.get_optype(n).as_func_defn()) - .collect_vec(); + .filter_map(|n| mono.get_optype(n).as_func_defn().map(|fd| (&fd.name, fd))) + .collect::>(); let expected_mangled_names = [ mangle_name("double", &[usize_t().into()]), mangle_name("triple", &[usize_t().into()]), @@ -334,103 +311,78 @@ mod test { mangle_name("triple", &[pair_type(usize_t()).into()]), ]; - assert_eq!( - funcs.iter().map(|fd| &fd.name).sorted().collect_vec(), - ["main", "double", "triple"] - .into_iter() - .chain(expected_mangled_names.iter().map(String::as_str)) - .sorted() - .collect_vec() - ); for n in expected_mangled_names.iter() { - let mono_fn = funcs.iter().find(|fd| &fd.name == n).unwrap(); - assert!(mono_fn.signature.params().is_empty()); + let mono_fn = funcs.remove(n).unwrap(); + assert!((*mono_fn).signature.params().is_empty()); } assert_eq!( - monomorphize(mono_hugr.clone(), &PRELUDE_REGISTRY), - mono_hugr - ); // Idempotent + funcs.keys().sorted().collect_vec(), + ["double", "main", "triple"].iter().collect_vec() + ); - let nopoly = remove_polyfuncs(mono_hugr); - let funcs = nopoly + assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent + + let nopoly = remove_polyfuncs(mono); + let mut funcs = nopoly .nodes() - .filter_map(|n| nopoly.get_optype(n).as_func_defn()) - .collect_vec(); + .filter_map(|n| nopoly.get_optype(n).as_func_defn().map(|fd| (&fd.name, fd))) + .collect::>(); - assert_eq!( - funcs.iter().map(|fd| &fd.name).sorted().collect_vec(), - expected_mangled_names - .iter() - .chain(std::iter::once(&"main".into())) - .sorted() - .collect_vec() - ); - assert!(funcs.into_iter().all(|fd| fd.signature.params().is_empty())); + assert!(funcs.values().all(|fd| (*fd).signature.params().is_empty())); + for n in expected_mangled_names { + assert!(funcs.remove(&n).is_some()); + } + assert_eq!(funcs.keys().collect_vec(), vec![&"main"]); + Ok(()) } #[test] - fn test_flattening() { - //polyf1 contains polyf2 contains monof -> pf1 and pf1 share pf2's and they share monof + fn test_flattening() -> Result<(), Box> { + //pf1 contains pf2 contains mono_func -> pf1 and pf1 share pf2's and they share mono_func - let reg = ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()]) - .unwrap(); + let reg = + ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; let tv0 = || Type::new_var_use(0, TypeBound::Any); + let pf_any = |sig: Signature| PolyFuncType::new([TypeBound::Any.into()], sig); let ity = || INT_TYPES[3].clone(); - let mut outer = FunctionBuilder::new("mainish", Signature::new(ity(), usize_t())).unwrap(); - let sig = PolyFuncType::new( - [TypeBound::Any.into()], - Signature::new(tv0(), vec![tv0(), usize_t(), usize_t()]), - ); - let mut pf1 = outer.define_function("pf1", sig).unwrap(); + let mut outer = FunctionBuilder::new("mainish", Signature::new(ity(), usize_t()))?; + let sig = pf_any(Signature::new(tv0(), vec![tv0(), usize_t(), usize_t()])); + let mut pf1 = outer.define_function("pf1", sig)?; - let sig = PolyFuncType::new( - [TypeBound::Any.into()], - Signature::new(tv0(), vec![tv0(), usize_t()]), - ); - let mut pf2 = pf1.define_function("pf2", sig).unwrap(); + let sig = pf_any(Signature::new(tv0(), vec![tv0(), usize_t()])); + let mut pf2 = pf1.define_function("pf2", sig)?; let mono_func = { - let mut mono_b = pf2 - .define_function("get_usz", Signature::new(type_row![], usize_t())) - .unwrap(); - let cst0 = mono_b.add_load_value(ConstUsize::new(1)); - mono_b.finish_with_outputs([cst0]).unwrap() + let mut fb = pf2.define_function("get_usz", Signature::new(type_row![], usize_t()))?; + let cst0 = fb.add_load_value(ConstUsize::new(1)); + fb.finish_with_outputs([cst0])? }; let pf2 = { let [inw] = pf2.input_wires_arr(); - - let [usz] = pf2 - .call(mono_func.handle(), &[], [], ®) - .unwrap() - .outputs_arr(); - - pf2.finish_with_outputs([inw, usz]).unwrap() + let [usz] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); + pf2.finish_with_outputs([inw, usz])? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not let [a, u] = pf1 - .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®) - .unwrap() + .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®)? .outputs_arr(); let [u1, u2] = pf1 - .call(pf2.handle(), &[usize_t().into()], [u], ®) - .unwrap() + .call(pf2.handle(), &[usize_t().into()], [u], ®)? .outputs_arr(); - let pf1 = pf1.finish_with_outputs([a, u1, u2]).unwrap(); + let pf1 = pf1.finish_with_outputs([a, u1, u2])?; // Outer: two calls to pf1 with different TypeArgs let [_, u, _] = outer - .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®) - .unwrap() + .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®)? .outputs_arr(); let [_, u, _] = outer - .call(pf1.handle(), &[usize_t().into()], [u], ®) - .unwrap() + .call(pf1.handle(), &[usize_t().into()], [u], ®)? .outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([u], ®).unwrap(); + let hugr = outer.finish_hugr_with_outputs([u], ®)?; let mono_hugr = monomorphize(hugr, ®); - mono_hugr.validate(®).unwrap(); + mono_hugr.validate(®)?; let funcs = mono_hugr .nodes() .filter_map(|n| mono_hugr.get_optype(n).as_func_defn().map(|fd| (n, fd))) @@ -454,6 +406,7 @@ mod test { assert!(fd.signature.params().is_empty()); assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); } + Ok(()) } #[test] From 9fc327146c61706f1726efd92237355a019299c5 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 2 Dec 2024 21:24:38 +0000 Subject: [PATCH 29/63] clippy --- hugr-core/src/hugr/monomorphize.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 1751ccc86..cc7bdf0b7 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -313,7 +313,7 @@ mod test { for n in expected_mangled_names.iter() { let mono_fn = funcs.remove(n).unwrap(); - assert!((*mono_fn).signature.params().is_empty()); + assert!(mono_fn.signature.params().is_empty()); } assert_eq!( @@ -329,7 +329,7 @@ mod test { .filter_map(|n| nopoly.get_optype(n).as_func_defn().map(|fd| (&fd.name, fd))) .collect::>(); - assert!(funcs.values().all(|fd| (*fd).signature.params().is_empty())); + assert!(funcs.values().all(|fd| fd.signature.params().is_empty())); for n in expected_mangled_names { assert!(funcs.remove(&n).is_some()); } From 9f0b9061005459e6fd8cc7cdf57449fe6e662f0f Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 3 Dec 2024 12:44:02 +0000 Subject: [PATCH 30/63] Make test_module also test recursion --- hugr-core/src/hugr/monomorphize.rs | 45 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index cc7bdf0b7..043ab0815 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -211,8 +211,8 @@ mod test { use crate::builder::test::simple_dfg_hugr; use crate::builder::{ - BuildHandle, Container, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, - HugrBuilder, ModuleBuilder, + Container, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, + ModuleBuilder, }; use crate::extension::prelude::{usize_t, ConstUsize, UnpackTuple}; use crate::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; @@ -239,25 +239,31 @@ mod test { Type::new_tuple(vec![ty.clone(), ty.clone(), ty]) } - fn add_double(ctr: &mut impl Container) -> BuildHandle> { - let elem_ty = Type::new_var_use(0, TypeBound::Copyable); - let pfty = PolyFuncType::new( - [TypeBound::Copyable.into()], - Signature::new(elem_ty.clone(), pair_type(elem_ty.clone())), - ); - let mut fb = ctr.define_function("double", pfty).unwrap(); - let [elem] = fb.input_wires_arr(); - let tag = Tag::new(0, vec![vec![elem_ty; 2].into()]); - let tag = fb.add_dataflow_op(tag, [elem, elem]).unwrap(); - fb.finish_with_outputs(tag.outputs()).unwrap() - } - #[test] fn test_module() -> Result<(), Box> { + let tv0 = || Type::new_var_use(0, TypeBound::Copyable); let mut mb = ModuleBuilder::new(); - let db = add_double(&mut mb); + let db = { + let pfty = PolyFuncType::new( + [TypeBound::Copyable.into()], + Signature::new(tv0(), pair_type(tv0())), + ); + let mut fb = mb.define_function("double", pfty)?; + let [elem] = fb.input_wires_arr(); + // A "genuine" impl might: + // let tag = Tag::new(0, vec![vec![elem_ty; 2].into()]); + // let tag = fb.add_dataflow_op(tag, [elem, elem]).unwrap(); + // ...but since this will never execute, we can test recursion here + let tag = fb.call( + &FuncID::::from(fb.container_node()), + &[tv0().into()], + [elem], + &PRELUDE_REGISTRY, + )?; + fb.finish_with_outputs(tag.outputs())? + }; + let tr = { - let tv0 = || Type::new_var_use(0, TypeBound::Copyable); let pfty = PolyFuncType::new( [TypeBound::Copyable.into()], Signature::new(tv0(), Type::new_tuple(vec![tv0(); 3])), @@ -414,9 +420,4 @@ mod test { //monof2 contains polyf3 (and instantiates) - not moved //polyf4 contains polyf5 but not instantiated -> not moved } - - #[test] - fn test_recursive() { - // make map, - } } From 1b7f9ee4e9210950817047432b6826e0f7234292 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 3 Dec 2024 12:47:42 +0000 Subject: [PATCH 31/63] fix doclink --- hugr-core/src/ops.rs | 2 +- hugr-core/src/ops/dataflow.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index ff70fad09..c1f06bace 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -407,7 +407,7 @@ pub trait OpTrait: Sized { } /// Apply a type-level substitution to this OpType, i.e. replace - /// [type variables](Type::Variable) with new types. + /// [type variables](crate::types::TypeArg::new_var_use) with new types. fn substitute(self, _subst: &Substitution) -> Self { self } diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index 778ac5eb5..98208a53d 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -51,7 +51,7 @@ pub trait DataflowOpTrait: Sized { } /// Apply a type-level substitution to this OpType, i.e. replace - /// [type variables](Type::Variable) with new types. + /// [type variables](TypeArg::new_var_use) with new types. fn substitute(self, _subst: &Substitution) -> Self; } From 8a5218510b21a220090bcb3584f99d93d2c8dc0a Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 3 Dec 2024 16:46:14 +0000 Subject: [PATCH 32/63] helper list_funcs --- hugr-core/src/hugr/monomorphize.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index 043ab0815..e2e3866b4 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -218,10 +218,10 @@ mod test { use crate::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; use crate::hugr::monomorphize::mangle_inner_func; use crate::ops::handle::FuncID; - use crate::ops::Tag; + use crate::ops::{FuncDefn, Tag}; use crate::std_extensions::arithmetic::int_types::{self, INT_TYPES}; use crate::types::{PolyFuncType, Signature, Type, TypeBound}; - use crate::{type_row, Hugr, HugrView}; + use crate::{type_row, Hugr, HugrView, Node}; use super::{mangle_name, monomorphize, remove_polyfuncs}; @@ -330,12 +330,11 @@ mod test { assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent let nopoly = remove_polyfuncs(mono); - let mut funcs = nopoly - .nodes() - .filter_map(|n| nopoly.get_optype(n).as_func_defn().map(|fd| (&fd.name, fd))) - .collect::>(); + let mut funcs = list_funcs(&nopoly); - assert!(funcs.values().all(|fd| fd.signature.params().is_empty())); + assert!(funcs + .values() + .all(|(_, fd)| fd.signature.params().is_empty())); for n in expected_mangled_names { assert!(funcs.remove(&n).is_some()); } @@ -389,13 +388,10 @@ mod test { let mono_hugr = monomorphize(hugr, ®); mono_hugr.validate(®)?; - let funcs = mono_hugr - .nodes() - .filter_map(|n| mono_hugr.get_optype(n).as_func_defn().map(|fd| (n, fd))) - .collect_vec(); + let funcs = list_funcs(&mono_hugr); let pf2_name = mangle_inner_func("pf1", "pf2"); assert_eq!( - funcs.iter().map(|(_, fd)| &fd.name).sorted().collect_vec(), + funcs.keys().copied().sorted().collect_vec(), vec![ &mangle_name("pf1", &[ity().into()]), &mangle_name("pf1", &[usize_t().into()]), @@ -408,13 +404,19 @@ mod test { .sorted() .collect_vec() ); - for (n, fd) in funcs { + for (n, fd) in funcs.into_values() { assert!(fd.signature.params().is_empty()); assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); } Ok(()) } + fn list_funcs(h: &Hugr) -> HashMap<&String, (Node, &FuncDefn)> { + h.nodes() + .filter_map(|n| h.get_optype(n).as_func_defn().map(|fd| (&fd.name, (n, fd)))) + .collect::>() + } + #[test] fn test_not_flattened() { //monof2 contains polyf3 (and instantiates) - not moved From 5d54ee361cb920f9fbf12fe565c112047d87cc57 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 3 Dec 2024 16:52:21 +0000 Subject: [PATCH 33/63] Test part-flattening --- hugr-core/src/hugr/monomorphize.rs | 49 ++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index e2e3866b4..c31ded38b 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -211,13 +211,13 @@ mod test { use crate::builder::test::simple_dfg_hugr; use crate::builder::{ - Container, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, - ModuleBuilder, + Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, + HugrBuilder, ModuleBuilder, }; use crate::extension::prelude::{usize_t, ConstUsize, UnpackTuple}; use crate::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; use crate::hugr::monomorphize::mangle_inner_func; - use crate::ops::handle::FuncID; + use crate::ops::handle::{FuncID, NodeHandle}; use crate::ops::{FuncDefn, Tag}; use crate::std_extensions::arithmetic::int_types::{self, INT_TYPES}; use crate::types::{PolyFuncType, Signature, Type, TypeBound}; @@ -418,8 +418,45 @@ mod test { } #[test] - fn test_not_flattened() { - //monof2 contains polyf3 (and instantiates) - not moved - //polyf4 contains polyf5 but not instantiated -> not moved + fn test_no_flatten_out_of_mono_func() -> Result<(), Box> { + let ity = || INT_TYPES[4].clone(); + let reg = + ExtensionRegistry::try_new([PRELUDE.to_owned(), int_types::EXTENSION.to_owned()])?; + let sig = Signature::new_endo(vec![usize_t(), ity()]); + let mut dfg = DFGBuilder::new(sig.clone())?; + let mut mono = dfg.define_function("id2", sig)?; + let pf = mono.define_function( + "id", + PolyFuncType::new( + [TypeBound::Any.into()], + Signature::new_endo(Type::new_var_use(0, TypeBound::Any)), + ), + )?; + let outs = pf.input_wires(); + let pf = pf.finish_with_outputs(outs)?; + let [a, b] = mono.input_wires_arr(); + let [a] = mono + .call(pf.handle(), &[usize_t().into()], [a], ®)? + .outputs_arr(); + let [b] = mono + .call(pf.handle(), &[ity().into()], [b], ®)? + .outputs_arr(); + let mono = mono.finish_with_outputs([a, b])?; + let c = dfg.call(mono.handle(), &[], dfg.input_wires(), ®)?; + let hugr = dfg.finish_hugr_with_outputs(c.outputs(), ®)?; + let mono_hugr = monomorphize(hugr, ®); + + let mut funcs = list_funcs(&mono_hugr); + assert!(funcs + .values() + .all(|(_, fd)| fd.signature.params().is_empty())); + let (m, _) = funcs.remove(&"id2".to_string()).unwrap(); + assert_eq!(m, mono.handle().node()); + assert_eq!(mono_hugr.get_parent(m), Some(mono_hugr.root())); + for t in [usize_t(), ity()] { + let (n, _) = funcs.remove(&mangle_name("id", &[t.into()])).unwrap(); + assert_eq!(mono_hugr.get_parent(n), Some(m)); // Not lifted to top + } + Ok(()) } } From bbcda39299d8573f901e9a270593e46454b78c97 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Tue, 3 Dec 2024 17:10:27 +0000 Subject: [PATCH 34/63] fix tests for all-features --- hugr-core/src/hugr/monomorphize.rs | 44 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index c31ded38b..a3844020c 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -214,14 +214,14 @@ mod test { Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, }; - use crate::extension::prelude::{usize_t, ConstUsize, UnpackTuple}; + use crate::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; use crate::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; use crate::hugr::monomorphize::mangle_inner_func; use crate::ops::handle::{FuncID, NodeHandle}; use crate::ops::{FuncDefn, Tag}; use crate::std_extensions::arithmetic::int_types::{self, INT_TYPES}; - use crate::types::{PolyFuncType, Signature, Type, TypeBound}; - use crate::{type_row, Hugr, HugrView, Node}; + use crate::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; + use crate::{Hugr, HugrView, Node}; use super::{mangle_name, monomorphize, remove_polyfuncs}; @@ -239,6 +239,10 @@ mod test { Type::new_tuple(vec![ty.clone(), ty.clone(), ty]) } + fn prelusig(ins: impl Into, outs: impl Into) -> Signature { + Signature::new(ins, outs).with_extension_delta(PRELUDE_ID) + } + #[test] fn test_module() -> Result<(), Box> { let tv0 = || Type::new_var_use(0, TypeBound::Copyable); @@ -258,17 +262,17 @@ mod test { &FuncID::::from(fb.container_node()), &[tv0().into()], [elem], - &PRELUDE_REGISTRY, + &EMPTY_REG, )?; fb.finish_with_outputs(tag.outputs())? }; let tr = { - let pfty = PolyFuncType::new( - [TypeBound::Copyable.into()], - Signature::new(tv0(), Type::new_tuple(vec![tv0(); 3])), - ); - let mut fb = mb.define_function("triple", pfty)?; + let sig = prelusig(tv0(), Type::new_tuple(vec![tv0(); 3])); + let mut fb = mb.define_function( + "triple", + PolyFuncType::new([TypeBound::Copyable.into()], sig), + )?; let [elem] = fb.input_wires_arr(); let pair = fb.call(db.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY)?; @@ -280,11 +284,8 @@ mod test { fb.finish_with_outputs(trip.outputs())? }; { - let sig = Signature::new( - usize_t(), - vec![triple_type(usize_t()), triple_type(pair_type(usize_t()))], - ); - let mut fb = mb.define_function("main", sig)?; + let outs = vec![triple_type(usize_t()), triple_type(pair_type(usize_t()))]; + let mut fb = mb.define_function("main", prelusig(usize_t(), outs))?; let [elem] = fb.input_wires_arr(); let [res1] = fb .call(tr.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)? @@ -349,18 +350,19 @@ mod test { let reg = ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; let tv0 = || Type::new_var_use(0, TypeBound::Any); - let pf_any = |sig: Signature| PolyFuncType::new([TypeBound::Any.into()], sig); let ity = || INT_TYPES[3].clone(); - let mut outer = FunctionBuilder::new("mainish", Signature::new(ity(), usize_t()))?; - let sig = pf_any(Signature::new(tv0(), vec![tv0(), usize_t(), usize_t()])); - let mut pf1 = outer.define_function("pf1", sig)?; + let pf_any = |ins, outs| PolyFuncType::new([TypeBound::Any.into()], prelusig(ins, outs)); + + let mut outer = FunctionBuilder::new("mainish", prelusig(ity(), usize_t()))?; + + let pfty = pf_any(vec![tv0()], vec![tv0(), usize_t(), usize_t()]); + let mut pf1 = outer.define_function("pf1", pfty)?; - let sig = pf_any(Signature::new(tv0(), vec![tv0(), usize_t()])); - let mut pf2 = pf1.define_function("pf2", sig)?; + let mut pf2 = pf1.define_function("pf2", pf_any(vec![tv0()], vec![tv0(), usize_t()]))?; let mono_func = { - let mut fb = pf2.define_function("get_usz", Signature::new(type_row![], usize_t()))?; + let mut fb = pf2.define_function("get_usz", prelusig(vec![], usize_t()))?; let cst0 = fb.add_load_value(ConstUsize::new(1)); fb.finish_with_outputs([cst0])? }; From a26fa49915f143458cc0317475c83d117eccbee6 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 4 Dec 2024 08:55:36 +0000 Subject: [PATCH 35/63] silence clippy --- hugr-core/src/hugr/monomorphize.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index a3844020c..efb915a3a 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -452,6 +452,7 @@ mod test { assert!(funcs .values() .all(|(_, fd)| fd.signature.params().is_empty())); + #[allow(clippy::unnecessary_to_owned)] // It is necessary let (m, _) = funcs.remove(&"id2".to_string()).unwrap(); assert_eq!(m, mono.handle().node()); assert_eq!(mono_hugr.get_parent(m), Some(mono_hugr.root())); From 275b155729401435a748eeb4052b9f4cd2ebc00e Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 4 Dec 2024 09:01:00 +0000 Subject: [PATCH 36/63] common up is_polymorphic --- hugr-core/src/hugr/monomorphize.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-core/src/hugr/monomorphize.rs index efb915a3a..ac3747703 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-core/src/hugr/monomorphize.rs @@ -47,9 +47,12 @@ pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { h } +fn is_polymorphic(fd: &FuncDefn) -> bool { + !fd.signature.params().is_empty() +} + fn is_polymorphic_funcdefn(t: &OpType) -> bool { - t.as_func_defn() - .is_some_and(|fd| !fd.signature.params().is_empty()) + t.as_func_defn().is_some_and(is_polymorphic) } struct Instantiating<'a> { @@ -223,7 +226,7 @@ mod test { use crate::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; use crate::{Hugr, HugrView, Node}; - use super::{mangle_name, monomorphize, remove_polyfuncs}; + use super::{is_polymorphic, mangle_name, monomorphize, remove_polyfuncs}; #[rstest] fn test_null(simple_dfg_hugr: Hugr) { @@ -319,8 +322,7 @@ mod test { ]; for n in expected_mangled_names.iter() { - let mono_fn = funcs.remove(n).unwrap(); - assert!(mono_fn.signature.params().is_empty()); + assert!(!is_polymorphic(funcs.remove(n).unwrap())); } assert_eq!( @@ -333,9 +335,7 @@ mod test { let nopoly = remove_polyfuncs(mono); let mut funcs = list_funcs(&nopoly); - assert!(funcs - .values() - .all(|(_, fd)| fd.signature.params().is_empty())); + assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); for n in expected_mangled_names { assert!(funcs.remove(&n).is_some()); } @@ -407,7 +407,7 @@ mod test { .collect_vec() ); for (n, fd) in funcs.into_values() { - assert!(fd.signature.params().is_empty()); + assert!(!is_polymorphic(fd)); assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); } Ok(()) @@ -449,9 +449,7 @@ mod test { let mono_hugr = monomorphize(hugr, ®); let mut funcs = list_funcs(&mono_hugr); - assert!(funcs - .values() - .all(|(_, fd)| fd.signature.params().is_empty())); + assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); #[allow(clippy::unnecessary_to_owned)] // It is necessary let (m, _) = funcs.remove(&"id2".to_string()).unwrap(); assert_eq!(m, mono.handle().node()); From f7a6dca1918baed839aaa3951cb7afea7ac3814a Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 4 Dec 2024 14:50:16 +0000 Subject: [PATCH 37/63] Move monomorphize.rs into hugr-passes, pub Substitution::new --- hugr-core/src/hugr.rs | 3 - hugr-core/src/types.rs | 8 ++- hugr-passes/src/lib.rs | 2 + .../hugr => hugr-passes/src}/monomorphize.rs | 57 ++++++++++--------- 4 files changed, 40 insertions(+), 30 deletions(-) rename {hugr-core/src/hugr => hugr-passes/src}/monomorphize.rs (91%) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index dc8321f2f..b42622745 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -4,7 +4,6 @@ pub mod hugrmut; pub(crate) mod ident; pub mod internal; -mod monomorphize; pub mod rewrite; pub mod serialize; pub mod validate; @@ -33,8 +32,6 @@ use crate::ops::{OpTag, OpTrait}; pub use crate::ops::{OpType, DEFAULT_OPTYPE}; use crate::{Direction, Node}; -pub use monomorphize::{monomorphize, remove_polyfuncs}; - /// The Hugr data structure. #[derive(Clone, Debug, PartialEq)] pub struct Hugr { diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index a8cd3d9ba..b8e3a96e4 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -547,7 +547,13 @@ impl From for TypeRV { pub struct Substitution<'a>(&'a [TypeArg], &'a ExtensionRegistry); impl<'a> Substitution<'a> { - pub(crate) fn new(items: &'a [TypeArg], exts: &'a ExtensionRegistry) -> Self { + /// Create a new Substitution given the replacement values (indexed + /// as the variables they replace). `exts` must contain the [TypeDef] + /// for every custom [Type] (to which the Substitution is applied) + /// containing a type-variable. + /// + /// [TypeDef]: crate::extension::TypeDef + pub fn new(items: &'a [TypeArg], exts: &'a ExtensionRegistry) -> Self { Self(items, exts) } diff --git a/hugr-passes/src/lib.rs b/hugr-passes/src/lib.rs index 9042850d4..030e1b5b1 100644 --- a/hugr-passes/src/lib.rs +++ b/hugr-passes/src/lib.rs @@ -5,6 +5,8 @@ pub mod force_order; mod half_node; pub mod lower; pub mod merge_bbs; +mod monomorphize; +pub use monomorphize::{monomorphize, remove_polyfuncs}; pub mod nest_cfgs; pub mod non_local; pub mod validation; diff --git a/hugr-core/src/hugr/monomorphize.rs b/hugr-passes/src/monomorphize.rs similarity index 91% rename from hugr-core/src/hugr/monomorphize.rs rename to hugr-passes/src/monomorphize.rs index ac3747703..7eda5d180 100644 --- a/hugr-core/src/hugr/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -1,13 +1,13 @@ use std::collections::{hash_map::Entry, HashMap}; -use crate::{ +use hugr_core::{ extension::ExtensionRegistry, ops::{Call, FuncDefn, OpTrait}, types::{Signature, Substitution, TypeArg}, Node, }; -use super::{internal::HugrMutInternals, Hugr, HugrMut, HugrView, OpType}; +use hugr_core::hugr::{hugrmut::HugrMut, internal::HugrMutInternals, Hugr, HugrView, OpType}; /// Replaces calls to polymorphic functions with calls to new monomorphic /// instantiations of the polymorphic ones. @@ -114,8 +114,8 @@ fn mono_scan( h.disconnect(ch, fn_inp); // No-op if copying+substituting h.connect(new_tgt, fn_out, ch, fn_inp); - *h.op_types.get_mut(ch.pg_index()) = - Call::try_new(mono_sig.into(), vec![], reg).unwrap().into(); + h.replace_op(ch, Call::try_new(mono_sig.into(), vec![], reg).unwrap()) + .unwrap(); } } @@ -132,8 +132,12 @@ fn instantiate( let outer_name = h.get_optype(poly_func).as_func_defn().unwrap().name.clone(); let mut to_scan = Vec::from_iter(h.children(poly_func)); while let Some(n) = to_scan.pop() { - if let OpType::FuncDefn(fd) = h.op_types.get_mut(n.pg_index()) { - fd.name = mangle_inner_func(&outer_name, &fd.name); + if let OpType::FuncDefn(fd) = h.get_optype(n) { + let fd = FuncDefn { + name: mangle_inner_func(&outer_name, &fd.name), + signature: fd.signature.clone(), + }; + h.replace_op(n, fd).unwrap(); h.move_after_sibling(n, poly_func); } else { to_scan.extend(h.children(n)) @@ -210,29 +214,20 @@ mod test { use std::collections::HashMap; use itertools::Itertools; - use rstest::rstest; - use crate::builder::test::simple_dfg_hugr; - use crate::builder::{ + use hugr_core::builder::{ Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, }; - use crate::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; - use crate::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; - use crate::hugr::monomorphize::mangle_inner_func; - use crate::ops::handle::{FuncID, NodeHandle}; - use crate::ops::{FuncDefn, Tag}; - use crate::std_extensions::arithmetic::int_types::{self, INT_TYPES}; - use crate::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; - use crate::{Hugr, HugrView, Node}; - - use super::{is_polymorphic, mangle_name, monomorphize, remove_polyfuncs}; - - #[rstest] - fn test_null(simple_dfg_hugr: Hugr) { - let mono = monomorphize(simple_dfg_hugr.clone(), &EMPTY_REG); - assert_eq!(simple_dfg_hugr, mono); - } + use hugr_core::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; + use hugr_core::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; + use hugr_core::ops::handle::{FuncID, NodeHandle}; + use hugr_core::ops::{FuncDefn, Tag}; + use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; + use hugr_core::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; + use hugr_core::{Hugr, HugrView, Node}; + + use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize, remove_polyfuncs}; fn pair_type(ty: Type) -> Type { Type::new_tuple(vec![ty.clone(), ty]) @@ -246,6 +241,16 @@ mod test { Signature::new(ins, outs).with_extension_delta(PRELUDE_ID) } + #[test] + fn test_null() { + let dfg_builder = + DFGBuilder::new(Signature::new(vec![usize_t()], vec![usize_t()])).unwrap(); + let [i1] = dfg_builder.input_wires_arr(); + let hugr = dfg_builder.finish_prelude_hugr_with_outputs([i1]).unwrap(); + let hugr2 = monomorphize(hugr.clone(), &PRELUDE_REGISTRY); + assert_eq!(hugr, hugr2); + } + #[test] fn test_module() -> Result<(), Box> { let tv0 = || Type::new_var_use(0, TypeBound::Copyable); @@ -280,7 +285,7 @@ mod test { let pair = fb.call(db.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY)?; let [elem1, elem2] = fb - .add_dataflow_op(UnpackTuple(vec![tv0(); 2].into()), pair.outputs())? + .add_dataflow_op(UnpackTuple::new(vec![tv0(); 2].into()), pair.outputs())? .outputs_arr(); let tag = Tag::new(0, vec![vec![tv0(); 3].into()]); let trip = fb.add_dataflow_op(tag, [elem1, elem2, elem])?; From 3e755d1813a701ee635481b63df95f9d6d5dfbd1 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Wed, 4 Dec 2024 16:28:40 +0000 Subject: [PATCH 38/63] substitute(self) -> substitute(&self) calling subst_mut(&mut self) --- hugr-core/src/ops.rs | 16 +++++-- hugr-core/src/ops/controlflow.rs | 64 ++++++++++--------------- hugr-core/src/ops/custom.rs | 39 ++++++---------- hugr-core/src/ops/dataflow.rs | 80 +++++++++++--------------------- hugr-core/src/ops/sum.rs | 13 ++---- hugr-passes/src/monomorphize.rs | 2 +- 6 files changed, 84 insertions(+), 130 deletions(-) diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index a456e51a9..94b0c962b 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -350,7 +350,7 @@ pub trait StaticTag { #[enum_dispatch] /// Trait implemented by all OpType variants. -pub trait OpTrait: Sized { +pub trait OpTrait: Sized + Clone { /// A human-readable description of the operation. fn description(&self) -> &str; @@ -415,11 +415,19 @@ pub trait OpTrait: Sized { .is_some() as usize } + /// Like [Self::subst_mut] but returns a substituted clone of `self`. + /// The default impl is correct so long as `subst_mut` is, so trait + /// `impl`s should/must override that if the op may contain type variables. + fn substitute(&self, subst: &Substitution) -> Self { + let mut s = self.clone(); + s.subst_mut(subst); + s + } + /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](crate::types::TypeArg::new_var_use) with new types. - fn substitute(self, _subst: &Substitution) -> Self { - self - } + /// The default is appropriate when there are no type (variables) in the op. + fn subst_mut(&mut self, _subst: &Substitution) {} } /// Properties of child graphs of ops, if the op has children. diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 0a139cbd1..5a3c8d071 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -1,7 +1,7 @@ //! Control flow operations. use crate::extension::ExtensionSet; -use crate::types::{EdgeKind, Signature, Type, TypeRow}; +use crate::types::{EdgeKind, Signature, Substitution, Type, TypeRow}; use crate::Direction; use super::dataflow::{DataflowOpTrait, DataflowParent}; @@ -37,13 +37,11 @@ impl DataflowOpTrait for TailLoop { Signature::new(inputs, outputs).with_extension_delta(self.extension_delta.clone()) } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - Self { - just_inputs: self.just_inputs.substitute(subst), - just_outputs: self.just_outputs.substitute(subst), - rest: self.rest.substitute(subst), - extension_delta: self.extension_delta.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.just_inputs = self.just_inputs.substitute(subst); + self.just_outputs = self.just_outputs.substitute(subst); + self.rest = self.rest.substitute(subst); + self.extension_delta = self.extension_delta.substitute(subst); } } @@ -110,17 +108,13 @@ impl DataflowOpTrait for Conditional { .with_extension_delta(self.extension_delta.clone()) } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - Self { - sum_rows: self - .sum_rows - .into_iter() - .map(|r| r.substitute(subst)) - .collect(), - other_inputs: self.other_inputs.substitute(subst), - outputs: self.outputs.substitute(subst), - extension_delta: self.extension_delta.substitute(subst), + fn subst_mut(&mut self, subst: &Substitution) { + for row in self.sum_rows.iter_mut() { + *row = row.substitute(subst) } + self.other_inputs = self.other_inputs.substitute(subst); + self.outputs = self.outputs.substitute(subst); + self.extension_delta = self.extension_delta.substitute(subst); } } @@ -152,10 +146,8 @@ impl DataflowOpTrait for CFG { self.signature.clone() } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - Self { - signature: self.signature.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.signature = self.signature.substitute(subst); } } @@ -238,17 +230,13 @@ impl OpTrait for DataflowBlock { } } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - Self { - inputs: self.inputs.substitute(subst), - other_outputs: self.other_outputs.substitute(subst), - sum_rows: self - .sum_rows - .into_iter() - .map(|r| r.substitute(subst)) - .collect(), - extension_delta: self.extension_delta.substitute(subst), + fn subst_mut(&mut self, subst: &Substitution) { + self.inputs = self.inputs.substitute(subst); + self.other_outputs = self.other_outputs.substitute(subst); + for r in self.sum_rows.iter_mut() { + *r = r.substitute(subst); } + self.extension_delta = self.extension_delta.substitute(subst); } } @@ -276,10 +264,8 @@ impl OpTrait for ExitBlock { } } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - Self { - cfg_outputs: self.cfg_outputs.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.cfg_outputs = self.cfg_outputs.substitute(subst); } } @@ -345,10 +331,8 @@ impl OpTrait for Case { ::TAG } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - Self { - signature: self.signature.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.signature = self.signature.substitute(subst); } } diff --git a/hugr-core/src/ops/custom.rs b/hugr-core/src/ops/custom.rs index 5d32dee5a..7d8feeb8e 100644 --- a/hugr-core/src/ops/custom.rs +++ b/hugr-core/src/ops/custom.rs @@ -11,8 +11,11 @@ use { ::proptest_derive::Arbitrary, }; -use crate::extension::{ConstFoldResult, ExtensionId, ExtensionRegistry, OpDef, SignatureError}; use crate::types::{type_param::TypeArg, Signature}; +use crate::{ + extension::{ConstFoldResult, ExtensionId, ExtensionRegistry, OpDef, SignatureError}, + types::Substitution, +}; use crate::{ops, IncomingPort, Node}; use super::dataflow::DataflowOpTrait; @@ -161,24 +164,17 @@ impl DataflowOpTrait for ExtensionOp { self.signature.clone() } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - let args = self - .args - .into_iter() - .map(|ta| ta.substitute(subst)) - .collect::>(); - let signature = self.signature.substitute(subst); + fn subst_mut(&mut self, subst: &Substitution) { + for ta in self.args.iter_mut() { + *ta = ta.substitute(subst); + } + self.signature = self.signature.substitute(subst); debug_assert_eq!( self.def - .compute_signature(&args, subst.extension_registry()) + .compute_signature(&self.args, subst.extension_registry()) .as_ref(), - Ok(&signature) + Ok(&self.signature) ); - Self { - def: self.def, - args, - signature, - } } } @@ -270,16 +266,11 @@ impl DataflowOpTrait for OpaqueOp { .with_extension_delta(self.extension().clone()) } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - Self { - args: self - .args - .into_iter() - .map(|ta| ta.substitute(subst)) - .collect(), - signature: self.signature.substitute(subst), - ..self + fn subst_mut(&mut self, subst: &Substitution) { + for ta in self.args.iter_mut() { + *ta = ta.substitute(subst); } + self.signature = self.signature.substitute(subst); } } diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index 98208a53d..cc137ab0c 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -11,7 +11,7 @@ use crate::IncomingPort; use ::proptest_derive::Arbitrary; /// Trait implemented by all dataflow operations. -pub trait DataflowOpTrait: Sized { +pub trait DataflowOpTrait: Sized + Clone { /// Tag identifying the operation. const TAG: OpTag; @@ -52,7 +52,7 @@ pub trait DataflowOpTrait: Sized { /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](TypeArg::new_var_use) with new types. - fn substitute(self, _subst: &Substitution) -> Self; + fn subst_mut(&mut self, _subst: &Substitution) {} } /// Helpers to construct input and output nodes @@ -113,10 +113,8 @@ impl DataflowOpTrait for Input { Signature::new(TypeRow::new(), self.types.clone()) } - fn substitute(self, subst: &Substitution) -> Self { - Self { - types: self.types.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.types = self.types.substitute(subst); } } impl DataflowOpTrait for Output { @@ -136,10 +134,8 @@ impl DataflowOpTrait for Output { None } - fn substitute(self, subst: &Substitution) -> Self { - Self { - types: self.types.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.types = self.types.substitute(subst); } } @@ -168,8 +164,8 @@ impl OpTrait for T { DataflowOpTrait::static_input(self) } - fn substitute(self, subst: &crate::types::Substitution) -> Self { - DataflowOpTrait::substitute(self, subst) + fn subst_mut(&mut self, subst: &Substitution) { + DataflowOpTrait::subst_mut(self, subst) } } impl StaticTag for T { @@ -208,24 +204,17 @@ impl DataflowOpTrait for Call { Some(EdgeKind::Function(self.called_function_type().clone())) } - fn substitute(self, subst: &Substitution) -> Self { - let type_args = self - .type_args - .into_iter() - .map(|ta| ta.substitute(subst)) - .collect::>(); - let instantiation = self.instantiation.substitute(subst); + fn subst_mut(&mut self, subst: &Substitution) { + for ta in self.type_args.iter_mut() { + *ta = ta.substitute(subst); + } + self.instantiation = self.instantiation.substitute(subst); debug_assert_eq!( self.func_sig - .instantiate(&type_args, subst.extension_registry()) + .instantiate(&self.type_args, subst.extension_registry()) .as_ref(), - Ok(&instantiation) + Ok(&self.instantiation) ); - Self { - type_args, - instantiation, - func_sig: self.func_sig, - } } } impl Call { @@ -320,10 +309,8 @@ impl DataflowOpTrait for CallIndirect { s } - fn substitute(self, subst: &Substitution) -> Self { - Self { - signature: self.signature.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.signature = self.signature.substitute(subst); } } @@ -335,6 +322,7 @@ pub struct LoadConstant { pub datatype: Type, } impl_op_name!(LoadConstant); +// Constants cannot contain type variables, so no substitution required impl DataflowOpTrait for LoadConstant { const TAG: OpTag = OpTag::LoadConst; @@ -349,11 +337,6 @@ impl DataflowOpTrait for LoadConstant { fn static_input(&self) -> Option { Some(EdgeKind::Const(self.constant_type().clone())) } - - fn substitute(self, _subst: &Substitution) -> Self { - // Constants cannot refer to TypeArgs, so neither can loading them - self - } } impl LoadConstant { @@ -411,24 +394,17 @@ impl DataflowOpTrait for LoadFunction { Some(EdgeKind::Function(self.func_sig.clone())) } - fn substitute(self, subst: &Substitution) -> Self { - let type_args = self - .type_args - .into_iter() - .map(|ta| ta.substitute(subst)) - .collect::>(); - let signature = self.signature.substitute(subst); + fn subst_mut(&mut self, subst: &Substitution) { + for ta in self.type_args.iter_mut() { + *ta = ta.substitute(subst); + } + self.signature = self.signature.substitute(subst); debug_assert_eq!( self.func_sig - .instantiate(&type_args, subst.extension_registry()) + .instantiate(&self.type_args, subst.extension_registry()) .as_ref(), - Ok(&signature) + Ok(&self.signature) ); - Self { - func_sig: self.func_sig, - type_args, - signature, - } } } impl LoadFunction { @@ -520,9 +496,7 @@ impl DataflowOpTrait for DFG { self.inner_signature() } - fn substitute(self, subst: &Substitution) -> Self { - Self { - signature: self.signature.substitute(subst), - } + fn subst_mut(&mut self, subst: &Substitution) { + self.signature = self.signature.substitute(subst) } } diff --git a/hugr-core/src/ops/sum.rs b/hugr-core/src/ops/sum.rs index a4a3f4e62..76d3020a9 100644 --- a/hugr-core/src/ops/sum.rs +++ b/hugr-core/src/ops/sum.rs @@ -2,7 +2,7 @@ use super::dataflow::DataflowOpTrait; use super::{impl_op_name, OpTag}; -use crate::types::{EdgeKind, Signature, Type, TypeRow}; +use crate::types::{EdgeKind, Signature, Substitution, Type, TypeRow}; /// An operation that creates a tagged sum value from one of its variants. #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -53,12 +53,9 @@ impl DataflowOpTrait for Tag { Some(EdgeKind::StateOrder) } - fn substitute(mut self, subst: &crate::types::Substitution) -> Self { - self.variants = self - .variants - .into_iter() - .map(|r| r.substitute(subst)) - .collect(); - self + fn subst_mut(&mut self, subst: &Substitution) { + for r in self.variants.iter_mut() { + *r = r.substitute(subst); + } } } diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 7eda5d180..fbda2c088 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -83,7 +83,7 @@ fn mono_scan( // Perform substitution, and recurse into containers (mono_scan does nothing if no children) let ch = if let Some(ref mut inst) = subst_into { let new_ch = - h.add_node_with_parent(inst.target_container, ch_op.clone().substitute(inst.subst)); + h.add_node_with_parent(inst.target_container, ch_op.substitute(inst.subst)); inst.node_map.insert(old_ch, new_ch); let mut inst = Instantiating { target_container: new_ch, From 41d081222b5e6e37acd71b02ae4c94a77a09fee2 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 6 Dec 2024 11:38:27 +0000 Subject: [PATCH 39/63] Revert subst_mut; substitute takes &self; OpTrait requires Clone --- hugr-core/src/ops.rs | 14 ++---- hugr-core/src/ops/controlflow.rs | 56 ++++++++++++---------- hugr-core/src/ops/custom.rs | 35 ++++++++------ hugr-core/src/ops/dataflow.rs | 82 +++++++++++++++++++++----------- hugr-core/src/ops/sum.rs | 9 ++-- 5 files changed, 114 insertions(+), 82 deletions(-) diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index 94b0c962b..3ef82fc9e 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -415,19 +415,11 @@ pub trait OpTrait: Sized + Clone { .is_some() as usize } - /// Like [Self::subst_mut] but returns a substituted clone of `self`. - /// The default impl is correct so long as `subst_mut` is, so trait - /// `impl`s should/must override that if the op may contain type variables. - fn substitute(&self, subst: &Substitution) -> Self { - let mut s = self.clone(); - s.subst_mut(subst); - s - } - /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](crate::types::TypeArg::new_var_use) with new types. - /// The default is appropriate when there are no type (variables) in the op. - fn subst_mut(&mut self, _subst: &Substitution) {} + fn substitute(&self, _subst: &Substitution) -> Self { + self.clone() + } } /// Properties of child graphs of ops, if the op has children. diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 5a3c8d071..1574a716a 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -1,7 +1,7 @@ //! Control flow operations. use crate::extension::ExtensionSet; -use crate::types::{EdgeKind, Signature, Substitution, Type, TypeRow}; +use crate::types::{EdgeKind, Signature, Type, TypeRow}; use crate::Direction; use super::dataflow::{DataflowOpTrait, DataflowParent}; @@ -37,11 +37,13 @@ impl DataflowOpTrait for TailLoop { Signature::new(inputs, outputs).with_extension_delta(self.extension_delta.clone()) } - fn subst_mut(&mut self, subst: &Substitution) { - self.just_inputs = self.just_inputs.substitute(subst); - self.just_outputs = self.just_outputs.substitute(subst); - self.rest = self.rest.substitute(subst); - self.extension_delta = self.extension_delta.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + just_inputs: self.just_inputs.substitute(subst), + just_outputs: self.just_outputs.substitute(subst), + rest: self.rest.substitute(subst), + extension_delta: self.extension_delta.substitute(subst), + } } } @@ -108,13 +110,13 @@ impl DataflowOpTrait for Conditional { .with_extension_delta(self.extension_delta.clone()) } - fn subst_mut(&mut self, subst: &Substitution) { - for row in self.sum_rows.iter_mut() { - *row = row.substitute(subst) + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(), + other_inputs: self.other_inputs.substitute(subst), + outputs: self.outputs.substitute(subst), + extension_delta: self.extension_delta.substitute(subst), } - self.other_inputs = self.other_inputs.substitute(subst); - self.outputs = self.outputs.substitute(subst); - self.extension_delta = self.extension_delta.substitute(subst); } } @@ -146,8 +148,10 @@ impl DataflowOpTrait for CFG { self.signature.clone() } - fn subst_mut(&mut self, subst: &Substitution) { - self.signature = self.signature.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } } } @@ -230,13 +234,13 @@ impl OpTrait for DataflowBlock { } } - fn subst_mut(&mut self, subst: &Substitution) { - self.inputs = self.inputs.substitute(subst); - self.other_outputs = self.other_outputs.substitute(subst); - for r in self.sum_rows.iter_mut() { - *r = r.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + inputs: self.inputs.substitute(subst), + other_outputs: self.other_outputs.substitute(subst), + sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(), + extension_delta: self.extension_delta.substitute(subst), } - self.extension_delta = self.extension_delta.substitute(subst); } } @@ -264,8 +268,10 @@ impl OpTrait for ExitBlock { } } - fn subst_mut(&mut self, subst: &Substitution) { - self.cfg_outputs = self.cfg_outputs.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + cfg_outputs: self.cfg_outputs.substitute(subst), + } } } @@ -331,8 +337,10 @@ impl OpTrait for Case { ::TAG } - fn subst_mut(&mut self, subst: &Substitution) { - self.signature = self.signature.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } } } diff --git a/hugr-core/src/ops/custom.rs b/hugr-core/src/ops/custom.rs index 7d8feeb8e..fce09b640 100644 --- a/hugr-core/src/ops/custom.rs +++ b/hugr-core/src/ops/custom.rs @@ -11,11 +11,8 @@ use { ::proptest_derive::Arbitrary, }; +use crate::extension::{ConstFoldResult, ExtensionId, ExtensionRegistry, OpDef, SignatureError}; use crate::types::{type_param::TypeArg, Signature}; -use crate::{ - extension::{ConstFoldResult, ExtensionId, ExtensionRegistry, OpDef, SignatureError}, - types::Substitution, -}; use crate::{ops, IncomingPort, Node}; use super::dataflow::DataflowOpTrait; @@ -164,17 +161,24 @@ impl DataflowOpTrait for ExtensionOp { self.signature.clone() } - fn subst_mut(&mut self, subst: &Substitution) { - for ta in self.args.iter_mut() { - *ta = ta.substitute(subst); - } - self.signature = self.signature.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + let args = self + .args + .iter() + .map(|ta| ta.substitute(subst)) + .collect::>(); + let signature = self.signature.substitute(subst); debug_assert_eq!( self.def - .compute_signature(&self.args, subst.extension_registry()) + .compute_signature(&args, subst.extension_registry()) .as_ref(), - Ok(&self.signature) + Ok(&signature) ); + Self { + def: self.def.clone(), + args, + signature, + } } } @@ -266,11 +270,12 @@ impl DataflowOpTrait for OpaqueOp { .with_extension_delta(self.extension().clone()) } - fn subst_mut(&mut self, subst: &Substitution) { - for ta in self.args.iter_mut() { - *ta = ta.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + args: self.args.iter().map(|ta| ta.substitute(subst)).collect(), + signature: self.signature.substitute(subst), + ..self.clone() } - self.signature = self.signature.substitute(subst); } } diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index cc137ab0c..c87b784c6 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -11,7 +11,7 @@ use crate::IncomingPort; use ::proptest_derive::Arbitrary; /// Trait implemented by all dataflow operations. -pub trait DataflowOpTrait: Sized + Clone { +pub trait DataflowOpTrait: Sized { /// Tag identifying the operation. const TAG: OpTag; @@ -52,7 +52,7 @@ pub trait DataflowOpTrait: Sized + Clone { /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](TypeArg::new_var_use) with new types. - fn subst_mut(&mut self, _subst: &Substitution) {} + fn substitute(&self, _subst: &Substitution) -> Self; } /// Helpers to construct input and output nodes @@ -113,8 +113,10 @@ impl DataflowOpTrait for Input { Signature::new(TypeRow::new(), self.types.clone()) } - fn subst_mut(&mut self, subst: &Substitution) { - self.types = self.types.substitute(subst); + fn substitute(&self, subst: &Substitution) -> Self { + Self { + types: self.types.substitute(subst), + } } } impl DataflowOpTrait for Output { @@ -134,12 +136,14 @@ impl DataflowOpTrait for Output { None } - fn subst_mut(&mut self, subst: &Substitution) { - self.types = self.types.substitute(subst); + fn substitute(&self, subst: &Substitution) -> Self { + Self { + types: self.types.substitute(subst), + } } } -impl OpTrait for T { +impl OpTrait for T { fn description(&self) -> &str { DataflowOpTrait::description(self) } @@ -164,8 +168,8 @@ impl OpTrait for T { DataflowOpTrait::static_input(self) } - fn subst_mut(&mut self, subst: &Substitution) { - DataflowOpTrait::subst_mut(self, subst) + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + DataflowOpTrait::substitute(self, subst) } } impl StaticTag for T { @@ -204,17 +208,24 @@ impl DataflowOpTrait for Call { Some(EdgeKind::Function(self.called_function_type().clone())) } - fn subst_mut(&mut self, subst: &Substitution) { - for ta in self.type_args.iter_mut() { - *ta = ta.substitute(subst); - } - self.instantiation = self.instantiation.substitute(subst); + fn substitute(&self, subst: &Substitution) -> Self { + let type_args = self + .type_args + .iter() + .map(|ta| ta.substitute(subst)) + .collect::>(); + let instantiation = self.instantiation.substitute(subst); debug_assert_eq!( self.func_sig - .instantiate(&self.type_args, subst.extension_registry()) + .instantiate(&type_args, subst.extension_registry()) .as_ref(), - Ok(&self.instantiation) + Ok(&instantiation) ); + Self { + type_args, + instantiation, + func_sig: self.func_sig.clone(), + } } } impl Call { @@ -309,8 +320,10 @@ impl DataflowOpTrait for CallIndirect { s } - fn subst_mut(&mut self, subst: &Substitution) { - self.signature = self.signature.substitute(subst); + fn substitute(&self, subst: &Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } } } @@ -322,7 +335,6 @@ pub struct LoadConstant { pub datatype: Type, } impl_op_name!(LoadConstant); -// Constants cannot contain type variables, so no substitution required impl DataflowOpTrait for LoadConstant { const TAG: OpTag = OpTag::LoadConst; @@ -337,6 +349,11 @@ impl DataflowOpTrait for LoadConstant { fn static_input(&self) -> Option { Some(EdgeKind::Const(self.constant_type().clone())) } + + fn substitute(&self, _subst: &Substitution) -> Self { + // Constants cannot refer to TypeArgs, so neither can loading them + self.clone() + } } impl LoadConstant { @@ -394,17 +411,24 @@ impl DataflowOpTrait for LoadFunction { Some(EdgeKind::Function(self.func_sig.clone())) } - fn subst_mut(&mut self, subst: &Substitution) { - for ta in self.type_args.iter_mut() { - *ta = ta.substitute(subst); - } - self.signature = self.signature.substitute(subst); + fn substitute(&self, subst: &Substitution) -> Self { + let type_args = self + .type_args + .iter() + .map(|ta| ta.substitute(subst)) + .collect::>(); + let signature = self.signature.substitute(subst); debug_assert_eq!( self.func_sig - .instantiate(&self.type_args, subst.extension_registry()) + .instantiate(&type_args, subst.extension_registry()) .as_ref(), - Ok(&self.signature) + Ok(&signature) ); + Self { + func_sig: self.func_sig.clone(), + type_args, + signature, + } } } impl LoadFunction { @@ -496,7 +520,9 @@ impl DataflowOpTrait for DFG { self.inner_signature() } - fn subst_mut(&mut self, subst: &Substitution) { - self.signature = self.signature.substitute(subst) + fn substitute(&self, subst: &Substitution) -> Self { + Self { + signature: self.signature.substitute(subst), + } } } diff --git a/hugr-core/src/ops/sum.rs b/hugr-core/src/ops/sum.rs index 76d3020a9..171c55614 100644 --- a/hugr-core/src/ops/sum.rs +++ b/hugr-core/src/ops/sum.rs @@ -2,7 +2,7 @@ use super::dataflow::DataflowOpTrait; use super::{impl_op_name, OpTag}; -use crate::types::{EdgeKind, Signature, Substitution, Type, TypeRow}; +use crate::types::{EdgeKind, Signature, Type, TypeRow}; /// An operation that creates a tagged sum value from one of its variants. #[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] @@ -53,9 +53,10 @@ impl DataflowOpTrait for Tag { Some(EdgeKind::StateOrder) } - fn subst_mut(&mut self, subst: &Substitution) { - for r in self.variants.iter_mut() { - *r = r.substitute(subst); + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + Self { + variants: self.variants.iter().map(|r| r.substitute(subst)).collect(), + tag: self.tag, } } } From d4ab4aa651538566c21dfb1faadbea9c861da0ca Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 9 Dec 2024 17:21:05 +0000 Subject: [PATCH 40/63] can't monomorphize array ops polymorphic in size because can't define them --- hugr-passes/src/monomorphize.rs | 59 ++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index fbda2c088..687ddaa11 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -213,18 +213,20 @@ fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { mod test { use std::collections::HashMap; + use hugr_core::extension::simple_op::HasConcrete; + use hugr_core::types::type_param::TypeParam; use itertools::Itertools; use hugr_core::builder::{ Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, }; - use hugr_core::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; + use hugr_core::extension::prelude::{array_type, usize_t, ArrayOp, ArrayOpDef, ConstUsize, UnpackTuple, PRELUDE_ID}; use hugr_core::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; use hugr_core::ops::handle::{FuncID, NodeHandle}; use hugr_core::ops::{FuncDefn, Tag}; use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; - use hugr_core::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; + use hugr_core::types::{PolyFuncType, Signature, Type, TypeArg, TypeBound, TypeRow}; use hugr_core::{Hugr, HugrView, Node}; use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize, remove_polyfuncs}; @@ -354,46 +356,49 @@ mod test { let reg = ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; - let tv0 = || Type::new_var_use(0, TypeBound::Any); - let ity = || INT_TYPES[3].clone(); + let tv = |i| Type::new_var_use(i, TypeBound::Copyable); + let sv = |i| TypeArg::new_var_use(i, TypeParam::max_nat()); + let sa = |n| TypeArg::BoundedNat { n }; - let pf_any = |ins, outs| PolyFuncType::new([TypeBound::Any.into()], prelusig(ins, outs)); + let n: u64 = 5; + let mut outer = FunctionBuilder::new("mainish", prelusig( + array_type(sa(n), array_type(sa(2), usize_t())), + vec![usize_t(); 2]))?; - let mut outer = FunctionBuilder::new("mainish", prelusig(ity(), usize_t()))?; + let arr2u = array_type(sa(2), usize_t()); + let pf1t = PolyFuncType::new([TypeParam::max_nat()], + prelusig(array_type(sv(0), arr2u.clone()),usize_t())); + let mut pf1 = outer.define_function("pf1", pf1t)?; - let pfty = pf_any(vec![tv0()], vec![tv0(), usize_t(), usize_t()]); - let mut pf1 = outer.define_function("pf1", pfty)?; - - let mut pf2 = pf1.define_function("pf2", pf_any(vec![tv0()], vec![tv0(), usize_t()]))?; + let pf2t = PolyFuncType::new([TypeParam::max_nat(), TypeBound::Copyable.into()], + prelusig(vec![array_type(sv(0), tv(1))], vec![tv(1)])); + let mut pf2 = pf1.define_function("pf2", pf2t)?; let mono_func = { let mut fb = pf2.define_function("get_usz", prelusig(vec![], usize_t()))?; let cst0 = fb.add_load_value(ConstUsize::new(1)); fb.finish_with_outputs([cst0])? }; + //panic!("ALAN got here"); let pf2 = { let [inw] = pf2.input_wires_arr(); - let [usz] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); - pf2.finish_with_outputs([inw, usz])? + let [idx] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); + let get = pf2.add_dataflow_op(ArrayOpDef::get.instantiate(&[sv(0), tv(1).into()]).unwrap(), [inw, idx])?; + pf2.finish_with_outputs(get.outputs())? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not - let [a, u] = pf1 - .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®)? - .outputs_arr(); - let [u1, u2] = pf1 - .call(pf2.handle(), &[usize_t().into()], [u], ®)? - .outputs_arr(); - let pf1 = pf1.finish_with_outputs([a, u1, u2])?; + let inner = pf1.call(pf2.handle(), &[sv(0), arr2u.into()], pf1.input_wires(), ®)?; + let elem = pf1.call(pf2.handle(), &[TypeArg::BoundedNat { n: 2 }, usize_t().into()], inner.outputs(), ®)?; + let pf1 = pf1.finish_with_outputs(elem.outputs())?; // Outer: two calls to pf1 with different TypeArgs - let [_, u, _] = outer - .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®)? + let [e1] = outer + .call(pf1.handle(), &[sa(n)], outer.input_wires(), ®)? .outputs_arr(); - let [_, u, _] = outer - .call(pf1.handle(), &[usize_t().into()], [u], ®)? - .outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([u], ®)?; + let ar2 = outer.add_dataflow_op(ArrayOpDef::pop_left.to_concrete(usize_t(), n), outer.input_wires())?; + let [e2] = outer.call(pf1.handle(), &[sa(n-1)], ar2.outputs(), ®)?.outputs_arr(); + let hugr = outer.finish_hugr_with_outputs([e1, e2], ®)?; - let mono_hugr = monomorphize(hugr, ®); + /*let mono_hugr = monomorphize(hugr, ®); mono_hugr.validate(®)?; let funcs = list_funcs(&mono_hugr); let pf2_name = mangle_inner_func("pf1", "pf2"); @@ -414,7 +419,7 @@ mod test { for (n, fd) in funcs.into_values() { assert!(!is_polymorphic(fd)); assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); - } + }*/ Ok(()) } From 565a78a8133b729386cd55b08861c7758f3ac7d4 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 9 Dec 2024 18:48:28 +0000 Subject: [PATCH 41/63] Revert "can't monomorphize array ops polymorphic in size because can't define them" This reverts commit d4ab4aa651538566c21dfb1faadbea9c861da0ca. --- hugr-passes/src/monomorphize.rs | 59 +++++++++++++++------------------ 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 687ddaa11..fbda2c088 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -213,20 +213,18 @@ fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { mod test { use std::collections::HashMap; - use hugr_core::extension::simple_op::HasConcrete; - use hugr_core::types::type_param::TypeParam; use itertools::Itertools; use hugr_core::builder::{ Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, }; - use hugr_core::extension::prelude::{array_type, usize_t, ArrayOp, ArrayOpDef, ConstUsize, UnpackTuple, PRELUDE_ID}; + use hugr_core::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; use hugr_core::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; use hugr_core::ops::handle::{FuncID, NodeHandle}; use hugr_core::ops::{FuncDefn, Tag}; use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; - use hugr_core::types::{PolyFuncType, Signature, Type, TypeArg, TypeBound, TypeRow}; + use hugr_core::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; use hugr_core::{Hugr, HugrView, Node}; use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize, remove_polyfuncs}; @@ -356,49 +354,46 @@ mod test { let reg = ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; - let tv = |i| Type::new_var_use(i, TypeBound::Copyable); - let sv = |i| TypeArg::new_var_use(i, TypeParam::max_nat()); - let sa = |n| TypeArg::BoundedNat { n }; + let tv0 = || Type::new_var_use(0, TypeBound::Any); + let ity = || INT_TYPES[3].clone(); - let n: u64 = 5; - let mut outer = FunctionBuilder::new("mainish", prelusig( - array_type(sa(n), array_type(sa(2), usize_t())), - vec![usize_t(); 2]))?; + let pf_any = |ins, outs| PolyFuncType::new([TypeBound::Any.into()], prelusig(ins, outs)); - let arr2u = array_type(sa(2), usize_t()); - let pf1t = PolyFuncType::new([TypeParam::max_nat()], - prelusig(array_type(sv(0), arr2u.clone()),usize_t())); - let mut pf1 = outer.define_function("pf1", pf1t)?; + let mut outer = FunctionBuilder::new("mainish", prelusig(ity(), usize_t()))?; - let pf2t = PolyFuncType::new([TypeParam::max_nat(), TypeBound::Copyable.into()], - prelusig(vec![array_type(sv(0), tv(1))], vec![tv(1)])); - let mut pf2 = pf1.define_function("pf2", pf2t)?; + let pfty = pf_any(vec![tv0()], vec![tv0(), usize_t(), usize_t()]); + let mut pf1 = outer.define_function("pf1", pfty)?; + + let mut pf2 = pf1.define_function("pf2", pf_any(vec![tv0()], vec![tv0(), usize_t()]))?; let mono_func = { let mut fb = pf2.define_function("get_usz", prelusig(vec![], usize_t()))?; let cst0 = fb.add_load_value(ConstUsize::new(1)); fb.finish_with_outputs([cst0])? }; - //panic!("ALAN got here"); let pf2 = { let [inw] = pf2.input_wires_arr(); - let [idx] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); - let get = pf2.add_dataflow_op(ArrayOpDef::get.instantiate(&[sv(0), tv(1).into()]).unwrap(), [inw, idx])?; - pf2.finish_with_outputs(get.outputs())? + let [usz] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); + pf2.finish_with_outputs([inw, usz])? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not - let inner = pf1.call(pf2.handle(), &[sv(0), arr2u.into()], pf1.input_wires(), ®)?; - let elem = pf1.call(pf2.handle(), &[TypeArg::BoundedNat { n: 2 }, usize_t().into()], inner.outputs(), ®)?; - let pf1 = pf1.finish_with_outputs(elem.outputs())?; + let [a, u] = pf1 + .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®)? + .outputs_arr(); + let [u1, u2] = pf1 + .call(pf2.handle(), &[usize_t().into()], [u], ®)? + .outputs_arr(); + let pf1 = pf1.finish_with_outputs([a, u1, u2])?; // Outer: two calls to pf1 with different TypeArgs - let [e1] = outer - .call(pf1.handle(), &[sa(n)], outer.input_wires(), ®)? + let [_, u, _] = outer + .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®)? .outputs_arr(); - let ar2 = outer.add_dataflow_op(ArrayOpDef::pop_left.to_concrete(usize_t(), n), outer.input_wires())?; - let [e2] = outer.call(pf1.handle(), &[sa(n-1)], ar2.outputs(), ®)?.outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([e1, e2], ®)?; + let [_, u, _] = outer + .call(pf1.handle(), &[usize_t().into()], [u], ®)? + .outputs_arr(); + let hugr = outer.finish_hugr_with_outputs([u], ®)?; - /*let mono_hugr = monomorphize(hugr, ®); + let mono_hugr = monomorphize(hugr, ®); mono_hugr.validate(®)?; let funcs = list_funcs(&mono_hugr); let pf2_name = mangle_inner_func("pf1", "pf2"); @@ -419,7 +414,7 @@ mod test { for (n, fd) in funcs.into_values() { assert!(!is_polymorphic(fd)); assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); - }*/ + } Ok(()) } From 134bda4edb89de5a096d09b409b5c5b5f597d9b5 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 9 Dec 2024 19:32:07 +0000 Subject: [PATCH 42/63] A couple of test tidies --- hugr-passes/src/monomorphize.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index fbda2c088..167915692 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -315,10 +315,7 @@ mod test { let mono = monomorphize(hugr, &PRELUDE_REGISTRY); mono.validate(&PRELUDE_REGISTRY)?; - let mut funcs = mono - .nodes() - .filter_map(|n| mono.get_optype(n).as_func_defn().map(|fd| (&fd.name, fd))) - .collect::>(); + let mut funcs = list_funcs(&mono); let expected_mangled_names = [ mangle_name("double", &[usize_t().into()]), mangle_name("triple", &[usize_t().into()]), @@ -327,12 +324,12 @@ mod test { ]; for n in expected_mangled_names.iter() { - assert!(!is_polymorphic(funcs.remove(n).unwrap())); + assert!(!is_polymorphic(funcs.remove(n).unwrap().1)); } assert_eq!( - funcs.keys().sorted().collect_vec(), - ["double", "main", "triple"].iter().collect_vec() + funcs.into_keys().sorted().collect_vec(), + ["double", "main", "triple"] ); assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent From 9f88f971ae4067d34fc0ae38f39cfc1b73c989b8 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 9 Dec 2024 19:39:33 +0000 Subject: [PATCH 43/63] Remove remove_polyfuncs --- hugr-passes/src/lib.rs | 2 +- hugr-passes/src/monomorphize.rs | 100 +++++++++++++++----------------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/hugr-passes/src/lib.rs b/hugr-passes/src/lib.rs index 6c7f5fa06..ea7478b99 100644 --- a/hugr-passes/src/lib.rs +++ b/hugr-passes/src/lib.rs @@ -7,7 +7,7 @@ mod half_node; pub mod lower; pub mod merge_bbs; mod monomorphize; -pub use monomorphize::{monomorphize, remove_polyfuncs}; +pub use monomorphize::monomorphize; pub mod nest_cfgs; pub mod non_local; pub mod validation; diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 167915692..f2036ab87 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -10,39 +10,14 @@ use hugr_core::{ use hugr_core::hugr::{hugrmut::HugrMut, internal::HugrMutInternals, Hugr, HugrView, OpType}; /// Replaces calls to polymorphic functions with calls to new monomorphic -/// instantiations of the polymorphic ones. -/// -/// If the Hugr is [Module](OpType::Module)-rooted, -/// * then the original polymorphic [FuncDefn]s are left untouched (including Calls inside them) -/// - call [remove_polyfuncs] when no other Hugr will be linked in that might instantiate these -/// * else, the originals are removed (they are invisible from outside the Hugr). +/// instantiations of the polymorphic ones. The original polymorphic [FuncDefn]s +/// are left untouched, although with fewer calls (they may still have calls +/// from *other* polymorphic functions still present). pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { let root = h.root(); // 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(&mut h, root, None, &mut HashMap::new(), reg); - if !h.get_optype(root).is_module() { - return remove_polyfuncs(h); - } - } - h -} - -/// Removes any polymorphic [FuncDefn]s from the Hugr. Note that if these have -/// calls from *monomorphic* code, this will make the Hugr invalid (call [monomorphize] -/// first). -pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { - let mut pfs_to_delete = Vec::new(); - let mut to_scan = Vec::from_iter(h.children(h.root())); - while let Some(n) = to_scan.pop() { - if is_polymorphic_funcdefn(h.get_optype(n)) { - pfs_to_delete.push(n) - } else { - to_scan.extend(h.children(n)); - } - } - for n in pfs_to_delete { - h.remove_subtree(n); } h } @@ -227,7 +202,7 @@ mod test { use hugr_core::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; use hugr_core::{Hugr, HugrView, Node}; - use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize, remove_polyfuncs}; + use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize}; fn pair_type(ty: Type) -> Type { Type::new_tuple(vec![ty.clone(), ty]) @@ -312,6 +287,7 @@ mod test { .count(), 3 ); + assert_eq!(hugr.static_targets(db.node()).unwrap().count(), 3); // from double (recursive), triple, main let mono = monomorphize(hugr, &PRELUDE_REGISTRY); mono.validate(&PRELUDE_REGISTRY)?; @@ -332,16 +308,22 @@ mod test { ["double", "main", "triple"] ); - assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent - - let nopoly = remove_polyfuncs(mono); - let mut funcs = list_funcs(&nopoly); + // Original double/triple retained, but call to double from main now lost + assert!(is_polymorphic( + mono.get_optype(db.node()).as_func_defn().unwrap() + )); + assert!(is_polymorphic( + mono.get_optype(tr.node()).as_func_defn().unwrap() + )); + assert_eq!( + mono.static_targets(db.node()) + .unwrap() + .map(|(call, _port)| mono.get_parent(call)) + .collect_vec(), + vec![Some(db.node()), Some(tr.node())] + ); - assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); - for n in expected_mangled_names { - assert!(funcs.remove(&n).is_some()); - } - assert_eq!(funcs.keys().collect_vec(), vec![&"main"]); + assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent Ok(()) } @@ -402,15 +384,23 @@ mod test { &mangle_name(&pf2_name, &[ity().into()]), // from pf1 &mangle_name(&pf2_name, &[usize_t().into()]), // from pf1 and (2*)pf1 &mangle_inner_func(&pf2_name, "get_usz"), - "mainish" + "mainish", + "pf1", + &pf2_name, ] .into_iter() .sorted() .collect_vec() ); for (n, fd) in funcs.into_values() { - assert!(!is_polymorphic(fd)); - assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); + assert_eq!( + is_polymorphic(fd), + ["pf1", &pf2_name].contains(&fd.name.as_str()) + ); + assert_eq!( + mono_hugr.get_parent(n), + (fd.name != "mainish").then_some(mono_hugr.root()) + ); } Ok(()) } @@ -428,33 +418,35 @@ mod test { ExtensionRegistry::try_new([PRELUDE.to_owned(), int_types::EXTENSION.to_owned()])?; let sig = Signature::new_endo(vec![usize_t(), ity()]); let mut dfg = DFGBuilder::new(sig.clone())?; - let mut mono = dfg.define_function("id2", sig)?; - let pf = mono.define_function( + let mut monof = dfg.define_function("id2", sig)?; + let polyf = monof.define_function( "id", PolyFuncType::new( [TypeBound::Any.into()], Signature::new_endo(Type::new_var_use(0, TypeBound::Any)), ), )?; - let outs = pf.input_wires(); - let pf = pf.finish_with_outputs(outs)?; - let [a, b] = mono.input_wires_arr(); - let [a] = mono - .call(pf.handle(), &[usize_t().into()], [a], ®)? + let outs = polyf.input_wires(); + let polyf = polyf.finish_with_outputs(outs)?; + let [a, b] = monof.input_wires_arr(); + let [a] = monof + .call(polyf.handle(), &[usize_t().into()], [a], ®)? .outputs_arr(); - let [b] = mono - .call(pf.handle(), &[ity().into()], [b], ®)? + let [b] = monof + .call(polyf.handle(), &[ity().into()], [b], ®)? .outputs_arr(); - let mono = mono.finish_with_outputs([a, b])?; - let c = dfg.call(mono.handle(), &[], dfg.input_wires(), ®)?; + let monof = monof.finish_with_outputs([a, b])?; + let c = dfg.call(monof.handle(), &[], dfg.input_wires(), ®)?; let hugr = dfg.finish_hugr_with_outputs(c.outputs(), ®)?; let mono_hugr = monomorphize(hugr, ®); let mut funcs = list_funcs(&mono_hugr); - assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); + for (n, fd) in funcs.values() { + assert_eq!(is_polymorphic(fd), *n == polyf.node()); + } #[allow(clippy::unnecessary_to_owned)] // It is necessary let (m, _) = funcs.remove(&"id2".to_string()).unwrap(); - assert_eq!(m, mono.handle().node()); + assert_eq!(m, monof.node()); assert_eq!(mono_hugr.get_parent(m), Some(mono_hugr.root())); for t in [usize_t(), ity()] { let (n, _) = funcs.remove(&mangle_name("id", &[t.into()])).unwrap(); From 5d0ca00c902b54c1542e88bed8e632f0f7dd5a86 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 9 Dec 2024 20:08:02 +0000 Subject: [PATCH 44/63] Tests in controlflow.rs --- hugr-core/src/ops/controlflow.rs | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 1574a716a..0cfb4afc2 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -355,3 +355,100 @@ impl Case { &self.signature.output } } + +#[cfg(test)] +mod test { + use crate::{ + extension::{ + prelude::{qb_t, usize_t, PRELUDE_ID}, + ExtensionSet, PRELUDE_REGISTRY, + }, + ops::{Conditional, DataflowOpTrait, DataflowParent}, + types::{Signature, Substitution, Type, TypeArg, TypeBound, TypeRV}, + }; + + use super::{DataflowBlock, TailLoop}; + + #[test] + fn test_subst_dataflow_block() { + use crate::ops::OpTrait; + let tv0 = Type::new_var_use(0, TypeBound::Any); + let dfb = DataflowBlock { + inputs: vec![usize_t(), tv0.clone()].into(), + other_outputs: vec![tv0.clone()].into(), + sum_rows: vec![usize_t().into(), vec![qb_t(), tv0.clone()].into()], + extension_delta: ExtensionSet::type_var(1), + }; + let dfb2 = dfb.substitute(&Substitution::new( + &[ + qb_t().into(), + TypeArg::Extensions { + es: PRELUDE_ID.into(), + }, + ], + &PRELUDE_REGISTRY, + )); + let st = Type::new_sum(vec![vec![usize_t()], vec![qb_t(); 2]]); + assert_eq!( + dfb2.inner_signature(), + Signature::new(vec![usize_t(), qb_t()], vec![st, qb_t()]) + .with_extension_delta(PRELUDE_ID) + ); + } + + #[test] + fn test_subst_conditional() { + let tv1 = Type::new_var_use(1, TypeBound::Any); + let cond = Conditional { + sum_rows: vec![usize_t().into(), tv1.clone().into()], + other_inputs: vec![Type::new_tuple(TypeRV::new_row_var_use(0, TypeBound::Any))].into(), + outputs: vec![usize_t(), tv1].into(), + extension_delta: ExtensionSet::new(), + }; + let cond2 = cond.substitute(&Substitution::new( + &[ + TypeArg::Sequence { + elems: vec![usize_t().into(); 3], + }, + qb_t().into(), + ], + &PRELUDE_REGISTRY, + )); + let st = Type::new_sum(vec![usize_t(), qb_t()]); //both single-element variants + assert_eq!( + cond2.signature(), + Signature::new( + vec![st, Type::new_tuple(vec![usize_t(); 3])], + vec![usize_t(), qb_t()] + ) + ); + } + + #[test] + fn test_tail_loop() { + let tv0 = Type::new_var_use(0, TypeBound::Copyable); + let tail_loop = TailLoop { + just_inputs: vec![qb_t(), tv0.clone()].into(), + just_outputs: vec![tv0.clone(), qb_t()].into(), + rest: vec![tv0.clone()].into(), + extension_delta: ExtensionSet::type_var(1), + }; + let tail2 = tail_loop.substitute(&Substitution::new( + &[ + usize_t().into(), + TypeArg::Extensions { + es: PRELUDE_ID.into(), + }, + ], + &PRELUDE_REGISTRY, + )); + assert_eq!( + tail2.signature(), + Signature::new( + vec![qb_t(), usize_t(), usize_t()], + vec![usize_t(), qb_t(), usize_t()] + ) + .with_extension_delta(PRELUDE_ID) + ); + } +} From 00c918c6736ef55fc7e25d9050024a7fddffcae3 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Tue, 10 Dec 2024 10:39:04 +0000 Subject: [PATCH 45/63] Revert "Remove remove_polyfuncs" This reverts commit 9f88f971ae4067d34fc0ae38f39cfc1b73c989b8. --- hugr-passes/src/lib.rs | 2 +- hugr-passes/src/monomorphize.rs | 100 +++++++++++++++++--------------- 2 files changed, 55 insertions(+), 47 deletions(-) diff --git a/hugr-passes/src/lib.rs b/hugr-passes/src/lib.rs index ea7478b99..6c7f5fa06 100644 --- a/hugr-passes/src/lib.rs +++ b/hugr-passes/src/lib.rs @@ -7,7 +7,7 @@ mod half_node; pub mod lower; pub mod merge_bbs; mod monomorphize; -pub use monomorphize::monomorphize; +pub use monomorphize::{monomorphize, remove_polyfuncs}; pub mod nest_cfgs; pub mod non_local; pub mod validation; diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index f2036ab87..167915692 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -10,14 +10,39 @@ use hugr_core::{ use hugr_core::hugr::{hugrmut::HugrMut, internal::HugrMutInternals, Hugr, HugrView, OpType}; /// Replaces calls to polymorphic functions with calls to new monomorphic -/// instantiations of the polymorphic ones. The original polymorphic [FuncDefn]s -/// are left untouched, although with fewer calls (they may still have calls -/// from *other* polymorphic functions still present). +/// instantiations of the polymorphic ones. +/// +/// If the Hugr is [Module](OpType::Module)-rooted, +/// * then the original polymorphic [FuncDefn]s are left untouched (including Calls inside them) +/// - call [remove_polyfuncs] when no other Hugr will be linked in that might instantiate these +/// * else, the originals are removed (they are invisible from outside the Hugr). pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { let root = h.root(); // 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(&mut h, root, None, &mut HashMap::new(), reg); + if !h.get_optype(root).is_module() { + return remove_polyfuncs(h); + } + } + h +} + +/// Removes any polymorphic [FuncDefn]s from the Hugr. Note that if these have +/// calls from *monomorphic* code, this will make the Hugr invalid (call [monomorphize] +/// first). +pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { + let mut pfs_to_delete = Vec::new(); + let mut to_scan = Vec::from_iter(h.children(h.root())); + while let Some(n) = to_scan.pop() { + if is_polymorphic_funcdefn(h.get_optype(n)) { + pfs_to_delete.push(n) + } else { + to_scan.extend(h.children(n)); + } + } + for n in pfs_to_delete { + h.remove_subtree(n); } h } @@ -202,7 +227,7 @@ mod test { use hugr_core::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; use hugr_core::{Hugr, HugrView, Node}; - use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize}; + use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize, remove_polyfuncs}; fn pair_type(ty: Type) -> Type { Type::new_tuple(vec![ty.clone(), ty]) @@ -287,7 +312,6 @@ mod test { .count(), 3 ); - assert_eq!(hugr.static_targets(db.node()).unwrap().count(), 3); // from double (recursive), triple, main let mono = monomorphize(hugr, &PRELUDE_REGISTRY); mono.validate(&PRELUDE_REGISTRY)?; @@ -308,22 +332,16 @@ mod test { ["double", "main", "triple"] ); - // Original double/triple retained, but call to double from main now lost - assert!(is_polymorphic( - mono.get_optype(db.node()).as_func_defn().unwrap() - )); - assert!(is_polymorphic( - mono.get_optype(tr.node()).as_func_defn().unwrap() - )); - assert_eq!( - mono.static_targets(db.node()) - .unwrap() - .map(|(call, _port)| mono.get_parent(call)) - .collect_vec(), - vec![Some(db.node()), Some(tr.node())] - ); - assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent + + let nopoly = remove_polyfuncs(mono); + let mut funcs = list_funcs(&nopoly); + + assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); + for n in expected_mangled_names { + assert!(funcs.remove(&n).is_some()); + } + assert_eq!(funcs.keys().collect_vec(), vec![&"main"]); Ok(()) } @@ -384,23 +402,15 @@ mod test { &mangle_name(&pf2_name, &[ity().into()]), // from pf1 &mangle_name(&pf2_name, &[usize_t().into()]), // from pf1 and (2*)pf1 &mangle_inner_func(&pf2_name, "get_usz"), - "mainish", - "pf1", - &pf2_name, + "mainish" ] .into_iter() .sorted() .collect_vec() ); for (n, fd) in funcs.into_values() { - assert_eq!( - is_polymorphic(fd), - ["pf1", &pf2_name].contains(&fd.name.as_str()) - ); - assert_eq!( - mono_hugr.get_parent(n), - (fd.name != "mainish").then_some(mono_hugr.root()) - ); + assert!(!is_polymorphic(fd)); + assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); } Ok(()) } @@ -418,35 +428,33 @@ mod test { ExtensionRegistry::try_new([PRELUDE.to_owned(), int_types::EXTENSION.to_owned()])?; let sig = Signature::new_endo(vec![usize_t(), ity()]); let mut dfg = DFGBuilder::new(sig.clone())?; - let mut monof = dfg.define_function("id2", sig)?; - let polyf = monof.define_function( + let mut mono = dfg.define_function("id2", sig)?; + let pf = mono.define_function( "id", PolyFuncType::new( [TypeBound::Any.into()], Signature::new_endo(Type::new_var_use(0, TypeBound::Any)), ), )?; - let outs = polyf.input_wires(); - let polyf = polyf.finish_with_outputs(outs)?; - let [a, b] = monof.input_wires_arr(); - let [a] = monof - .call(polyf.handle(), &[usize_t().into()], [a], ®)? + let outs = pf.input_wires(); + let pf = pf.finish_with_outputs(outs)?; + let [a, b] = mono.input_wires_arr(); + let [a] = mono + .call(pf.handle(), &[usize_t().into()], [a], ®)? .outputs_arr(); - let [b] = monof - .call(polyf.handle(), &[ity().into()], [b], ®)? + let [b] = mono + .call(pf.handle(), &[ity().into()], [b], ®)? .outputs_arr(); - let monof = monof.finish_with_outputs([a, b])?; - let c = dfg.call(monof.handle(), &[], dfg.input_wires(), ®)?; + let mono = mono.finish_with_outputs([a, b])?; + let c = dfg.call(mono.handle(), &[], dfg.input_wires(), ®)?; let hugr = dfg.finish_hugr_with_outputs(c.outputs(), ®)?; let mono_hugr = monomorphize(hugr, ®); let mut funcs = list_funcs(&mono_hugr); - for (n, fd) in funcs.values() { - assert_eq!(is_polymorphic(fd), *n == polyf.node()); - } + assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); #[allow(clippy::unnecessary_to_owned)] // It is necessary let (m, _) = funcs.remove(&"id2".to_string()).unwrap(); - assert_eq!(m, monof.node()); + assert_eq!(m, mono.handle().node()); assert_eq!(mono_hugr.get_parent(m), Some(mono_hugr.root())); for t in [usize_t(), ity()] { let (n, _) = funcs.remove(&mangle_name("id", &[t.into()])).unwrap(); From 43826beb79b31ade3ea528d6ec58f612dd04f8c6 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Tue, 10 Dec 2024 12:10:49 +0000 Subject: [PATCH 46/63] wip --- hugr-core/src/builder/build_traits.rs | 7 ++- hugr-core/src/ops/dataflow.rs | 13 +++++- hugr-passes/src/monomorphize.rs | 64 ++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index cb05f2552..3538d9ea7 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -2,7 +2,7 @@ use crate::extension::prelude::MakeTuple; use crate::hugr::hugrmut::InsertionResult; use crate::hugr::views::HugrView; use crate::hugr::{NodeMetadata, ValidationError}; -use crate::ops::{self, OpTag, OpTrait, OpType, Tag, TailLoop}; +use crate::ops::{self, CallIndirect, OpTag, OpTrait, OpType, Tag, TailLoop}; use crate::utils::collect_array; use crate::{IncomingPort, Node, OutgoingPort}; @@ -689,6 +689,11 @@ pub trait Dataflow: Container { Ok(op_id) } + /// TODO docs + fn call_indirect(&mut self, signature: Signature, function: Wire, input_wires: impl IntoIterator) -> Result, BuildError> { + self.add_dataflow_op(CallIndirect { signature }, iter::once(function).chain(input_wires)) + } + /// For the vector of `wires`, produce a `CircuitBuilder` where ops can be /// added using indices in to the vector. fn as_circuit(&mut self, wires: impl IntoIterator) -> CircuitBuilder { diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index c87b784c6..5f3986c60 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -4,7 +4,7 @@ use super::{impl_op_name, OpTag, OpTrait}; use crate::extension::{ExtensionRegistry, ExtensionSet, SignatureError}; use crate::ops::StaticTag; -use crate::types::{EdgeKind, PolyFuncType, Signature, Substitution, Type, TypeArg, TypeRow}; +use crate::types::{EdgeKind, FuncValueType, PolyFuncType, Signature, Substitution, Type, TypeArg, TypeEnum, TypeRow}; use crate::IncomingPort; #[cfg(test)] @@ -392,7 +392,7 @@ pub struct LoadFunction { pub func_sig: PolyFuncType, /// The type arguments that instantiate `func_sig`. pub type_args: Vec, - /// The instantiation of `func_sig`. + /// The signature of the op. pub signature: Signature, // Cache, so we can fail in try_new() not in signature() } impl_op_name!(LoadFunction); @@ -457,6 +457,15 @@ impl LoadFunction { &self.func_sig } + #[inline] + /// TODO docs + pub fn instantiation(&self) -> Signature { + let TypeEnum::Function (ty) = self.signature.output[0].as_type_enum() else { + panic!("") + }; + ty.as_ref().clone().try_into().unwrap() + } + /// The IncomingPort which links to the loaded function. /// /// This matches [`OpType::static_input_port`]. diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 167915692..bb806b587 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -2,12 +2,13 @@ use std::collections::{hash_map::Entry, HashMap}; use hugr_core::{ extension::ExtensionRegistry, - ops::{Call, FuncDefn, OpTrait}, + ops::{Call, FuncDefn, LoadFunction, OpTrait}, types::{Signature, Substitution, TypeArg}, Node, }; use hugr_core::hugr::{hugrmut::HugrMut, internal::HugrMutInternals, Hugr, HugrView, OpType}; +use itertools::Itertools as _; /// Replaces calls to polymorphic functions with calls to new monomorphic /// instantiations of the polymorphic ones. @@ -17,6 +18,9 @@ use hugr_core::hugr::{hugrmut::HugrMut, internal::HugrMutInternals, Hugr, HugrVi /// - call [remove_polyfuncs] when no other Hugr will be linked in that might instantiate these /// * else, the originals are removed (they are invisible from outside the Hugr). pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { + #[cfg(debug_assertions)] + h.validate(reg).unwrap_or_else(|e| panic!("{e}")); + let root = h.root(); // 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)) { @@ -25,6 +29,13 @@ pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { return remove_polyfuncs(h); } } + #[cfg(debug_assertions)] + { + h.validate(reg).unwrap_or_else(|e| { + eprintln!("{}", h.mermaid_string()); + panic!("{e}"); + }); + } h } @@ -74,7 +85,7 @@ fn mono_scan( cache: &mut Instantiations, reg: &ExtensionRegistry, ) { - for old_ch in h.children(parent).collect::>() { + for old_ch in h.children(parent).collect_vec() { let ch_op = h.get_optype(old_ch); debug_assert!(!ch_op.is_func_defn() || subst_into.is_none()); // If substituting, should have flattened already if is_polymorphic_funcdefn(ch_op) { @@ -99,9 +110,16 @@ fn mono_scan( // Now instantiate the target of any Call/LoadFunction to a polymorphic function... let ch_op = h.get_optype(ch); - let (type_args, mono_sig) = match ch_op { - OpType::Call(c) => (&c.type_args, c.instantiation.clone()), - OpType::LoadFunction(lf) => (&lf.type_args, lf.signature.clone()), + let (type_args, mono_sig, new_op) = match ch_op { + OpType::Call(c) => { + let mono_sig = c.instantiation.clone(); + (&c.type_args, mono_sig.clone(), OpType::from(Call::try_new(mono_sig.into(), [], reg).unwrap())) + } + OpType::LoadFunction(lf) => { + eprintln!("{lf:?}"); + let mono_sig = lf.instantiation(); + (&lf.type_args, mono_sig.clone(), LoadFunction::try_new(mono_sig.into(), [], reg).unwrap().into()) + } _ => continue, }; if type_args.is_empty() { @@ -110,11 +128,16 @@ fn mono_scan( let fn_inp = ch_op.static_input_port().unwrap(); let tgt = h.static_source(old_ch).unwrap(); // Use old_ch as edges not copied yet let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); - let fn_out = h.get_optype(new_tgt).static_output_port().unwrap(); + let fn_out = { + let func = h.get_optype(new_tgt).as_func_defn().unwrap(); + debug_assert_eq!(func.signature, mono_sig.into()); + h.get_optype(new_tgt).static_output_port().unwrap() + }; h.disconnect(ch, fn_inp); // No-op if copying+substituting h.connect(new_tgt, fn_out, ch, fn_inp); - h.replace_op(ch, Call::try_new(mono_sig.into(), vec![], reg).unwrap()) + + h.replace_op(ch, new_op) .unwrap(); } } @@ -462,4 +485,31 @@ mod test { } Ok(()) } + + #[test] + fn load_function() { + let hugr = { + let mut module_builder = ModuleBuilder::new(); + let foo = { + let builder = module_builder.define_function("foo", PolyFuncType::new([TypeBound::Any.into()], Signature::new_endo(Type::new_var_use(0,TypeBound::Any)))).unwrap(); + let inputs = builder.input_wires(); + builder.finish_with_outputs(inputs).unwrap() + }; + + let _main = { + let mut builder = module_builder.define_function("main", Signature::new_endo(Type::UNIT)).unwrap(); + let func_ptr = builder.load_func(foo.handle(), &[Type::UNIT.into()], &EMPTY_REG).unwrap(); + let [r] = builder.call_indirect(Signature::new_endo(Type::UNIT), func_ptr, builder.input_wires()).unwrap().outputs_arr(); + builder.finish_with_outputs([r]).unwrap() + }; + module_builder.finish_hugr(&EMPTY_REG).unwrap() + }; + + let mono_hugr = remove_polyfuncs(monomorphize(hugr, &EMPTY_REG)); + + let funcs = list_funcs(&mono_hugr); + assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); + } } + +// TODO LoadFunction From 802d361a5510a74b31cc3dd88b39413ef963ae70 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Tue, 10 Dec 2024 12:16:51 +0000 Subject: [PATCH 47/63] fix!: Replace `LoadFunction::signature` with `LoadFunction::instantiation` Closes #1755 BREAKING CHANGE: The `LoadFunction::signature` field is removed. Replace uses with `DataflowOpTrait::signature()`. --- hugr-core/src/extension/resolution/types.rs | 2 +- hugr-core/src/ops/dataflow.rs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/hugr-core/src/extension/resolution/types.rs b/hugr-core/src/extension/resolution/types.rs index b249a4dca..d512add5c 100644 --- a/hugr-core/src/extension/resolution/types.rs +++ b/hugr-core/src/extension/resolution/types.rs @@ -53,7 +53,7 @@ pub fn update_op_types_extensions( } OpType::LoadFunction(lf) => { update_signature_exts(node, lf.func_sig.body_mut(), extensions, used_extensions)?; - update_signature_exts(node, &mut lf.signature, extensions, used_extensions)?; + update_signature_exts(node, &mut lf.instantiation, extensions, used_extensions)?; } OpType::DFG(dfg) => { update_signature_exts(node, &mut dfg.signature, extensions, used_extensions)? diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index ed9a9e118..fa1ec0470 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -5,7 +5,7 @@ use super::{impl_op_name, OpTag, OpTrait}; use crate::extension::{ExtensionRegistry, ExtensionSet, SignatureError}; use crate::ops::StaticTag; use crate::types::{EdgeKind, PolyFuncType, Signature, Type, TypeArg, TypeRow}; -use crate::IncomingPort; +use crate::{type_row, IncomingPort}; #[cfg(test)] use ::proptest_derive::Arbitrary; @@ -341,7 +341,7 @@ pub struct LoadFunction { /// The type arguments that instantiate `func_sig`. pub type_args: Vec, /// The instantiation of `func_sig`. - pub signature: Signature, // Cache, so we can fail in try_new() not in signature() + pub instantiation: Signature, // Cache, so we can fail in try_new() not in signature() } impl_op_name!(LoadFunction); impl DataflowOpTrait for LoadFunction { @@ -352,7 +352,7 @@ impl DataflowOpTrait for LoadFunction { } fn signature(&self) -> Signature { - self.signature.clone() + Signature::new(type_row![], Type::new_function(self.instantiation.clone())) } fn static_input(&self) -> Option { @@ -371,11 +371,10 @@ impl LoadFunction { ) -> Result { let type_args = type_args.into(); let instantiation = func_sig.instantiate(&type_args, exts)?; - let signature = Signature::new(TypeRow::new(), vec![Type::new_function(instantiation)]); Ok(Self { func_sig, type_args, - signature, + instantiation, }) } @@ -404,12 +403,12 @@ impl LoadFunction { self.type_args.clone(), extension_registry, )?; - if other.signature == self.signature { + if other.instantiation == self.instantiation { Ok(()) } else { Err(SignatureError::LoadFunctionIncorrectlyAppliesType { - cached: self.signature.clone(), - expected: other.signature.clone(), + cached: self.instantiation.clone(), + expected: other.instantiation.clone(), }) } } From afb6e17a300a533ab84ee77b2875fe7ed6652860 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 9 Dec 2024 17:21:05 +0000 Subject: [PATCH 48/63] Adjust flattening test to also check multiple typeargs and BoundedNats --- hugr-passes/src/monomorphize.rs | 105 ++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index f2036ab87..b60630b91 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -188,18 +188,24 @@ fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { mod test { use std::collections::HashMap; + use hugr_core::extension::simple_op::MakeRegisteredOp; + use hugr_core::types::type_param::TypeParam; use itertools::Itertools; use hugr_core::builder::{ Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, }; - use hugr_core::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; + use hugr_core::extension::prelude::{ + array_type, usize_t, ArrayOpDef, ConstUsize, UnpackTuple, UnwrapBuilder, PRELUDE_ID, + }; use hugr_core::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; use hugr_core::ops::handle::{FuncID, NodeHandle}; - use hugr_core::ops::{FuncDefn, Tag}; + use hugr_core::ops::{DataflowOpTrait, FuncDefn, Tag}; use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; - use hugr_core::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; + use hugr_core::types::{ + PolyFuncType, Signature, SumType, Type, TypeArg, TypeBound, TypeEnum, TypeRow, + }; use hugr_core::{Hugr, HugrView, Node}; use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize}; @@ -227,7 +233,7 @@ mod test { } #[test] - fn test_module() -> Result<(), Box> { + fn test_recursion_module() -> Result<(), Box> { let tv0 = || Type::new_var_use(0, TypeBound::Copyable); let mut mb = ModuleBuilder::new(); let db = { @@ -328,22 +334,36 @@ mod test { } #[test] - fn test_flattening() -> Result<(), Box> { + fn test_flattening_multiargs_nats() -> Result<(), Box> { //pf1 contains pf2 contains mono_func -> pf1 and pf1 share pf2's and they share mono_func let reg = ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; - let tv0 = || Type::new_var_use(0, TypeBound::Any); - let ity = || INT_TYPES[3].clone(); - - let pf_any = |ins, outs| PolyFuncType::new([TypeBound::Any.into()], prelusig(ins, outs)); - - let mut outer = FunctionBuilder::new("mainish", prelusig(ity(), usize_t()))?; + let tv = |i| Type::new_var_use(i, TypeBound::Copyable); + let sv = |i| TypeArg::new_var_use(i, TypeParam::max_nat()); + let sa = |n| TypeArg::BoundedNat { n }; + + let n: u64 = 5; + let mut outer = FunctionBuilder::new( + "mainish", + prelusig( + array_type(sa(n), array_type(sa(2), usize_t())), + vec![usize_t(); 2], + ), + )?; - let pfty = pf_any(vec![tv0()], vec![tv0(), usize_t(), usize_t()]); - let mut pf1 = outer.define_function("pf1", pfty)?; + let arr2u = || array_type(sa(2), usize_t()); + let pf1t = PolyFuncType::new( + [TypeParam::max_nat()], + prelusig(array_type(sv(0), arr2u()), usize_t()), + ); + let mut pf1 = outer.define_function("pf1", pf1t)?; - let mut pf2 = pf1.define_function("pf2", pf_any(vec![tv0()], vec![tv0(), usize_t()]))?; + let pf2t = PolyFuncType::new( + [TypeParam::max_nat(), TypeBound::Copyable.into()], + prelusig(vec![array_type(sv(0), tv(1))], tv(1)), + ); + let mut pf2 = pf1.define_function("pf2", pf2t)?; let mono_func = { let mut fb = pf2.define_function("get_usz", prelusig(vec![], usize_t()))?; @@ -352,25 +372,45 @@ mod test { }; let pf2 = { let [inw] = pf2.input_wires_arr(); - let [usz] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); - pf2.finish_with_outputs([inw, usz])? + let [idx] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); + let op_def = PRELUDE.get_op("get").unwrap(); + let op = + hugr_core::ops::ExtensionOp::new(op_def.clone(), vec![sv(0), tv(1).into()], ®)?; + let [get] = pf2.add_dataflow_op(op, [inw, idx])?.outputs_arr(); + let [got] = pf2.build_unwrap_sum(®, 1, SumType::new([vec![], vec![tv(1)]]), get)?; + pf2.finish_with_outputs([got])? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not - let [a, u] = pf1 - .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®)? - .outputs_arr(); - let [u1, u2] = pf1 - .call(pf2.handle(), &[usize_t().into()], [u], ®)? - .outputs_arr(); - let pf1 = pf1.finish_with_outputs([a, u1, u2])?; + let inner = pf1.call( + pf2.handle(), + &[sv(0), arr2u().into()], + pf1.input_wires(), + ®, + )?; + let elem = pf1.call( + pf2.handle(), + &[TypeArg::BoundedNat { n: 2 }, usize_t().into()], + inner.outputs(), + ®, + )?; + let pf1 = pf1.finish_with_outputs(elem.outputs())?; // Outer: two calls to pf1 with different TypeArgs - let [_, u, _] = outer - .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®)? + let [e1] = outer + .call(pf1.handle(), &[sa(n)], outer.input_wires(), ®)? .outputs_arr(); - let [_, u, _] = outer - .call(pf1.handle(), &[usize_t().into()], [u], ®)? + let popleft = ArrayOpDef::pop_left.to_concrete(arr2u(), n); + let ar2 = outer.add_dataflow_op(popleft.clone(), outer.input_wires())?; + let sig = popleft.to_extension_op().unwrap().signature(); + let TypeEnum::Sum(st) = sig.output().get(0).unwrap().as_type_enum() else { + panic!() + }; + let [_, ar2_unwrapped] = outer + .build_unwrap_sum(®, 1, st.clone(), ar2.out_wire(0)) + .unwrap(); + let [e2] = outer + .call(pf1.handle(), &[sa(n - 1)], [ar2_unwrapped], ®)? .outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([u], ®)?; + let hugr = outer.finish_hugr_with_outputs([e1, e2], ®)?; let mono_hugr = monomorphize(hugr, ®); mono_hugr.validate(®)?; @@ -379,10 +419,11 @@ mod test { assert_eq!( funcs.keys().copied().sorted().collect_vec(), vec![ - &mangle_name("pf1", &[ity().into()]), - &mangle_name("pf1", &[usize_t().into()]), - &mangle_name(&pf2_name, &[ity().into()]), // from pf1 - &mangle_name(&pf2_name, &[usize_t().into()]), // from pf1 and (2*)pf1 + &mangle_name("pf1", &[TypeArg::BoundedNat { n: 5 }]), + &mangle_name("pf1", &[TypeArg::BoundedNat { n: 4 }]), + &mangle_name(&pf2_name, &[TypeArg::BoundedNat { n: 5 }, arr2u().into()]), // from pf1<5> + &mangle_name(&pf2_name, &[TypeArg::BoundedNat { n: 4 }, arr2u().into()]), // from pf1<4> + &mangle_name(&pf2_name, &[TypeArg::BoundedNat { n: 2 }, usize_t().into()]), // from both pf1<4> and <5> &mangle_inner_func(&pf2_name, "get_usz"), "mainish", "pf1", From 5d2ff7d03584f6adb0d49dfc6052611e1702a1e2 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Wed, 11 Dec 2024 05:57:16 +0000 Subject: [PATCH 49/63] wip --- hugr-core/src/types/type_param.rs | 17 +++++- hugr-passes/src/monomorphize.rs | 93 +++++++++++++++++++++++++------ 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/hugr-core/src/types/type_param.rs b/hugr-core/src/types/type_param.rs index 3c40552aa..be688ad7c 100644 --- a/hugr-core/src/types/type_param.rs +++ b/hugr-core/src/types/type_param.rs @@ -155,18 +155,26 @@ pub enum TypeArg { n: u64, }, ///Instance of [TypeParam::String]. UTF-8 encoded string argument. + #[display("\"{arg}\"")] String { #[allow(missing_docs)] arg: String, }, /// Instance of [TypeParam::List] or [TypeParam::Tuple], defined by a /// sequence of elements. - #[display("Sequence({})", "elems.iter().map(|t|t.to_string()).join(\", \")")] + #[display("Sequence({})", { + use itertools::Itertools as _; + elems.iter().map(|t|t.to_string()).join(",") + })] Sequence { #[allow(missing_docs)] elems: Vec, }, /// Instance of [TypeParam::Extensions], providing the extension ids. + #[display("Exts({})", { + use itertools::Itertools as _; + es.iter().map(|t|t.to_string()).join(",") + })] Extensions { #[allow(missing_docs)] es: ExtensionSet, @@ -202,6 +210,13 @@ impl From for TypeArg { } } +impl From<&str> for TypeArg { + fn from(arg: &str) -> Self { + TypeArg::String { arg: arg.to_string() } + } + +} + impl From> for TypeArg { fn from(elems: Vec) -> Self { Self::Sequence { elems } diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index ebee5e448..d25eff8cd 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -1,4 +1,4 @@ -use std::collections::{hash_map::Entry, HashMap}; +use std::{collections::{hash_map::Entry, HashMap}, ops::Deref}; use hugr_core::{ extension::ExtensionRegistry, @@ -17,9 +17,14 @@ use itertools::Itertools as _; /// * then the original polymorphic [FuncDefn]s are left untouched (including Calls inside them) /// - call [remove_polyfuncs] when no other Hugr will be linked in that might instantiate these /// * else, the originals are removed (they are invisible from outside the Hugr). +/// +/// If the Hugr is [FuncDefn](OpType::FuncDefn)-rooted with polymorphic +/// signature then the hugr is untouched. pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { + let validate = |h: &Hugr| h.validate(reg).unwrap_or_else(|e| panic!("{e}")); + #[cfg(debug_assertions)] - h.validate(reg).unwrap_or_else(|e| panic!("{e}")); + validate(&h); let root = h.root(); // If the root is a polymorphic function, then there are no external calls, so nothing to do @@ -30,18 +35,16 @@ pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { } } #[cfg(debug_assertions)] - { - h.validate(reg).unwrap_or_else(|e| { - eprintln!("{}", h.mermaid_string()); - panic!("{e}"); - }); - } + validate(&h); h } /// Removes any polymorphic [FuncDefn]s from the Hugr. Note that if these have /// calls from *monomorphic* code, this will make the Hugr invalid (call [monomorphize] /// first). +/// +/// TODO replace this with a more general remove-unused-functions pass +/// https://github.com/CQCL/hugr/issues/1753 pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { let mut pfs_to_delete = Vec::new(); let mut to_scan = Vec::from_iter(h.children(h.root())); @@ -231,19 +234,62 @@ fn instantiate( mono_tgt } -fn mangle_name(name: &str, type_args: &[TypeArg]) -> String { - let s = format!("__{name}_{type_args:?}"); - s.replace(['[', ']', '{', '}', ' '], "_") +struct TypeArgsList<'a>(&'a[TypeArg]); + +impl<'a> std::fmt::Display for TypeArgsList<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for arg in self.0 { + f.write_str("$")?; + write_type_arg_str(arg, f)?; + } + Ok(()) + } +} + +fn write_type_arg_str(arg: &TypeArg, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match arg { + TypeArg::Type { ty } => { + f.write_str(&ty.to_string().replace("$", "\\$")) + }, + TypeArg::BoundedNat { n } => f.write_fmt(format_args!("n({n})")), + TypeArg::String { arg } => f.write_fmt(format_args!("s({})", arg.replace("$", "\\$"))), + TypeArg::Sequence { elems } => { + f.write_str("seq(")?; + let mut first = true; + for arg in elems.iter() { + if first { + first = false; + } else { + f.write_str(",")?; + } + write_type_arg_str(arg, f)?; + } + f.write_str(")")?; + Ok(()) + } + TypeArg::Extensions { es } => f.write_fmt(format_args!("es({})", es.iter().map(|x| x.deref()).join(","))), + TypeArg::Variable { .. } => panic!("type_arg_str variable: {arg}"), + _ => panic!("unknown type arg: {arg}"), + } +} + +/// We do our best to generate unique names. +/// +/// We depend on the [Display] impl of [TypeArg]. +/// +fn mangle_name(name: &str, type_args: impl AsRef<[TypeArg]>) -> String { + format!("${name}${}", TypeArgsList(type_args.as_ref())) } fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { - format!("$_{outer_name}_$_{inner_name}") + format!("${outer_name}${inner_name}") } #[cfg(test)] mod test { use std::collections::HashMap; + use hugr_core::types::type_param::TypeParam; use itertools::Itertools; use hugr_core::builder::{ @@ -251,12 +297,13 @@ mod test { HugrBuilder, ModuleBuilder, }; use hugr_core::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; - use hugr_core::extension::{ExtensionRegistry, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; + use hugr_core::extension::{ExtensionRegistry, ExtensionSet, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; use hugr_core::ops::handle::{FuncID, NodeHandle}; use hugr_core::ops::{FuncDefn, Tag}; use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; - use hugr_core::types::{PolyFuncType, Signature, Type, TypeBound, TypeRow}; + use hugr_core::types::{PolyFuncType, Signature, Type, TypeArg, TypeBound, TypeRow}; use hugr_core::{Hugr, HugrView, Node}; + use rstest::rstest; use super::{is_polymorphic, mangle_inner_func, mangle_name, monomorphize, remove_polyfuncs}; @@ -537,6 +584,20 @@ mod test { let funcs = list_funcs(&mono_hugr); assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); } -} -// TODO LoadFunction + #[rstest] + #[case::bounded_nat(vec![0.into()], "$foo$$n(0)")] + #[case::type_(vec![Type::UNIT.into()], "$foo$$[]")] + #[case::string(vec!["arg".into()], "$foo$$s(arg)")] + #[case::dollar_string(vec!["$arg".into()], "$foo$$s(\\$arg)")] + #[case::sequence(vec![vec![0.into(), Type::UNIT.into()].into()], "$foo$$seq(n(0),[])")] + #[case::extensionset(vec![ExtensionSet::from_iter([PRELUDE_ID,int_types::EXTENSION_ID]).into()], + "$foo$$es(arithmetic.int.types,prelude)")] // alphabetic ordering of extension names + #[should_panic] + #[case::typeargvariable(vec![TypeArg::new_var_use(1, TypeParam::String).into()], + "$foo$$v(1)")] + #[case::multiple(vec![0.into(), "arg".into()], "$foo$$n(0)$s(arg)")] + fn test_mangle_name(#[case] args: Vec, #[case] expected: String) { + assert_eq!(mangle_name("foo", &args), expected); + } +} From 703004e5d008eb1b12e81b65bd44904b732fc965 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Wed, 11 Dec 2024 12:02:34 +0000 Subject: [PATCH 50/63] wip --- devenv.lock | 69 +++++++++++++------------- hugr-core/src/ops.rs | 5 +- hugr-core/src/ops/controlflow.rs | 54 +++++++++++---------- hugr-core/src/ops/custom.rs | 14 +++--- hugr-core/src/ops/dataflow.rs | 40 +++++++-------- hugr-core/src/ops/sum.rs | 5 +- hugr-core/src/types.rs | 22 ++++----- hugr-core/src/types/custom.rs | 6 +-- hugr-core/src/types/poly_func.rs | 2 +- hugr-core/src/types/signature.rs | 6 +-- hugr-core/src/types/type_param.rs | 12 ++--- hugr-core/src/types/type_row.rs | 4 +- hugr-passes/src/monomorphize.rs | 81 ++++++++++++++----------------- 13 files changed, 157 insertions(+), 163 deletions(-) diff --git a/devenv.lock b/devenv.lock index c326bd43b..4b625b566 100644 --- a/devenv.lock +++ b/devenv.lock @@ -39,10 +39,10 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, + "lastModified": 1733328505, "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -51,10 +51,32 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1733665616, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "d8c02f0ffef0ef39f6063731fc539d8c71eb463a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ - "pre-commit-hooks", + "git-hooks", "nixpkgs" ] }, @@ -88,63 +110,44 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1704290814, + "lastModified": 1733730953, "owner": "NixOS", "repo": "nixpkgs", - "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", + "rev": "7109b680d161993918b0a126f38bc39763e5a709", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-stable_2": { "locked": { - "lastModified": 1728193676, + "lastModified": 1704290814, "owner": "NixOS", "repo": "nixpkgs", - "rev": "ecbc1ca8ffd6aea8372ad16be9ebbb39889e55b6", + "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-24.05", + "ref": "nixos-23.05", "repo": "nixpkgs", "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable_2" - }, - "locked": { - "lastModified": 1728092656, - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "1211305a5b237771e13fcca0c51e60ad47326a9a", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "devenv": "devenv", "fenix": "fenix", + "git-hooks": "git-hooks", "nixpkgs": "nixpkgs", - "nixpkgs-stable": "nixpkgs-stable", - "pre-commit-hooks": "pre-commit-hooks" + "nixpkgs-stable": "nixpkgs-stable_2", + "pre-commit-hooks": [ + "git-hooks" + ] } }, "rust-analyzer-src": { diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index a2ff0eed8..312127e84 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -15,9 +15,8 @@ use crate::extension::resolution::{ use std::borrow::Cow; use crate::extension::simple_op::MakeExtensionOp; -use crate::extension::{ExtensionId, ExtensionSet}; +use crate::extension::{ExtensionId, ExtensionSet, ExtensionRegistry}; use crate::types::{EdgeKind, Signature, Substitution}; -use crate::extension::{ExtensionId, ExtensionRegistry, ExtensionSet}; use crate::{Direction, OutgoingPort, Port}; use crate::{IncomingPort, PortIndex}; use derive_more::Display; @@ -437,7 +436,7 @@ pub trait OpTrait: Sized + Clone { /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](crate::types::TypeArg::new_var_use) with new types. - fn substitute(&self, _subst: &Substitution) -> Self { + fn substitute(&self, _subst: &Substitution, _reg: &ExtensionRegistry) -> Self { self.clone() } } diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 44c78eae3..fcf841acb 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; -use crate::extension::ExtensionSet; +use crate::extension::{ExtensionRegistry, ExtensionSet}; use crate::types::{EdgeKind, Signature, Type, TypeRow}; use crate::Direction; @@ -42,11 +42,11 @@ impl DataflowOpTrait for TailLoop { ) } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - just_inputs: self.just_inputs.substitute(subst), - just_outputs: self.just_outputs.substitute(subst), - rest: self.rest.substitute(subst), + just_inputs: self.just_inputs.substitute(subst, reg), + just_outputs: self.just_outputs.substitute(subst, reg), + rest: self.rest.substitute(subst, reg), extension_delta: self.extension_delta.substitute(subst), } } @@ -121,11 +121,11 @@ impl DataflowOpTrait for Conditional { ) } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(), - other_inputs: self.other_inputs.substitute(subst), - outputs: self.outputs.substitute(subst), + sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst, reg)).collect(), + other_inputs: self.other_inputs.substitute(subst, reg), + outputs: self.outputs.substitute(subst, reg), extension_delta: self.extension_delta.substitute(subst), } } @@ -159,9 +159,9 @@ impl DataflowOpTrait for CFG { Cow::Borrowed(&self.signature) } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - signature: self.signature.substitute(subst), + signature: self.signature.substitute(subst, reg), } } } @@ -248,11 +248,11 @@ impl OpTrait for DataflowBlock { } } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - inputs: self.inputs.substitute(subst), - other_outputs: self.other_outputs.substitute(subst), - sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(), + inputs: self.inputs.substitute(subst, reg), + other_outputs: self.other_outputs.substitute(subst, reg), + sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst, reg)).collect(), extension_delta: self.extension_delta.substitute(subst), } } @@ -282,9 +282,9 @@ impl OpTrait for ExitBlock { } } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - cfg_outputs: self.cfg_outputs.substitute(subst), + cfg_outputs: self.cfg_outputs.substitute(subst, reg), } } } @@ -351,9 +351,9 @@ impl OpTrait for Case { ::TAG } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - signature: self.signature.substitute(subst), + signature: self.signature.substitute(subst, reg), } } } @@ -372,6 +372,8 @@ impl Case { #[cfg(test)] mod test { + use std::borrow::Borrow; + use crate::{ extension::{ prelude::{qb_t, usize_t, PRELUDE_ID}, @@ -399,9 +401,9 @@ mod test { TypeArg::Extensions { es: PRELUDE_ID.into(), }, - ], + ]), &PRELUDE_REGISTRY, - )); + ); let st = Type::new_sum(vec![vec![usize_t()], vec![qb_t(); 2]]); assert_eq!( dfb2.inner_signature(), @@ -425,9 +427,9 @@ mod test { elems: vec![usize_t().into(); 3], }, qb_t().into(), - ], + ]), &PRELUDE_REGISTRY, - )); + ); let st = Type::new_sum(vec![usize_t(), qb_t()]); //both single-element variants assert_eq!( cond2.signature(), @@ -453,12 +455,12 @@ mod test { TypeArg::Extensions { es: PRELUDE_ID.into(), }, - ], + ]), &PRELUDE_REGISTRY, - )); + ); assert_eq!( tail2.signature(), - Signature::new( + ow r::new( vec![qb_t(), usize_t(), usize_t()], vec![usize_t(), qb_t(), usize_t()] ) diff --git a/hugr-core/src/ops/custom.rs b/hugr-core/src/ops/custom.rs index a0ad220e7..7c91595fe 100644 --- a/hugr-core/src/ops/custom.rs +++ b/hugr-core/src/ops/custom.rs @@ -167,16 +167,16 @@ impl DataflowOpTrait for ExtensionOp { Cow::Borrowed(&self.signature) } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { let args = self .args .iter() - .map(|ta| ta.substitute(subst)) + .map(|ta| ta.substitute(subst, reg)) .collect::>(); - let signature = self.signature.substitute(subst); + let signature = self.signature.substitute(subst, reg); debug_assert_eq!( self.def - .compute_signature(&args, subst.extension_registry()) + .compute_signature(&args, reg) .as_ref(), Ok(&signature) ); @@ -280,10 +280,10 @@ impl DataflowOpTrait for OpaqueOp { Cow::Borrowed(&self.signature) } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - args: self.args.iter().map(|ta| ta.substitute(subst)).collect(), - signature: self.signature.substitute(subst), + args: self.args.iter().map(|ta| ta.substitute(subst, reg)).collect(), + signature: self.signature.substitute(subst, reg), ..self.clone() } } diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index 9e6208b65..dfc65e18a 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -54,7 +54,7 @@ pub trait DataflowOpTrait: Sized { /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](TypeArg::new_var_use) with new types. - fn substitute(&self, _subst: &Substitution) -> Self; + fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self; } /// Helpers to construct input and output nodes @@ -116,9 +116,9 @@ impl DataflowOpTrait for Input { Cow::Owned(Signature::new(TypeRow::new(), self.types.clone())) } - fn substitute(&self, subst: &Substitution) -> Self { + fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { Self { - types: self.types.substitute(subst), + types: self.types.substitute(subst, reg), } } } @@ -140,9 +140,9 @@ impl DataflowOpTrait for Output { None } - fn substitute(&self, subst: &Substitution) -> Self { + fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { Self { - types: self.types.substitute(subst), + types: self.types.substitute(subst, reg), } } } @@ -172,8 +172,8 @@ impl OpTrait for T { DataflowOpTrait::static_input(self) } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { - DataflowOpTrait::substitute(self, subst) + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + DataflowOpTrait::substitute(self, subst, reg) } } impl StaticTag for T { @@ -212,16 +212,16 @@ impl DataflowOpTrait for Call { Some(EdgeKind::Function(self.called_function_type().clone())) } - fn substitute(&self, subst: &Substitution) -> Self { + fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { let type_args = self .type_args .iter() - .map(|ta| ta.substitute(subst)) + .map(|ta| ta.substitute(subst, reg)) .collect::>(); - let instantiation = self.instantiation.substitute(subst); + let instantiation = self.instantiation.substitute(subst, reg); debug_assert_eq!( self.func_sig - .instantiate(&type_args, subst.extension_registry()) + .instantiate(&type_args, reg) .as_ref(), Ok(&instantiation) ); @@ -325,9 +325,9 @@ impl DataflowOpTrait for CallIndirect { Cow::Owned(s) } - fn substitute(&self, subst: &Substitution) -> Self { + fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { Self { - signature: self.signature.substitute(subst), + signature: self.signature.substitute(subst, reg), } } } @@ -356,7 +356,7 @@ impl DataflowOpTrait for LoadConstant { Some(EdgeKind::Const(self.constant_type().clone())) } - fn substitute(&self, _subst: &Substitution) -> Self { + fn substitute(&self, _subst: &Substitution, _reg: &ExtensionRegistry) -> Self { // Constants cannot refer to TypeArgs, so neither can loading them self.clone() } @@ -420,16 +420,16 @@ impl DataflowOpTrait for LoadFunction { Some(EdgeKind::Function(self.func_sig.clone())) } - fn substitute(&self, subst: &Substitution) -> Self { + fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { let type_args = self .type_args .iter() - .map(|ta| ta.substitute(subst)) + .map(|ta| ta.substitute(subst, reg)) .collect::>(); - let instantiation = self.instantiation.substitute(subst); + let instantiation = self.instantiation.substitute(subst, reg); debug_assert_eq!( self.func_sig - .instantiate(&type_args, subst.extension_registry()) + .instantiate(&type_args, reg) .as_ref(), Ok(&instantiation) ); @@ -528,9 +528,9 @@ impl DataflowOpTrait for DFG { self.inner_signature() } - fn substitute(&self, subst: &Substitution) -> Self { + fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { Self { - signature: self.signature.substitute(subst), + signature: self.signature.substitute(subst, reg), } } } diff --git a/hugr-core/src/ops/sum.rs b/hugr-core/src/ops/sum.rs index 7cc9fb577..55b268cb4 100644 --- a/hugr-core/src/ops/sum.rs +++ b/hugr-core/src/ops/sum.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use super::dataflow::DataflowOpTrait; use super::{impl_op_name, OpTag}; +use crate::extension::ExtensionRegistry; use crate::types::{EdgeKind, Signature, Type, TypeRow}; /// An operation that creates a tagged sum value from one of its variants. @@ -56,9 +57,9 @@ impl DataflowOpTrait for Tag { Some(EdgeKind::StateOrder) } - fn substitute(&self, subst: &crate::types::Substitution) -> Self { + fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { Self { - variants: self.variants.iter().map(|r| r.substitute(subst)).collect(), + variants: self.variants.iter().map(|r| r.substitute(subst, reg)).collect(), tag: self.tag, } } diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index 72aae7b7e..dc91fc01e 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -448,7 +448,7 @@ impl TypeBase { /// * If [Type::validate]`(false)` returns successfully, this method will return a Vec containing exactly one type /// * If [Type::validate]`(false)` fails, but `(true)` succeeds, this method may (depending on structure of self) /// return a Vec containing any number of [Type]s. These may (or not) pass [Type::validate] - fn substitute(&self, t: &Substitution) -> Vec { + fn substitute(&self, t: &Substitution, reg: &ExtensionRegistry) -> Vec { match &self.0 { TypeEnum::RowVar(rv) => rv.substitute(t), TypeEnum::Alias(_) | TypeEnum::Sum(SumType::Unit { .. }) => vec![self.clone()], @@ -458,18 +458,18 @@ impl TypeBase { }; vec![ty.into_()] } - TypeEnum::Extension(cty) => vec![TypeBase::new_extension(cty.substitute(t))], - TypeEnum::Function(bf) => vec![TypeBase::new_function(bf.substitute(t))], + TypeEnum::Extension(cty) => vec![TypeBase::new_extension(cty.substitute(t, reg))], + TypeEnum::Function(bf) => vec![TypeBase::new_function(bf.substitute(t, reg))], TypeEnum::Sum(SumType::General { rows }) => { - vec![TypeBase::new_sum(rows.iter().map(|r| r.substitute(t)))] + vec![TypeBase::new_sum(rows.iter().map(|r| r.substitute(t, reg)))] } } } } impl Type { - fn substitute1(&self, s: &Substitution) -> Self { - let v = self.substitute(s); + fn substitute1(&self, s: &Substitution, reg: &ExtensionRegistry) -> Self { + let v = self.substitute(s, reg); let [r] = v.try_into().unwrap(); // No row vars, so every Type produces exactly one r } @@ -548,7 +548,7 @@ impl From for TypeRV { /// Details a replacement of type variables with a finite list of known values. /// (Variables out of the range of the list will result in a panic) -pub struct Substitution<'a>(&'a [TypeArg], &'a ExtensionRegistry); +pub struct Substitution<'a>(&'a [TypeArg]); impl<'a> Substitution<'a> { /// Create a new Substitution given the replacement values (indexed @@ -557,8 +557,8 @@ impl<'a> Substitution<'a> { /// containing a type-variable. /// /// [TypeDef]: crate::extension::TypeDef - pub fn new(items: &'a [TypeArg], exts: &'a ExtensionRegistry) -> Self { - Self(items, exts) + pub fn new(items: &'a [TypeArg]) -> Self { + Self(items) } pub(crate) fn apply_var(&self, idx: usize, decl: &TypeParam) -> TypeArg { @@ -599,10 +599,6 @@ impl<'a> Substitution<'a> { _ => panic!("Not a type or list of types - call validate() ?"), } } - - pub(crate) fn extension_registry(&self) -> &ExtensionRegistry { - self.1 - } } pub(crate) fn check_typevar_decl( diff --git a/hugr-core/src/types/custom.rs b/hugr-core/src/types/custom.rs index 81bc814ac..f1a08098f 100644 --- a/hugr-core/src/types/custom.rs +++ b/hugr-core/src/types/custom.rs @@ -109,14 +109,14 @@ impl CustomType { }) } - pub(super) fn substitute(&self, tr: &Substitution) -> Self { + pub(super) fn substitute(&self, tr: &Substitution, reg: &ExtensionRegistry) -> Self { let args = self .args .iter() - .map(|arg| arg.substitute(tr)) + .map(|arg| arg.substitute(tr, reg)) .collect::>(); let bound = self - .get_type_def(tr.extension_registry()) + .get_type_def(reg) .expect("validate should rule this out") .bound(&args); debug_assert!(self.bound.contains(bound)); diff --git a/hugr-core/src/types/poly_func.rs b/hugr-core/src/types/poly_func.rs index 66dff8245..23d65453a 100644 --- a/hugr-core/src/types/poly_func.rs +++ b/hugr-core/src/types/poly_func.rs @@ -125,7 +125,7 @@ impl PolyFuncTypeBase { // Check that args are applicable, and that we have a value for each binder, // i.e. each possible free variable within the body. check_type_args(args, &self.params)?; - Ok(self.body.substitute(&Substitution(args, ext_reg))) + Ok(self.body.substitute(&Substitution(args), ext_reg)) } /// Validates this instance, checking that the types in the body are diff --git a/hugr-core/src/types/signature.rs b/hugr-core/src/types/signature.rs index c5d3459e1..2112acd34 100644 --- a/hugr-core/src/types/signature.rs +++ b/hugr-core/src/types/signature.rs @@ -61,10 +61,10 @@ impl FuncTypeBase { self.with_extension_delta(crate::extension::prelude::PRELUDE_ID) } - pub(crate) fn substitute(&self, tr: &Substitution) -> Self { + pub(crate) fn substitute(&self, tr: &Substitution, reg: &ExtensionRegistry) -> Self { Self { - input: self.input.substitute(tr), - output: self.output.substitute(tr), + input: self.input.substitute(tr, reg), + output: self.output.substitute(tr, reg), extension_reqs: self.extension_reqs.substitute(tr), } } diff --git a/hugr-core/src/types/type_param.rs b/hugr-core/src/types/type_param.rs index be688ad7c..9d39c327e 100644 --- a/hugr-core/src/types/type_param.rs +++ b/hugr-core/src/types/type_param.rs @@ -295,11 +295,11 @@ impl TypeArg { } } - pub(crate) fn substitute(&self, t: &Substitution) -> Self { + pub(crate) fn substitute(&self, t: &Substitution, reg: &ExtensionRegistry) -> Self { match self { TypeArg::Type { ty } => { // RowVariables are represented as TypeArg::Variable - ty.substitute1(t).into() + ty.substitute1(t, reg).into() } TypeArg::BoundedNat { .. } | TypeArg::String { .. } => self.clone(), // We do not allow variables as bounds on BoundedNat's TypeArg::Sequence { elems } => { @@ -314,7 +314,7 @@ impl TypeArg { // So, anything that doesn't produce a Type, was a row variable => multiple Types elems .iter() - .flat_map(|ta| match ta.substitute(t) { + .flat_map(|ta| match ta.substitute(t, reg) { ty @ TypeArg::Type { .. } => vec![ty], TypeArg::Sequence { elems } => elems, _ => panic!("Expected Type or row of Types"), @@ -323,7 +323,7 @@ impl TypeArg { } _ => { // not types, no need to flatten (and mustn't, in case of nested Sequences) - elems.iter().map(|ta| ta.substitute(t)).collect() + elems.iter().map(|ta| ta.substitute(t, reg)).collect() } }; TypeArg::Sequence { elems } @@ -551,7 +551,7 @@ mod test { }; check_type_arg(&outer_arg, &outer_param).unwrap(); - let outer_arg2 = outer_arg.substitute(&Substitution(&[row_arg], &PRELUDE_REGISTRY)); + let outer_arg2 = outer_arg.substitute(&Substitution(&[row_arg]), &PRELUDE_REGISTRY); assert_eq!( outer_arg2, vec![bool_t().into(), TypeArg::UNIT, usize_t().into()].into() @@ -594,7 +594,7 @@ mod test { let row_var_arg = vec![usize_t().into(), bool_t().into()].into(); check_type_arg(&row_var_arg, &row_var_decl).unwrap(); let subst_arg = - good_arg.substitute(&Substitution(&[row_var_arg.clone()], &PRELUDE_REGISTRY)); + good_arg.substitute(&Substitution(&[row_var_arg.clone()]), &PRELUDE_REGISTRY); check_type_arg(&subst_arg, &outer_param).unwrap(); // invariance of substitution assert_eq!( subst_arg, diff --git a/hugr-core/src/types/type_row.rs b/hugr-core/src/types/type_row.rs index c807b872f..8dfb2a434 100644 --- a/hugr-core/src/types/type_row.rs +++ b/hugr-core/src/types/type_row.rs @@ -71,9 +71,9 @@ impl TypeRowBase { /// Applies a substitution to the row. /// For `TypeRowRV`, note this may change the length of the row. /// For `TypeRow`, guaranteed not to change the length of the row. - pub(crate) fn substitute(&self, s: &Substitution) -> Self { + pub(crate) fn substitute(&self, s: &Substitution, reg: &ExtensionRegistry) -> Self { self.iter() - .flat_map(|ty| ty.substitute(s)) + .flat_map(|ty| ty.substitute(s, reg)) .collect::>() .into() } diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index d25eff8cd..f0b3dd94c 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -20,8 +20,8 @@ use itertools::Itertools as _; /// /// If the Hugr is [FuncDefn](OpType::FuncDefn)-rooted with polymorphic /// signature then the hugr is untouched. -pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { - let validate = |h: &Hugr| h.validate(reg).unwrap_or_else(|e| panic!("{e}")); +pub fn monomorphize(mut h: Hugr) -> Hugr { + let validate = |h: &Hugr| h.validate().unwrap_or_else(|e| panic!("{e}")); #[cfg(debug_assertions)] validate(&h); @@ -29,7 +29,7 @@ pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { let root = h.root(); // 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(&mut h, root, None, &mut HashMap::new(), reg); + mono_scan(&mut h, root, None, &mut HashMap::new()); if !h.get_optype(root).is_module() { return remove_polyfuncs(h); } @@ -86,7 +86,6 @@ fn mono_scan( parent: Node, mut subst_into: Option<&mut Instantiating>, cache: &mut Instantiations, - reg: &ExtensionRegistry, ) { for old_ch in h.children(parent).collect_vec() { let ch_op = h.get_optype(old_ch); @@ -97,17 +96,17 @@ fn mono_scan( // Perform substitution, and recurse into containers (mono_scan does nothing if no children) let ch = if let Some(ref mut inst) = subst_into { let new_ch = - h.add_node_with_parent(inst.target_container, ch_op.substitute(inst.subst)); + h.add_node_with_parent(inst.target_container, ch_op.substitute(inst.subst, h.extensions())); inst.node_map.insert(old_ch, new_ch); let mut inst = Instantiating { target_container: new_ch, node_map: inst.node_map, ..**inst }; - mono_scan(h, old_ch, Some(&mut inst), cache, reg); + mono_scan(h, old_ch, Some(&mut inst), cache); new_ch } else { - mono_scan(h, old_ch, None, cache, reg); + mono_scan(h, old_ch, None, cache); old_ch }; @@ -119,7 +118,7 @@ fn mono_scan( ( &c.type_args, mono_sig.clone(), - OpType::from(Call::try_new(mono_sig.into(), [], reg).unwrap()), + OpType::from(Call::try_new(mono_sig.into(), [], h.extensions()).unwrap()), ) } OpType::LoadFunction(lf) => { @@ -128,7 +127,7 @@ fn mono_scan( ( &lf.type_args, mono_sig.clone(), - LoadFunction::try_new(mono_sig.into(), [], reg) + LoadFunction::try_new(mono_sig.into(), [], h.extensions()) .unwrap() .into(), ) @@ -140,7 +139,7 @@ fn mono_scan( }; let fn_inp = ch_op.static_input_port().unwrap(); let tgt = h.static_source(old_ch).unwrap(); // Use old_ch as edges not copied yet - let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); + let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache); let fn_out = { let func = h.get_optype(new_tgt).as_func_defn().unwrap(); debug_assert_eq!(func.signature, mono_sig.into()); @@ -159,7 +158,6 @@ fn instantiate( type_args: Vec, mono_sig: Signature, cache: &mut Instantiations, - reg: &ExtensionRegistry, ) -> Node { let for_func = cache.entry(poly_func).or_insert_with(|| { // First time we've instantiated poly_func. Lift any nested FuncDefn's out to the same level. @@ -201,11 +199,11 @@ fn instantiate( // Now make the instantiation let mut node_map = HashMap::new(); let mut inst = Instantiating { - subst: &Substitution::new(&type_args, reg), + subst: &Substitution::new(&type_args), target_container: mono_tgt, node_map: &mut node_map, }; - mono_scan(h, poly_func, Some(&mut inst), cache, reg); + mono_scan(h, poly_func, Some(&mut inst), cache); // Copy edges...we have built a node_map for every node in the function. // Note we could avoid building the "large" map (smaller than the Hugr we've just created) // by doing this during recursion, but we'd need to be careful with nonlocal edges - @@ -324,8 +322,8 @@ mod test { let dfg_builder = DFGBuilder::new(Signature::new(vec![usize_t()], vec![usize_t()])).unwrap(); let [i1] = dfg_builder.input_wires_arr(); - let hugr = dfg_builder.finish_prelude_hugr_with_outputs([i1]).unwrap(); - let hugr2 = monomorphize(hugr.clone(), &PRELUDE_REGISTRY); + let hugr = dfg_builder.finish_hugr_with_outputs([i1]).unwrap(); + let hugr2 = monomorphize(hugr.clone()); assert_eq!(hugr, hugr2); } @@ -348,7 +346,6 @@ mod test { &FuncID::::from(fb.container_node()), &[tv0().into()], [elem], - &EMPTY_REG, )?; fb.finish_with_outputs(tag.outputs())? }; @@ -360,7 +357,7 @@ mod test { PolyFuncType::new([TypeBound::Copyable.into()], sig), )?; let [elem] = fb.input_wires_arr(); - let pair = fb.call(db.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY)?; + let pair = fb.call(db.handle(), &[tv0().into()], [elem])?; let [elem1, elem2] = fb .add_dataflow_op(UnpackTuple::new(vec![tv0(); 2].into()), pair.outputs())? @@ -374,24 +371,24 @@ mod test { let mut fb = mb.define_function("main", prelusig(usize_t(), outs))?; let [elem] = fb.input_wires_arr(); let [res1] = fb - .call(tr.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)? + .call(tr.handle(), &[usize_t().into()], [elem])? .outputs_arr(); - let pair = fb.call(db.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)?; + let pair = fb.call(db.handle(), &[usize_t().into()], [elem])?; let pty = pair_type(usize_t()).into(); let [res2] = fb - .call(tr.handle(), &[pty], pair.outputs(), &PRELUDE_REGISTRY)? + .call(tr.handle(), &[pty], pair.outputs())? .outputs_arr(); fb.finish_with_outputs([res1, res2])?; } - let hugr = mb.finish_hugr(&PRELUDE_REGISTRY)?; + let hugr = mb.finish_hugr()?; assert_eq!( hugr.nodes() .filter(|n| hugr.get_optype(*n).is_func_defn()) .count(), 3 ); - let mono = monomorphize(hugr, &PRELUDE_REGISTRY); - mono.validate(&PRELUDE_REGISTRY)?; + let mono = monomorphize(hugr); + mono.validate()?; let mut funcs = list_funcs(&mono); let expected_mangled_names = [ @@ -410,7 +407,7 @@ mod test { ["double", "main", "triple"] ); - assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent + assert_eq!(monomorphize(mono.clone()), mono); // Idempotent let nopoly = remove_polyfuncs(mono); let mut funcs = list_funcs(&nopoly); @@ -427,8 +424,6 @@ mod test { fn test_flattening() -> Result<(), Box> { //pf1 contains pf2 contains mono_func -> pf1 and pf1 share pf2's and they share mono_func - let reg = - ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; let tv0 = || Type::new_var_use(0, TypeBound::Any); let ity = || INT_TYPES[3].clone(); @@ -448,28 +443,28 @@ mod test { }; let pf2 = { let [inw] = pf2.input_wires_arr(); - let [usz] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); + let [usz] = pf2.call(mono_func.handle(), &[], [])?.outputs_arr(); pf2.finish_with_outputs([inw, usz])? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not let [a, u] = pf1 - .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®)? + .call(pf2.handle(), &[tv0().into()], pf1.input_wires())? .outputs_arr(); let [u1, u2] = pf1 - .call(pf2.handle(), &[usize_t().into()], [u], ®)? + .call(pf2.handle(), &[usize_t().into()], [u])? .outputs_arr(); let pf1 = pf1.finish_with_outputs([a, u1, u2])?; // Outer: two calls to pf1 with different TypeArgs let [_, u, _] = outer - .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®)? + .call(pf1.handle(), &[ity().into()], outer.input_wires())? .outputs_arr(); let [_, u, _] = outer - .call(pf1.handle(), &[usize_t().into()], [u], ®)? + .call(pf1.handle(), &[usize_t().into()], [u])? .outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([u], ®)?; + let hugr = outer.finish_hugr_with_outputs([u])?; - let mono_hugr = monomorphize(hugr, ®); - mono_hugr.validate(®)?; + let mono_hugr = monomorphize(hugr); + mono_hugr.validate()?; let funcs = list_funcs(&mono_hugr); let pf2_name = mangle_inner_func("pf1", "pf2"); assert_eq!( @@ -502,8 +497,6 @@ mod test { #[test] fn test_no_flatten_out_of_mono_func() -> Result<(), Box> { let ity = || INT_TYPES[4].clone(); - let reg = - ExtensionRegistry::try_new([PRELUDE.to_owned(), int_types::EXTENSION.to_owned()])?; let sig = Signature::new_endo(vec![usize_t(), ity()]); let mut dfg = DFGBuilder::new(sig.clone())?; let mut mono = dfg.define_function("id2", sig)?; @@ -518,15 +511,15 @@ mod test { let pf = pf.finish_with_outputs(outs)?; let [a, b] = mono.input_wires_arr(); let [a] = mono - .call(pf.handle(), &[usize_t().into()], [a], ®)? + .call(pf.handle(), &[usize_t().into()], [a])? .outputs_arr(); let [b] = mono - .call(pf.handle(), &[ity().into()], [b], ®)? + .call(pf.handle(), &[ity().into()], [b])? .outputs_arr(); let mono = mono.finish_with_outputs([a, b])?; - let c = dfg.call(mono.handle(), &[], dfg.input_wires(), ®)?; - let hugr = dfg.finish_hugr_with_outputs(c.outputs(), ®)?; - let mono_hugr = monomorphize(hugr, ®); + let c = dfg.call(mono.handle(), &[], dfg.input_wires())?; + let hugr = dfg.finish_hugr_with_outputs(c.outputs())?; + let mono_hugr = monomorphize(hugr); let mut funcs = list_funcs(&mono_hugr); assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); @@ -564,7 +557,7 @@ mod test { .define_function("main", Signature::new_endo(Type::UNIT)) .unwrap(); let func_ptr = builder - .load_func(foo.handle(), &[Type::UNIT.into()], &EMPTY_REG) + .load_func(foo.handle(), &[Type::UNIT.into()]) .unwrap(); let [r] = builder .call_indirect( @@ -576,10 +569,10 @@ mod test { .outputs_arr(); builder.finish_with_outputs([r]).unwrap() }; - module_builder.finish_hugr(&EMPTY_REG).unwrap() + module_builder.finish_hugr().unwrap() }; - let mono_hugr = remove_polyfuncs(monomorphize(hugr, &EMPTY_REG)); + let mono_hugr = remove_polyfuncs(monomorphize(hugr)); let funcs = list_funcs(&mono_hugr); assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); From 43c2f3e4f9c6cab56bd2c1f9e0ef93523aab7d67 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 07:34:57 +0000 Subject: [PATCH 51/63] Revert "wip" This reverts commit 703004e5d008eb1b12e81b65bd44904b732fc965. --- devenv.lock | 69 +++++++++++++------------- hugr-core/src/ops.rs | 5 +- hugr-core/src/ops/controlflow.rs | 54 ++++++++++----------- hugr-core/src/ops/custom.rs | 14 +++--- hugr-core/src/ops/dataflow.rs | 40 +++++++-------- hugr-core/src/ops/sum.rs | 5 +- hugr-core/src/types.rs | 22 +++++---- hugr-core/src/types/custom.rs | 6 +-- hugr-core/src/types/poly_func.rs | 2 +- hugr-core/src/types/signature.rs | 6 +-- hugr-core/src/types/type_param.rs | 12 ++--- hugr-core/src/types/type_row.rs | 4 +- hugr-passes/src/monomorphize.rs | 81 +++++++++++++++++-------------- 13 files changed, 163 insertions(+), 157 deletions(-) diff --git a/devenv.lock b/devenv.lock index 4b625b566..c326bd43b 100644 --- a/devenv.lock +++ b/devenv.lock @@ -39,10 +39,10 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1733328505, + "lastModified": 1696426674, "owner": "edolstra", "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -51,32 +51,10 @@ "type": "github" } }, - "git-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable" - }, - "locked": { - "lastModified": 1733665616, - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "d8c02f0ffef0ef39f6063731fc539d8c71eb463a", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, "gitignore": { "inputs": { "nixpkgs": [ - "git-hooks", + "pre-commit-hooks", "nixpkgs" ] }, @@ -110,44 +88,63 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1733730953, + "lastModified": 1704290814, "owner": "NixOS", "repo": "nixpkgs", - "rev": "7109b680d161993918b0a126f38bc39763e5a709", + "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-24.05", + "ref": "nixos-23.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-stable_2": { "locked": { - "lastModified": 1704290814, + "lastModified": 1728193676, "owner": "NixOS", "repo": "nixpkgs", - "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", + "rev": "ecbc1ca8ffd6aea8372ad16be9ebbb39889e55b6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_2" + }, + "locked": { + "lastModified": 1728092656, + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "1211305a5b237771e13fcca0c51e60ad47326a9a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "devenv": "devenv", "fenix": "fenix", - "git-hooks": "git-hooks", "nixpkgs": "nixpkgs", - "nixpkgs-stable": "nixpkgs-stable_2", - "pre-commit-hooks": [ - "git-hooks" - ] + "nixpkgs-stable": "nixpkgs-stable", + "pre-commit-hooks": "pre-commit-hooks" } }, "rust-analyzer-src": { diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index 312127e84..a2ff0eed8 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -15,8 +15,9 @@ use crate::extension::resolution::{ use std::borrow::Cow; use crate::extension::simple_op::MakeExtensionOp; -use crate::extension::{ExtensionId, ExtensionSet, ExtensionRegistry}; +use crate::extension::{ExtensionId, ExtensionSet}; use crate::types::{EdgeKind, Signature, Substitution}; +use crate::extension::{ExtensionId, ExtensionRegistry, ExtensionSet}; use crate::{Direction, OutgoingPort, Port}; use crate::{IncomingPort, PortIndex}; use derive_more::Display; @@ -436,7 +437,7 @@ pub trait OpTrait: Sized + Clone { /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](crate::types::TypeArg::new_var_use) with new types. - fn substitute(&self, _subst: &Substitution, _reg: &ExtensionRegistry) -> Self { + fn substitute(&self, _subst: &Substitution) -> Self { self.clone() } } diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index fcf841acb..44c78eae3 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; -use crate::extension::{ExtensionRegistry, ExtensionSet}; +use crate::extension::ExtensionSet; use crate::types::{EdgeKind, Signature, Type, TypeRow}; use crate::Direction; @@ -42,11 +42,11 @@ impl DataflowOpTrait for TailLoop { ) } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - just_inputs: self.just_inputs.substitute(subst, reg), - just_outputs: self.just_outputs.substitute(subst, reg), - rest: self.rest.substitute(subst, reg), + just_inputs: self.just_inputs.substitute(subst), + just_outputs: self.just_outputs.substitute(subst), + rest: self.rest.substitute(subst), extension_delta: self.extension_delta.substitute(subst), } } @@ -121,11 +121,11 @@ impl DataflowOpTrait for Conditional { ) } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst, reg)).collect(), - other_inputs: self.other_inputs.substitute(subst, reg), - outputs: self.outputs.substitute(subst, reg), + sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(), + other_inputs: self.other_inputs.substitute(subst), + outputs: self.outputs.substitute(subst), extension_delta: self.extension_delta.substitute(subst), } } @@ -159,9 +159,9 @@ impl DataflowOpTrait for CFG { Cow::Borrowed(&self.signature) } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - signature: self.signature.substitute(subst, reg), + signature: self.signature.substitute(subst), } } } @@ -248,11 +248,11 @@ impl OpTrait for DataflowBlock { } } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - inputs: self.inputs.substitute(subst, reg), - other_outputs: self.other_outputs.substitute(subst, reg), - sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst, reg)).collect(), + inputs: self.inputs.substitute(subst), + other_outputs: self.other_outputs.substitute(subst), + sum_rows: self.sum_rows.iter().map(|r| r.substitute(subst)).collect(), extension_delta: self.extension_delta.substitute(subst), } } @@ -282,9 +282,9 @@ impl OpTrait for ExitBlock { } } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - cfg_outputs: self.cfg_outputs.substitute(subst, reg), + cfg_outputs: self.cfg_outputs.substitute(subst), } } } @@ -351,9 +351,9 @@ impl OpTrait for Case { ::TAG } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - signature: self.signature.substitute(subst, reg), + signature: self.signature.substitute(subst), } } } @@ -372,8 +372,6 @@ impl Case { #[cfg(test)] mod test { - use std::borrow::Borrow; - use crate::{ extension::{ prelude::{qb_t, usize_t, PRELUDE_ID}, @@ -401,9 +399,9 @@ mod test { TypeArg::Extensions { es: PRELUDE_ID.into(), }, - ]), + ], &PRELUDE_REGISTRY, - ); + )); let st = Type::new_sum(vec![vec![usize_t()], vec![qb_t(); 2]]); assert_eq!( dfb2.inner_signature(), @@ -427,9 +425,9 @@ mod test { elems: vec![usize_t().into(); 3], }, qb_t().into(), - ]), + ], &PRELUDE_REGISTRY, - ); + )); let st = Type::new_sum(vec![usize_t(), qb_t()]); //both single-element variants assert_eq!( cond2.signature(), @@ -455,12 +453,12 @@ mod test { TypeArg::Extensions { es: PRELUDE_ID.into(), }, - ]), + ], &PRELUDE_REGISTRY, - ); + )); assert_eq!( tail2.signature(), - ow r::new( + Signature::new( vec![qb_t(), usize_t(), usize_t()], vec![usize_t(), qb_t(), usize_t()] ) diff --git a/hugr-core/src/ops/custom.rs b/hugr-core/src/ops/custom.rs index 7c91595fe..a0ad220e7 100644 --- a/hugr-core/src/ops/custom.rs +++ b/hugr-core/src/ops/custom.rs @@ -167,16 +167,16 @@ impl DataflowOpTrait for ExtensionOp { Cow::Borrowed(&self.signature) } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { let args = self .args .iter() - .map(|ta| ta.substitute(subst, reg)) + .map(|ta| ta.substitute(subst)) .collect::>(); - let signature = self.signature.substitute(subst, reg); + let signature = self.signature.substitute(subst); debug_assert_eq!( self.def - .compute_signature(&args, reg) + .compute_signature(&args, subst.extension_registry()) .as_ref(), Ok(&signature) ); @@ -280,10 +280,10 @@ impl DataflowOpTrait for OpaqueOp { Cow::Borrowed(&self.signature) } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - args: self.args.iter().map(|ta| ta.substitute(subst, reg)).collect(), - signature: self.signature.substitute(subst, reg), + args: self.args.iter().map(|ta| ta.substitute(subst)).collect(), + signature: self.signature.substitute(subst), ..self.clone() } } diff --git a/hugr-core/src/ops/dataflow.rs b/hugr-core/src/ops/dataflow.rs index dfc65e18a..9e6208b65 100644 --- a/hugr-core/src/ops/dataflow.rs +++ b/hugr-core/src/ops/dataflow.rs @@ -54,7 +54,7 @@ pub trait DataflowOpTrait: Sized { /// Apply a type-level substitution to this OpType, i.e. replace /// [type variables](TypeArg::new_var_use) with new types. - fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self; + fn substitute(&self, _subst: &Substitution) -> Self; } /// Helpers to construct input and output nodes @@ -116,9 +116,9 @@ impl DataflowOpTrait for Input { Cow::Owned(Signature::new(TypeRow::new(), self.types.clone())) } - fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &Substitution) -> Self { Self { - types: self.types.substitute(subst, reg), + types: self.types.substitute(subst), } } } @@ -140,9 +140,9 @@ impl DataflowOpTrait for Output { None } - fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &Substitution) -> Self { Self { - types: self.types.substitute(subst, reg), + types: self.types.substitute(subst), } } } @@ -172,8 +172,8 @@ impl OpTrait for T { DataflowOpTrait::static_input(self) } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { - DataflowOpTrait::substitute(self, subst, reg) + fn substitute(&self, subst: &crate::types::Substitution) -> Self { + DataflowOpTrait::substitute(self, subst) } } impl StaticTag for T { @@ -212,16 +212,16 @@ impl DataflowOpTrait for Call { Some(EdgeKind::Function(self.called_function_type().clone())) } - fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &Substitution) -> Self { let type_args = self .type_args .iter() - .map(|ta| ta.substitute(subst, reg)) + .map(|ta| ta.substitute(subst)) .collect::>(); - let instantiation = self.instantiation.substitute(subst, reg); + let instantiation = self.instantiation.substitute(subst); debug_assert_eq!( self.func_sig - .instantiate(&type_args, reg) + .instantiate(&type_args, subst.extension_registry()) .as_ref(), Ok(&instantiation) ); @@ -325,9 +325,9 @@ impl DataflowOpTrait for CallIndirect { Cow::Owned(s) } - fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &Substitution) -> Self { Self { - signature: self.signature.substitute(subst, reg), + signature: self.signature.substitute(subst), } } } @@ -356,7 +356,7 @@ impl DataflowOpTrait for LoadConstant { Some(EdgeKind::Const(self.constant_type().clone())) } - fn substitute(&self, _subst: &Substitution, _reg: &ExtensionRegistry) -> Self { + fn substitute(&self, _subst: &Substitution) -> Self { // Constants cannot refer to TypeArgs, so neither can loading them self.clone() } @@ -420,16 +420,16 @@ impl DataflowOpTrait for LoadFunction { Some(EdgeKind::Function(self.func_sig.clone())) } - fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &Substitution) -> Self { let type_args = self .type_args .iter() - .map(|ta| ta.substitute(subst, reg)) + .map(|ta| ta.substitute(subst)) .collect::>(); - let instantiation = self.instantiation.substitute(subst, reg); + let instantiation = self.instantiation.substitute(subst); debug_assert_eq!( self.func_sig - .instantiate(&type_args, reg) + .instantiate(&type_args, subst.extension_registry()) .as_ref(), Ok(&instantiation) ); @@ -528,9 +528,9 @@ impl DataflowOpTrait for DFG { self.inner_signature() } - fn substitute(&self, subst: &Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &Substitution) -> Self { Self { - signature: self.signature.substitute(subst, reg), + signature: self.signature.substitute(subst), } } } diff --git a/hugr-core/src/ops/sum.rs b/hugr-core/src/ops/sum.rs index 55b268cb4..7cc9fb577 100644 --- a/hugr-core/src/ops/sum.rs +++ b/hugr-core/src/ops/sum.rs @@ -4,7 +4,6 @@ use std::borrow::Cow; use super::dataflow::DataflowOpTrait; use super::{impl_op_name, OpTag}; -use crate::extension::ExtensionRegistry; use crate::types::{EdgeKind, Signature, Type, TypeRow}; /// An operation that creates a tagged sum value from one of its variants. @@ -57,9 +56,9 @@ impl DataflowOpTrait for Tag { Some(EdgeKind::StateOrder) } - fn substitute(&self, subst: &crate::types::Substitution, reg: &ExtensionRegistry) -> Self { + fn substitute(&self, subst: &crate::types::Substitution) -> Self { Self { - variants: self.variants.iter().map(|r| r.substitute(subst, reg)).collect(), + variants: self.variants.iter().map(|r| r.substitute(subst)).collect(), tag: self.tag, } } diff --git a/hugr-core/src/types.rs b/hugr-core/src/types.rs index dc91fc01e..72aae7b7e 100644 --- a/hugr-core/src/types.rs +++ b/hugr-core/src/types.rs @@ -448,7 +448,7 @@ impl TypeBase { /// * If [Type::validate]`(false)` returns successfully, this method will return a Vec containing exactly one type /// * If [Type::validate]`(false)` fails, but `(true)` succeeds, this method may (depending on structure of self) /// return a Vec containing any number of [Type]s. These may (or not) pass [Type::validate] - fn substitute(&self, t: &Substitution, reg: &ExtensionRegistry) -> Vec { + fn substitute(&self, t: &Substitution) -> Vec { match &self.0 { TypeEnum::RowVar(rv) => rv.substitute(t), TypeEnum::Alias(_) | TypeEnum::Sum(SumType::Unit { .. }) => vec![self.clone()], @@ -458,18 +458,18 @@ impl TypeBase { }; vec![ty.into_()] } - TypeEnum::Extension(cty) => vec![TypeBase::new_extension(cty.substitute(t, reg))], - TypeEnum::Function(bf) => vec![TypeBase::new_function(bf.substitute(t, reg))], + TypeEnum::Extension(cty) => vec![TypeBase::new_extension(cty.substitute(t))], + TypeEnum::Function(bf) => vec![TypeBase::new_function(bf.substitute(t))], TypeEnum::Sum(SumType::General { rows }) => { - vec![TypeBase::new_sum(rows.iter().map(|r| r.substitute(t, reg)))] + vec![TypeBase::new_sum(rows.iter().map(|r| r.substitute(t)))] } } } } impl Type { - fn substitute1(&self, s: &Substitution, reg: &ExtensionRegistry) -> Self { - let v = self.substitute(s, reg); + fn substitute1(&self, s: &Substitution) -> Self { + let v = self.substitute(s); let [r] = v.try_into().unwrap(); // No row vars, so every Type produces exactly one r } @@ -548,7 +548,7 @@ impl From for TypeRV { /// Details a replacement of type variables with a finite list of known values. /// (Variables out of the range of the list will result in a panic) -pub struct Substitution<'a>(&'a [TypeArg]); +pub struct Substitution<'a>(&'a [TypeArg], &'a ExtensionRegistry); impl<'a> Substitution<'a> { /// Create a new Substitution given the replacement values (indexed @@ -557,8 +557,8 @@ impl<'a> Substitution<'a> { /// containing a type-variable. /// /// [TypeDef]: crate::extension::TypeDef - pub fn new(items: &'a [TypeArg]) -> Self { - Self(items) + pub fn new(items: &'a [TypeArg], exts: &'a ExtensionRegistry) -> Self { + Self(items, exts) } pub(crate) fn apply_var(&self, idx: usize, decl: &TypeParam) -> TypeArg { @@ -599,6 +599,10 @@ impl<'a> Substitution<'a> { _ => panic!("Not a type or list of types - call validate() ?"), } } + + pub(crate) fn extension_registry(&self) -> &ExtensionRegistry { + self.1 + } } pub(crate) fn check_typevar_decl( diff --git a/hugr-core/src/types/custom.rs b/hugr-core/src/types/custom.rs index f1a08098f..81bc814ac 100644 --- a/hugr-core/src/types/custom.rs +++ b/hugr-core/src/types/custom.rs @@ -109,14 +109,14 @@ impl CustomType { }) } - pub(super) fn substitute(&self, tr: &Substitution, reg: &ExtensionRegistry) -> Self { + pub(super) fn substitute(&self, tr: &Substitution) -> Self { let args = self .args .iter() - .map(|arg| arg.substitute(tr, reg)) + .map(|arg| arg.substitute(tr)) .collect::>(); let bound = self - .get_type_def(reg) + .get_type_def(tr.extension_registry()) .expect("validate should rule this out") .bound(&args); debug_assert!(self.bound.contains(bound)); diff --git a/hugr-core/src/types/poly_func.rs b/hugr-core/src/types/poly_func.rs index 23d65453a..66dff8245 100644 --- a/hugr-core/src/types/poly_func.rs +++ b/hugr-core/src/types/poly_func.rs @@ -125,7 +125,7 @@ impl PolyFuncTypeBase { // Check that args are applicable, and that we have a value for each binder, // i.e. each possible free variable within the body. check_type_args(args, &self.params)?; - Ok(self.body.substitute(&Substitution(args), ext_reg)) + Ok(self.body.substitute(&Substitution(args, ext_reg))) } /// Validates this instance, checking that the types in the body are diff --git a/hugr-core/src/types/signature.rs b/hugr-core/src/types/signature.rs index 2112acd34..c5d3459e1 100644 --- a/hugr-core/src/types/signature.rs +++ b/hugr-core/src/types/signature.rs @@ -61,10 +61,10 @@ impl FuncTypeBase { self.with_extension_delta(crate::extension::prelude::PRELUDE_ID) } - pub(crate) fn substitute(&self, tr: &Substitution, reg: &ExtensionRegistry) -> Self { + pub(crate) fn substitute(&self, tr: &Substitution) -> Self { Self { - input: self.input.substitute(tr, reg), - output: self.output.substitute(tr, reg), + input: self.input.substitute(tr), + output: self.output.substitute(tr), extension_reqs: self.extension_reqs.substitute(tr), } } diff --git a/hugr-core/src/types/type_param.rs b/hugr-core/src/types/type_param.rs index 9d39c327e..be688ad7c 100644 --- a/hugr-core/src/types/type_param.rs +++ b/hugr-core/src/types/type_param.rs @@ -295,11 +295,11 @@ impl TypeArg { } } - pub(crate) fn substitute(&self, t: &Substitution, reg: &ExtensionRegistry) -> Self { + pub(crate) fn substitute(&self, t: &Substitution) -> Self { match self { TypeArg::Type { ty } => { // RowVariables are represented as TypeArg::Variable - ty.substitute1(t, reg).into() + ty.substitute1(t).into() } TypeArg::BoundedNat { .. } | TypeArg::String { .. } => self.clone(), // We do not allow variables as bounds on BoundedNat's TypeArg::Sequence { elems } => { @@ -314,7 +314,7 @@ impl TypeArg { // So, anything that doesn't produce a Type, was a row variable => multiple Types elems .iter() - .flat_map(|ta| match ta.substitute(t, reg) { + .flat_map(|ta| match ta.substitute(t) { ty @ TypeArg::Type { .. } => vec![ty], TypeArg::Sequence { elems } => elems, _ => panic!("Expected Type or row of Types"), @@ -323,7 +323,7 @@ impl TypeArg { } _ => { // not types, no need to flatten (and mustn't, in case of nested Sequences) - elems.iter().map(|ta| ta.substitute(t, reg)).collect() + elems.iter().map(|ta| ta.substitute(t)).collect() } }; TypeArg::Sequence { elems } @@ -551,7 +551,7 @@ mod test { }; check_type_arg(&outer_arg, &outer_param).unwrap(); - let outer_arg2 = outer_arg.substitute(&Substitution(&[row_arg]), &PRELUDE_REGISTRY); + let outer_arg2 = outer_arg.substitute(&Substitution(&[row_arg], &PRELUDE_REGISTRY)); assert_eq!( outer_arg2, vec![bool_t().into(), TypeArg::UNIT, usize_t().into()].into() @@ -594,7 +594,7 @@ mod test { let row_var_arg = vec![usize_t().into(), bool_t().into()].into(); check_type_arg(&row_var_arg, &row_var_decl).unwrap(); let subst_arg = - good_arg.substitute(&Substitution(&[row_var_arg.clone()]), &PRELUDE_REGISTRY); + good_arg.substitute(&Substitution(&[row_var_arg.clone()], &PRELUDE_REGISTRY)); check_type_arg(&subst_arg, &outer_param).unwrap(); // invariance of substitution assert_eq!( subst_arg, diff --git a/hugr-core/src/types/type_row.rs b/hugr-core/src/types/type_row.rs index 8dfb2a434..c807b872f 100644 --- a/hugr-core/src/types/type_row.rs +++ b/hugr-core/src/types/type_row.rs @@ -71,9 +71,9 @@ impl TypeRowBase { /// Applies a substitution to the row. /// For `TypeRowRV`, note this may change the length of the row. /// For `TypeRow`, guaranteed not to change the length of the row. - pub(crate) fn substitute(&self, s: &Substitution, reg: &ExtensionRegistry) -> Self { + pub(crate) fn substitute(&self, s: &Substitution) -> Self { self.iter() - .flat_map(|ty| ty.substitute(s, reg)) + .flat_map(|ty| ty.substitute(s)) .collect::>() .into() } diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index f0b3dd94c..d25eff8cd 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -20,8 +20,8 @@ use itertools::Itertools as _; /// /// If the Hugr is [FuncDefn](OpType::FuncDefn)-rooted with polymorphic /// signature then the hugr is untouched. -pub fn monomorphize(mut h: Hugr) -> Hugr { - let validate = |h: &Hugr| h.validate().unwrap_or_else(|e| panic!("{e}")); +pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { + let validate = |h: &Hugr| h.validate(reg).unwrap_or_else(|e| panic!("{e}")); #[cfg(debug_assertions)] validate(&h); @@ -29,7 +29,7 @@ pub fn monomorphize(mut h: Hugr) -> Hugr { let root = h.root(); // 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(&mut h, root, None, &mut HashMap::new()); + mono_scan(&mut h, root, None, &mut HashMap::new(), reg); if !h.get_optype(root).is_module() { return remove_polyfuncs(h); } @@ -86,6 +86,7 @@ fn mono_scan( parent: Node, mut subst_into: Option<&mut Instantiating>, cache: &mut Instantiations, + reg: &ExtensionRegistry, ) { for old_ch in h.children(parent).collect_vec() { let ch_op = h.get_optype(old_ch); @@ -96,17 +97,17 @@ fn mono_scan( // Perform substitution, and recurse into containers (mono_scan does nothing if no children) let ch = if let Some(ref mut inst) = subst_into { let new_ch = - h.add_node_with_parent(inst.target_container, ch_op.substitute(inst.subst, h.extensions())); + h.add_node_with_parent(inst.target_container, ch_op.substitute(inst.subst)); inst.node_map.insert(old_ch, new_ch); let mut inst = Instantiating { target_container: new_ch, node_map: inst.node_map, ..**inst }; - mono_scan(h, old_ch, Some(&mut inst), cache); + mono_scan(h, old_ch, Some(&mut inst), cache, reg); new_ch } else { - mono_scan(h, old_ch, None, cache); + mono_scan(h, old_ch, None, cache, reg); old_ch }; @@ -118,7 +119,7 @@ fn mono_scan( ( &c.type_args, mono_sig.clone(), - OpType::from(Call::try_new(mono_sig.into(), [], h.extensions()).unwrap()), + OpType::from(Call::try_new(mono_sig.into(), [], reg).unwrap()), ) } OpType::LoadFunction(lf) => { @@ -127,7 +128,7 @@ fn mono_scan( ( &lf.type_args, mono_sig.clone(), - LoadFunction::try_new(mono_sig.into(), [], h.extensions()) + LoadFunction::try_new(mono_sig.into(), [], reg) .unwrap() .into(), ) @@ -139,7 +140,7 @@ fn mono_scan( }; let fn_inp = ch_op.static_input_port().unwrap(); let tgt = h.static_source(old_ch).unwrap(); // Use old_ch as edges not copied yet - let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache); + let new_tgt = instantiate(h, tgt, type_args.clone(), mono_sig.clone(), cache, reg); let fn_out = { let func = h.get_optype(new_tgt).as_func_defn().unwrap(); debug_assert_eq!(func.signature, mono_sig.into()); @@ -158,6 +159,7 @@ fn instantiate( type_args: Vec, mono_sig: Signature, cache: &mut Instantiations, + reg: &ExtensionRegistry, ) -> Node { let for_func = cache.entry(poly_func).or_insert_with(|| { // First time we've instantiated poly_func. Lift any nested FuncDefn's out to the same level. @@ -199,11 +201,11 @@ fn instantiate( // Now make the instantiation let mut node_map = HashMap::new(); let mut inst = Instantiating { - subst: &Substitution::new(&type_args), + subst: &Substitution::new(&type_args, reg), target_container: mono_tgt, node_map: &mut node_map, }; - mono_scan(h, poly_func, Some(&mut inst), cache); + mono_scan(h, poly_func, Some(&mut inst), cache, reg); // Copy edges...we have built a node_map for every node in the function. // Note we could avoid building the "large" map (smaller than the Hugr we've just created) // by doing this during recursion, but we'd need to be careful with nonlocal edges - @@ -322,8 +324,8 @@ mod test { let dfg_builder = DFGBuilder::new(Signature::new(vec![usize_t()], vec![usize_t()])).unwrap(); let [i1] = dfg_builder.input_wires_arr(); - let hugr = dfg_builder.finish_hugr_with_outputs([i1]).unwrap(); - let hugr2 = monomorphize(hugr.clone()); + let hugr = dfg_builder.finish_prelude_hugr_with_outputs([i1]).unwrap(); + let hugr2 = monomorphize(hugr.clone(), &PRELUDE_REGISTRY); assert_eq!(hugr, hugr2); } @@ -346,6 +348,7 @@ mod test { &FuncID::::from(fb.container_node()), &[tv0().into()], [elem], + &EMPTY_REG, )?; fb.finish_with_outputs(tag.outputs())? }; @@ -357,7 +360,7 @@ mod test { PolyFuncType::new([TypeBound::Copyable.into()], sig), )?; let [elem] = fb.input_wires_arr(); - let pair = fb.call(db.handle(), &[tv0().into()], [elem])?; + let pair = fb.call(db.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY)?; let [elem1, elem2] = fb .add_dataflow_op(UnpackTuple::new(vec![tv0(); 2].into()), pair.outputs())? @@ -371,24 +374,24 @@ mod test { let mut fb = mb.define_function("main", prelusig(usize_t(), outs))?; let [elem] = fb.input_wires_arr(); let [res1] = fb - .call(tr.handle(), &[usize_t().into()], [elem])? + .call(tr.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)? .outputs_arr(); - let pair = fb.call(db.handle(), &[usize_t().into()], [elem])?; + let pair = fb.call(db.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)?; let pty = pair_type(usize_t()).into(); let [res2] = fb - .call(tr.handle(), &[pty], pair.outputs())? + .call(tr.handle(), &[pty], pair.outputs(), &PRELUDE_REGISTRY)? .outputs_arr(); fb.finish_with_outputs([res1, res2])?; } - let hugr = mb.finish_hugr()?; + let hugr = mb.finish_hugr(&PRELUDE_REGISTRY)?; assert_eq!( hugr.nodes() .filter(|n| hugr.get_optype(*n).is_func_defn()) .count(), 3 ); - let mono = monomorphize(hugr); - mono.validate()?; + let mono = monomorphize(hugr, &PRELUDE_REGISTRY); + mono.validate(&PRELUDE_REGISTRY)?; let mut funcs = list_funcs(&mono); let expected_mangled_names = [ @@ -407,7 +410,7 @@ mod test { ["double", "main", "triple"] ); - assert_eq!(monomorphize(mono.clone()), mono); // Idempotent + assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent let nopoly = remove_polyfuncs(mono); let mut funcs = list_funcs(&nopoly); @@ -424,6 +427,8 @@ mod test { fn test_flattening() -> Result<(), Box> { //pf1 contains pf2 contains mono_func -> pf1 and pf1 share pf2's and they share mono_func + let reg = + ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; let tv0 = || Type::new_var_use(0, TypeBound::Any); let ity = || INT_TYPES[3].clone(); @@ -443,28 +448,28 @@ mod test { }; let pf2 = { let [inw] = pf2.input_wires_arr(); - let [usz] = pf2.call(mono_func.handle(), &[], [])?.outputs_arr(); + let [usz] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); pf2.finish_with_outputs([inw, usz])? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not let [a, u] = pf1 - .call(pf2.handle(), &[tv0().into()], pf1.input_wires())? + .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®)? .outputs_arr(); let [u1, u2] = pf1 - .call(pf2.handle(), &[usize_t().into()], [u])? + .call(pf2.handle(), &[usize_t().into()], [u], ®)? .outputs_arr(); let pf1 = pf1.finish_with_outputs([a, u1, u2])?; // Outer: two calls to pf1 with different TypeArgs let [_, u, _] = outer - .call(pf1.handle(), &[ity().into()], outer.input_wires())? + .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®)? .outputs_arr(); let [_, u, _] = outer - .call(pf1.handle(), &[usize_t().into()], [u])? + .call(pf1.handle(), &[usize_t().into()], [u], ®)? .outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([u])?; + let hugr = outer.finish_hugr_with_outputs([u], ®)?; - let mono_hugr = monomorphize(hugr); - mono_hugr.validate()?; + let mono_hugr = monomorphize(hugr, ®); + mono_hugr.validate(®)?; let funcs = list_funcs(&mono_hugr); let pf2_name = mangle_inner_func("pf1", "pf2"); assert_eq!( @@ -497,6 +502,8 @@ mod test { #[test] fn test_no_flatten_out_of_mono_func() -> Result<(), Box> { let ity = || INT_TYPES[4].clone(); + let reg = + ExtensionRegistry::try_new([PRELUDE.to_owned(), int_types::EXTENSION.to_owned()])?; let sig = Signature::new_endo(vec![usize_t(), ity()]); let mut dfg = DFGBuilder::new(sig.clone())?; let mut mono = dfg.define_function("id2", sig)?; @@ -511,15 +518,15 @@ mod test { let pf = pf.finish_with_outputs(outs)?; let [a, b] = mono.input_wires_arr(); let [a] = mono - .call(pf.handle(), &[usize_t().into()], [a])? + .call(pf.handle(), &[usize_t().into()], [a], ®)? .outputs_arr(); let [b] = mono - .call(pf.handle(), &[ity().into()], [b])? + .call(pf.handle(), &[ity().into()], [b], ®)? .outputs_arr(); let mono = mono.finish_with_outputs([a, b])?; - let c = dfg.call(mono.handle(), &[], dfg.input_wires())?; - let hugr = dfg.finish_hugr_with_outputs(c.outputs())?; - let mono_hugr = monomorphize(hugr); + let c = dfg.call(mono.handle(), &[], dfg.input_wires(), ®)?; + let hugr = dfg.finish_hugr_with_outputs(c.outputs(), ®)?; + let mono_hugr = monomorphize(hugr, ®); let mut funcs = list_funcs(&mono_hugr); assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); @@ -557,7 +564,7 @@ mod test { .define_function("main", Signature::new_endo(Type::UNIT)) .unwrap(); let func_ptr = builder - .load_func(foo.handle(), &[Type::UNIT.into()]) + .load_func(foo.handle(), &[Type::UNIT.into()], &EMPTY_REG) .unwrap(); let [r] = builder .call_indirect( @@ -569,10 +576,10 @@ mod test { .outputs_arr(); builder.finish_with_outputs([r]).unwrap() }; - module_builder.finish_hugr().unwrap() + module_builder.finish_hugr(&EMPTY_REG).unwrap() }; - let mono_hugr = remove_polyfuncs(monomorphize(hugr)); + let mono_hugr = remove_polyfuncs(monomorphize(hugr, &EMPTY_REG)); let funcs = list_funcs(&mono_hugr); assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); From 0adb4323ebfa2897ea3a04dcf29d7f5dc6f29555 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 07:39:12 +0000 Subject: [PATCH 52/63] fix up merge --- hugr-core/src/ops.rs | 1 - hugr-passes/src/monomorphize.rs | 70 ++++++++++++++++----------------- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index a2ff0eed8..dd42b2a18 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -15,7 +15,6 @@ use crate::extension::resolution::{ use std::borrow::Cow; use crate::extension::simple_op::MakeExtensionOp; -use crate::extension::{ExtensionId, ExtensionSet}; use crate::types::{EdgeKind, Signature, Substitution}; use crate::extension::{ExtensionId, ExtensionRegistry, ExtensionSet}; use crate::{Direction, OutgoingPort, Port}; diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index d25eff8cd..903936324 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -20,8 +20,9 @@ use itertools::Itertools as _; /// /// If the Hugr is [FuncDefn](OpType::FuncDefn)-rooted with polymorphic /// signature then the hugr is untouched. -pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { - let validate = |h: &Hugr| h.validate(reg).unwrap_or_else(|e| panic!("{e}")); +pub fn monomorphize(mut h: Hugr) -> Hugr { + let validate = |h: &Hugr| h.validate().unwrap_or_else(|e| panic!("{e}")); + let reg = h.extensions().to_owned(); #[cfg(debug_assertions)] validate(&h); @@ -29,7 +30,7 @@ pub fn monomorphize(mut h: Hugr, reg: &ExtensionRegistry) -> Hugr { let root = h.root(); // 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(&mut h, root, None, &mut HashMap::new(), reg); + mono_scan(&mut h, root, None, &mut HashMap::new(), ®); if !h.get_optype(root).is_module() { return remove_polyfuncs(h); } @@ -297,7 +298,7 @@ mod test { HugrBuilder, ModuleBuilder, }; use hugr_core::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID}; - use hugr_core::extension::{ExtensionRegistry, ExtensionSet, EMPTY_REG, PRELUDE, PRELUDE_REGISTRY}; + use hugr_core::extension::ExtensionSet; use hugr_core::ops::handle::{FuncID, NodeHandle}; use hugr_core::ops::{FuncDefn, Tag}; use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; @@ -324,8 +325,8 @@ mod test { let dfg_builder = DFGBuilder::new(Signature::new(vec![usize_t()], vec![usize_t()])).unwrap(); let [i1] = dfg_builder.input_wires_arr(); - let hugr = dfg_builder.finish_prelude_hugr_with_outputs([i1]).unwrap(); - let hugr2 = monomorphize(hugr.clone(), &PRELUDE_REGISTRY); + let hugr = dfg_builder.finish_hugr_with_outputs([i1]).unwrap(); + let hugr2 = monomorphize(hugr.clone()); assert_eq!(hugr, hugr2); } @@ -348,7 +349,6 @@ mod test { &FuncID::::from(fb.container_node()), &[tv0().into()], [elem], - &EMPTY_REG, )?; fb.finish_with_outputs(tag.outputs())? }; @@ -360,7 +360,7 @@ mod test { PolyFuncType::new([TypeBound::Copyable.into()], sig), )?; let [elem] = fb.input_wires_arr(); - let pair = fb.call(db.handle(), &[tv0().into()], [elem], &PRELUDE_REGISTRY)?; + let pair = fb.call(db.handle(), &[tv0().into()], [elem])?; let [elem1, elem2] = fb .add_dataflow_op(UnpackTuple::new(vec![tv0(); 2].into()), pair.outputs())? @@ -374,24 +374,24 @@ mod test { let mut fb = mb.define_function("main", prelusig(usize_t(), outs))?; let [elem] = fb.input_wires_arr(); let [res1] = fb - .call(tr.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)? + .call(tr.handle(), &[usize_t().into()], [elem])? .outputs_arr(); - let pair = fb.call(db.handle(), &[usize_t().into()], [elem], &PRELUDE_REGISTRY)?; + let pair = fb.call(db.handle(), &[usize_t().into()], [elem])?; let pty = pair_type(usize_t()).into(); let [res2] = fb - .call(tr.handle(), &[pty], pair.outputs(), &PRELUDE_REGISTRY)? + .call(tr.handle(), &[pty], pair.outputs())? .outputs_arr(); fb.finish_with_outputs([res1, res2])?; } - let hugr = mb.finish_hugr(&PRELUDE_REGISTRY)?; + let hugr = mb.finish_hugr()?; assert_eq!( hugr.nodes() .filter(|n| hugr.get_optype(*n).is_func_defn()) .count(), 3 ); - let mono = monomorphize(hugr, &PRELUDE_REGISTRY); - mono.validate(&PRELUDE_REGISTRY)?; + let mono = monomorphize(hugr); + mono.validate()?; let mut funcs = list_funcs(&mono); let expected_mangled_names = [ @@ -410,7 +410,7 @@ mod test { ["double", "main", "triple"] ); - assert_eq!(monomorphize(mono.clone(), &PRELUDE_REGISTRY), mono); // Idempotent + assert_eq!(monomorphize(mono.clone()), mono); // Idempotent let nopoly = remove_polyfuncs(mono); let mut funcs = list_funcs(&nopoly); @@ -427,8 +427,6 @@ mod test { fn test_flattening() -> Result<(), Box> { //pf1 contains pf2 contains mono_func -> pf1 and pf1 share pf2's and they share mono_func - let reg = - ExtensionRegistry::try_new([int_types::EXTENSION.to_owned(), PRELUDE.to_owned()])?; let tv0 = || Type::new_var_use(0, TypeBound::Any); let ity = || INT_TYPES[3].clone(); @@ -448,28 +446,28 @@ mod test { }; let pf2 = { let [inw] = pf2.input_wires_arr(); - let [usz] = pf2.call(mono_func.handle(), &[], [], ®)?.outputs_arr(); + let [usz] = pf2.call(mono_func.handle(), &[], [])?.outputs_arr(); pf2.finish_with_outputs([inw, usz])? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not let [a, u] = pf1 - .call(pf2.handle(), &[tv0().into()], pf1.input_wires(), ®)? + .call(pf2.handle(), &[tv0().into()], pf1.input_wires())? .outputs_arr(); let [u1, u2] = pf1 - .call(pf2.handle(), &[usize_t().into()], [u], ®)? + .call(pf2.handle(), &[usize_t().into()], [u])? .outputs_arr(); let pf1 = pf1.finish_with_outputs([a, u1, u2])?; // Outer: two calls to pf1 with different TypeArgs let [_, u, _] = outer - .call(pf1.handle(), &[ity().into()], outer.input_wires(), ®)? + .call(pf1.handle(), &[ity().into()], outer.input_wires())? .outputs_arr(); let [_, u, _] = outer - .call(pf1.handle(), &[usize_t().into()], [u], ®)? + .call(pf1.handle(), &[usize_t().into()], [u])? .outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([u], ®)?; + let hugr = outer.finish_hugr_with_outputs([u])?; - let mono_hugr = monomorphize(hugr, ®); - mono_hugr.validate(®)?; + let mono_hugr = monomorphize(hugr); + mono_hugr.validate()?; let funcs = list_funcs(&mono_hugr); let pf2_name = mangle_inner_func("pf1", "pf2"); assert_eq!( @@ -502,8 +500,6 @@ mod test { #[test] fn test_no_flatten_out_of_mono_func() -> Result<(), Box> { let ity = || INT_TYPES[4].clone(); - let reg = - ExtensionRegistry::try_new([PRELUDE.to_owned(), int_types::EXTENSION.to_owned()])?; let sig = Signature::new_endo(vec![usize_t(), ity()]); let mut dfg = DFGBuilder::new(sig.clone())?; let mut mono = dfg.define_function("id2", sig)?; @@ -518,15 +514,15 @@ mod test { let pf = pf.finish_with_outputs(outs)?; let [a, b] = mono.input_wires_arr(); let [a] = mono - .call(pf.handle(), &[usize_t().into()], [a], ®)? + .call(pf.handle(), &[usize_t().into()], [a])? .outputs_arr(); let [b] = mono - .call(pf.handle(), &[ity().into()], [b], ®)? + .call(pf.handle(), &[ity().into()], [b])? .outputs_arr(); let mono = mono.finish_with_outputs([a, b])?; - let c = dfg.call(mono.handle(), &[], dfg.input_wires(), ®)?; - let hugr = dfg.finish_hugr_with_outputs(c.outputs(), ®)?; - let mono_hugr = monomorphize(hugr, ®); + let c = dfg.call(mono.handle(), &[], dfg.input_wires())?; + let hugr = dfg.finish_hugr_with_outputs(c.outputs())?; + let mono_hugr = monomorphize(hugr); let mut funcs = list_funcs(&mono_hugr); assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); @@ -564,7 +560,7 @@ mod test { .define_function("main", Signature::new_endo(Type::UNIT)) .unwrap(); let func_ptr = builder - .load_func(foo.handle(), &[Type::UNIT.into()], &EMPTY_REG) + .load_func(foo.handle(), &[Type::UNIT.into()]) .unwrap(); let [r] = builder .call_indirect( @@ -576,10 +572,10 @@ mod test { .outputs_arr(); builder.finish_with_outputs([r]).unwrap() }; - module_builder.finish_hugr(&EMPTY_REG).unwrap() + module_builder.finish_hugr().unwrap() }; - let mono_hugr = remove_polyfuncs(monomorphize(hugr, &EMPTY_REG)); + let mono_hugr = remove_polyfuncs(monomorphize(hugr)); let funcs = list_funcs(&mono_hugr); assert!(funcs.values().all(|(_, fd)| !is_polymorphic(fd))); @@ -587,10 +583,10 @@ mod test { #[rstest] #[case::bounded_nat(vec![0.into()], "$foo$$n(0)")] - #[case::type_(vec![Type::UNIT.into()], "$foo$$[]")] + #[case::type_(vec![Type::UNIT.into()], "$foo$$Unit")] #[case::string(vec!["arg".into()], "$foo$$s(arg)")] #[case::dollar_string(vec!["$arg".into()], "$foo$$s(\\$arg)")] - #[case::sequence(vec![vec![0.into(), Type::UNIT.into()].into()], "$foo$$seq(n(0),[])")] + #[case::sequence(vec![vec![0.into(), Type::UNIT.into()].into()], "$foo$$seq(n(0),Unit)")] #[case::extensionset(vec![ExtensionSet::from_iter([PRELUDE_ID,int_types::EXTENSION_ID]).into()], "$foo$$es(arithmetic.int.types,prelude)")] // alphabetic ordering of extension names #[should_panic] From 584570601ed75bde4815dec968cfb18ac2d1de0b Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 07:56:16 +0000 Subject: [PATCH 53/63] fmt --- hugr-core/src/ops.rs | 2 +- hugr-core/src/types/type_param.rs | 5 +-- hugr-passes/src/monomorphize.rs | 53 +++++++++++++++++-------------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index dd42b2a18..952490596 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -15,8 +15,8 @@ use crate::extension::resolution::{ use std::borrow::Cow; use crate::extension::simple_op::MakeExtensionOp; -use crate::types::{EdgeKind, Signature, Substitution}; use crate::extension::{ExtensionId, ExtensionRegistry, ExtensionSet}; +use crate::types::{EdgeKind, Signature, Substitution}; use crate::{Direction, OutgoingPort, Port}; use crate::{IncomingPort, PortIndex}; use derive_more::Display; diff --git a/hugr-core/src/types/type_param.rs b/hugr-core/src/types/type_param.rs index be688ad7c..9aa19963e 100644 --- a/hugr-core/src/types/type_param.rs +++ b/hugr-core/src/types/type_param.rs @@ -212,9 +212,10 @@ impl From for TypeArg { impl From<&str> for TypeArg { fn from(arg: &str) -> Self { - TypeArg::String { arg: arg.to_string() } + TypeArg::String { + arg: arg.to_string(), + } } - } impl From> for TypeArg { diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 5b3f3ff11..89d57b57e 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -1,4 +1,7 @@ -use std::{collections::{hash_map::Entry, HashMap}, ops::Deref}; +use std::{ + collections::{hash_map::Entry, HashMap}, + ops::Deref, +}; use hugr_core::{ extension::ExtensionRegistry, @@ -235,7 +238,7 @@ fn instantiate( mono_tgt } -struct TypeArgsList<'a>(&'a[TypeArg]); +struct TypeArgsList<'a>(&'a [TypeArg]); impl<'a> std::fmt::Display for TypeArgsList<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -249,9 +252,7 @@ impl<'a> std::fmt::Display for TypeArgsList<'a> { fn write_type_arg_str(arg: &TypeArg, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match arg { - TypeArg::Type { ty } => { - f.write_str(&ty.to_string().replace("$", "\\$")) - }, + TypeArg::Type { ty } => f.write_str(&ty.to_string().replace("$", "\\$")), TypeArg::BoundedNat { n } => f.write_fmt(format_args!("n({n})")), TypeArg::String { arg } => f.write_fmt(format_args!("s({})", arg.replace("$", "\\$"))), TypeArg::Sequence { elems } => { @@ -268,7 +269,10 @@ fn write_type_arg_str(arg: &TypeArg, f: &mut std::fmt::Formatter<'_>) -> std::fm f.write_str(")")?; Ok(()) } - TypeArg::Extensions { es } => f.write_fmt(format_args!("es({})", es.iter().map(|x| x.deref()).join(","))), + TypeArg::Extensions { es } => f.write_fmt(format_args!( + "es({})", + es.iter().map(|x| x.deref()).join(",") + )), TypeArg::Variable { .. } => panic!("type_arg_str variable: {arg}"), _ => panic!("unknown type arg: {arg}"), } @@ -300,12 +304,16 @@ mod test { Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, }; - use hugr_core::extension::prelude::{usize_t, ConstUsize, UnpackTuple, PRELUDE_ID, UnwrapBuilder, }; + use hugr_core::extension::prelude::{ + usize_t, ConstUsize, UnpackTuple, UnwrapBuilder, PRELUDE_ID, + }; use hugr_core::extension::ExtensionSet; use hugr_core::ops::handle::{FuncID, NodeHandle}; use hugr_core::ops::{DataflowOpTrait as _, FuncDefn, Tag}; use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; - use hugr_core::types::{PolyFuncType, Signature, Type, TypeArg, TypeBound, TypeRow, SumType, TypeEnum, }; + use hugr_core::types::{ + PolyFuncType, Signature, SumType, Type, TypeArg, TypeBound, TypeEnum, TypeRow, + }; use hugr_core::{Hugr, HugrView, Node}; use rstest::rstest; @@ -381,9 +389,7 @@ mod test { .outputs_arr(); let pair = fb.call(db.handle(), &[usize_t().into()], [elem])?; let pty = pair_type(usize_t()).into(); - let [res2] = fb - .call(tr.handle(), &[pty], pair.outputs())? - .outputs_arr(); + let [res2] = fb.call(tr.handle(), &[pty], pair.outputs())?.outputs_arr(); fb.finish_with_outputs([res1, res2])?; } let hugr = mb.finish_hugr()?; @@ -438,7 +444,8 @@ mod test { let mut outer = FunctionBuilder::new( "mainish", prelusig( - array_type_parametric(sa(n), array_type_parametric(sa(2), usize_t()).unwrap()).unwrap(), + array_type_parametric(sa(n), array_type_parametric(sa(2), usize_t()).unwrap()) + .unwrap(), vec![usize_t(); 2], ), )?; @@ -465,18 +472,18 @@ mod test { let [inw] = pf2.input_wires_arr(); let [idx] = pf2.call(mono_func.handle(), &[], [])?.outputs_arr(); let op_def = array::EXTENSION.get_op("get").unwrap(); - let op = - hugr_core::ops::ExtensionOp::new(op_def.clone(), vec![sv(0), tv(1).into()], &STD_REG)?; + let op = hugr_core::ops::ExtensionOp::new( + op_def.clone(), + vec![sv(0), tv(1).into()], + &STD_REG, + )?; let [get] = pf2.add_dataflow_op(op, [inw, idx])?.outputs_arr(); - let [got] = pf2.build_unwrap_sum(&STD_REG, 1, SumType::new([vec![], vec![tv(1)]]), get)?; + let [got] = + pf2.build_unwrap_sum(&STD_REG, 1, SumType::new([vec![], vec![tv(1)]]), get)?; pf2.finish_with_outputs([got])? }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not - let inner = pf1.call( - pf2.handle(), - &[sv(0), arr2u().into()], - pf1.input_wires(), - )?; + let inner = pf1.call(pf2.handle(), &[sv(0), arr2u().into()], pf1.input_wires())?; let elem = pf1.call( pf2.handle(), &[TypeArg::BoundedNat { n: 2 }, usize_t().into()], @@ -552,9 +559,7 @@ mod test { let [a] = mono .call(pf.handle(), &[usize_t().into()], [a])? .outputs_arr(); - let [b] = mono - .call(pf.handle(), &[ity().into()], [b])? - .outputs_arr(); + let [b] = mono.call(pf.handle(), &[ity().into()], [b])?.outputs_arr(); let mono = mono.finish_with_outputs([a, b])?; let c = dfg.call(mono.handle(), &[], dfg.input_wires())?; let hugr = dfg.finish_hugr_with_outputs(c.outputs())?; @@ -626,7 +631,7 @@ mod test { #[case::extensionset(vec![ExtensionSet::from_iter([PRELUDE_ID,int_types::EXTENSION_ID]).into()], "$foo$$es(arithmetic.int.types,prelude)")] // alphabetic ordering of extension names #[should_panic] - #[case::typeargvariable(vec![TypeArg::new_var_use(1, TypeParam::String).into()], + #[case::typeargvariable(vec![TypeArg::new_var_use(1, TypeParam::String)], "$foo$$v(1)")] #[case::multiple(vec![0.into(), "arg".into()], "$foo$$n(0)$s(arg)")] fn test_mangle_name(#[case] args: Vec, #[case] expected: String) { From 60498895c19d69c90fa475b65074ba126bff4e45 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 08:10:24 +0000 Subject: [PATCH 54/63] add array extension to signatures --- hugr-core/src/builder/build_traits.rs | 9 +++- hugr-passes/src/monomorphize.rs | 69 +++++++++++++-------------- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index ec62f9d9f..ee0978e56 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -710,7 +710,14 @@ pub trait Dataflow: Container { Ok(op_id) } - /// TODO docs + /// Add a [`ops::CallIndirect`] node, calling `function` with signature + /// `signature`, with inputs specified by `input_wires`. Returns a handle to + /// the corresponding Call node. + /// + /// # Errors + /// + /// This function will return an error if there is an error adding the + /// CallIndirect node. fn call_indirect( &mut self, signature: Signature, diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 89d57b57e..19cdcd8a2 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -295,8 +295,8 @@ mod test { use std::collections::HashMap; use hugr_core::extension::simple_op::MakeRegisteredOp as _; - use hugr_core::std_extensions::collections::array::{self, array_type_parametric, ArrayOpDef}; - use hugr_core::std_extensions::STD_REG; + use hugr_core::std_extensions::collections::array::{array_type_parametric, ArrayOpDef}; + use hugr_core::std_extensions::{collections, STD_REG}; use hugr_core::types::type_param::TypeParam; use itertools::Itertools; @@ -433,7 +433,7 @@ mod test { } #[test] - fn test_flattening_multiargs_nats() -> Result<(), Box> { + fn test_flattening_multiargs_nats() { //pf1 contains pf2 contains mono_func -> pf1 and pf1 share pf2's and they share mono_func let tv = |i| Type::new_var_use(i, TypeBound::Copyable); @@ -447,55 +447,55 @@ mod test { array_type_parametric(sa(n), array_type_parametric(sa(2), usize_t()).unwrap()) .unwrap(), vec![usize_t(); 2], - ), - )?; + ).with_extension_delta(collections::array::EXTENSION_ID), + ).unwrap(); let arr2u = || array_type_parametric(sa(2), usize_t()).unwrap(); let pf1t = PolyFuncType::new( [TypeParam::max_nat()], - prelusig(array_type_parametric(sv(0), arr2u()).unwrap(), usize_t()), + prelusig(array_type_parametric(sv(0), arr2u()).unwrap(), usize_t()).with_extension_delta(collections::array::EXTENSION_ID), ); - let mut pf1 = outer.define_function("pf1", pf1t)?; + let mut pf1 = outer.define_function("pf1", pf1t).unwrap(); let pf2t = PolyFuncType::new( [TypeParam::max_nat(), TypeBound::Copyable.into()], - prelusig(vec![array_type_parametric(sv(0), tv(1)).unwrap()], tv(1)), + prelusig(vec![array_type_parametric(sv(0), tv(1)).unwrap()], tv(1)).with_extension_delta(collections::array::EXTENSION_ID), ); - let mut pf2 = pf1.define_function("pf2", pf2t)?; + let mut pf2 = pf1.define_function("pf2", pf2t).unwrap(); let mono_func = { - let mut fb = pf2.define_function("get_usz", prelusig(vec![], usize_t()))?; + let mut fb = pf2.define_function("get_usz", prelusig(vec![], usize_t()).with_extension_delta(collections::array::EXTENSION_ID)).unwrap(); let cst0 = fb.add_load_value(ConstUsize::new(1)); - fb.finish_with_outputs([cst0])? + fb.finish_with_outputs([cst0]).unwrap() }; let pf2 = { let [inw] = pf2.input_wires_arr(); - let [idx] = pf2.call(mono_func.handle(), &[], [])?.outputs_arr(); - let op_def = array::EXTENSION.get_op("get").unwrap(); + let [idx] = pf2.call(mono_func.handle(), &[], []).unwrap().outputs_arr(); + let op_def = collections::array::EXTENSION.get_op("get").unwrap(); let op = hugr_core::ops::ExtensionOp::new( op_def.clone(), vec![sv(0), tv(1).into()], &STD_REG, - )?; - let [get] = pf2.add_dataflow_op(op, [inw, idx])?.outputs_arr(); + ).unwrap(); + let [get] = pf2.add_dataflow_op(op, [inw, idx]).unwrap().outputs_arr(); let [got] = - pf2.build_unwrap_sum(&STD_REG, 1, SumType::new([vec![], vec![tv(1)]]), get)?; - pf2.finish_with_outputs([got])? + pf2.build_unwrap_sum(&STD_REG, 1, SumType::new([vec![], vec![tv(1)]]), get).unwrap(); + pf2.finish_with_outputs([got]).unwrap() }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not - let inner = pf1.call(pf2.handle(), &[sv(0), arr2u().into()], pf1.input_wires())?; + let inner = pf1.call(pf2.handle(), &[sv(0), arr2u().into()], pf1.input_wires()).unwrap(); let elem = pf1.call( pf2.handle(), &[TypeArg::BoundedNat { n: 2 }, usize_t().into()], inner.outputs(), - )?; - let pf1 = pf1.finish_with_outputs(elem.outputs())?; + ).unwrap(); + let pf1 = pf1.finish_with_outputs(elem.outputs()).unwrap(); // Outer: two calls to pf1 with different TypeArgs let [e1] = outer - .call(pf1.handle(), &[sa(n)], outer.input_wires())? + .call(pf1.handle(), &[sa(n)], outer.input_wires()).unwrap() .outputs_arr(); let popleft = ArrayOpDef::pop_left.to_concrete(arr2u(), n); - let ar2 = outer.add_dataflow_op(popleft.clone(), outer.input_wires())?; + let ar2 = outer.add_dataflow_op(popleft.clone(), outer.input_wires()).unwrap(); let sig = popleft.to_extension_op().unwrap().signature().into_owned(); let TypeEnum::Sum(st) = sig.output().get(0).unwrap().as_type_enum() else { panic!() @@ -504,12 +504,12 @@ mod test { .build_unwrap_sum(&STD_REG, 1, st.clone(), ar2.out_wire(0)) .unwrap(); let [e2] = outer - .call(pf1.handle(), &[sa(n - 1)], [ar2_unwrapped])? + .call(pf1.handle(), &[sa(n - 1)], [ar2_unwrapped]).unwrap() .outputs_arr(); - let hugr = outer.finish_hugr_with_outputs([e1, e2])?; + let hugr = outer.finish_hugr_with_outputs([e1, e2]).unwrap(); let mono_hugr = monomorphize(hugr); - mono_hugr.validate()?; + mono_hugr.validate().unwrap(); let funcs = list_funcs(&mono_hugr); let pf2_name = mangle_inner_func("pf1", "pf2"); assert_eq!( @@ -531,7 +531,6 @@ mod test { assert!(!is_polymorphic(fd)); assert!(mono_hugr.get_parent(n) == (fd.name != "mainish").then_some(mono_hugr.root())); } - Ok(()) } fn list_funcs(h: &Hugr) -> HashMap<&String, (Node, &FuncDefn)> { @@ -544,25 +543,25 @@ mod test { fn test_no_flatten_out_of_mono_func() -> Result<(), Box> { let ity = || INT_TYPES[4].clone(); let sig = Signature::new_endo(vec![usize_t(), ity()]); - let mut dfg = DFGBuilder::new(sig.clone())?; - let mut mono = dfg.define_function("id2", sig)?; + let mut dfg = DFGBuilder::new(sig.clone()).unwrap(); + let mut mono = dfg.define_function("id2", sig).unwrap(); let pf = mono.define_function( "id", PolyFuncType::new( [TypeBound::Any.into()], Signature::new_endo(Type::new_var_use(0, TypeBound::Any)), ), - )?; + ).unwrap(); let outs = pf.input_wires(); - let pf = pf.finish_with_outputs(outs)?; + let pf = pf.finish_with_outputs(outs).unwrap(); let [a, b] = mono.input_wires_arr(); let [a] = mono - .call(pf.handle(), &[usize_t().into()], [a])? + .call(pf.handle(), &[usize_t().into()], [a]).unwrap() .outputs_arr(); - let [b] = mono.call(pf.handle(), &[ity().into()], [b])?.outputs_arr(); - let mono = mono.finish_with_outputs([a, b])?; - let c = dfg.call(mono.handle(), &[], dfg.input_wires())?; - let hugr = dfg.finish_hugr_with_outputs(c.outputs())?; + let [b] = mono.call(pf.handle(), &[ity().into()], [b]).unwrap().outputs_arr(); + let mono = mono.finish_with_outputs([a, b]).unwrap(); + let c = dfg.call(mono.handle(), &[], dfg.input_wires()).unwrap(); + let hugr = dfg.finish_hugr_with_outputs(c.outputs()).unwrap(); let mono_hugr = monomorphize(hugr); let mut funcs = list_funcs(&mono_hugr); From 144fc47a616aae13b25acf3977f0a7e89a094cd3 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 08:15:15 +0000 Subject: [PATCH 55/63] fmt --- hugr-passes/src/monomorphize.rs | 78 ++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 19cdcd8a2..ea94c6a31 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -447,24 +447,34 @@ mod test { array_type_parametric(sa(n), array_type_parametric(sa(2), usize_t()).unwrap()) .unwrap(), vec![usize_t(); 2], - ).with_extension_delta(collections::array::EXTENSION_ID), - ).unwrap(); + ) + .with_extension_delta(collections::array::EXTENSION_ID), + ) + .unwrap(); let arr2u = || array_type_parametric(sa(2), usize_t()).unwrap(); let pf1t = PolyFuncType::new( [TypeParam::max_nat()], - prelusig(array_type_parametric(sv(0), arr2u()).unwrap(), usize_t()).with_extension_delta(collections::array::EXTENSION_ID), + prelusig(array_type_parametric(sv(0), arr2u()).unwrap(), usize_t()) + .with_extension_delta(collections::array::EXTENSION_ID), ); let mut pf1 = outer.define_function("pf1", pf1t).unwrap(); let pf2t = PolyFuncType::new( [TypeParam::max_nat(), TypeBound::Copyable.into()], - prelusig(vec![array_type_parametric(sv(0), tv(1)).unwrap()], tv(1)).with_extension_delta(collections::array::EXTENSION_ID), + prelusig(vec![array_type_parametric(sv(0), tv(1)).unwrap()], tv(1)) + .with_extension_delta(collections::array::EXTENSION_ID), ); let mut pf2 = pf1.define_function("pf2", pf2t).unwrap(); let mono_func = { - let mut fb = pf2.define_function("get_usz", prelusig(vec![], usize_t()).with_extension_delta(collections::array::EXTENSION_ID)).unwrap(); + let mut fb = pf2 + .define_function( + "get_usz", + prelusig(vec![], usize_t()) + .with_extension_delta(collections::array::EXTENSION_ID), + ) + .unwrap(); let cst0 = fb.add_load_value(ConstUsize::new(1)); fb.finish_with_outputs([cst0]).unwrap() }; @@ -476,26 +486,35 @@ mod test { op_def.clone(), vec![sv(0), tv(1).into()], &STD_REG, - ).unwrap(); + ) + .unwrap(); let [get] = pf2.add_dataflow_op(op, [inw, idx]).unwrap().outputs_arr(); - let [got] = - pf2.build_unwrap_sum(&STD_REG, 1, SumType::new([vec![], vec![tv(1)]]), get).unwrap(); + let [got] = pf2 + .build_unwrap_sum(&STD_REG, 1, SumType::new([vec![], vec![tv(1)]]), get) + .unwrap(); pf2.finish_with_outputs([got]).unwrap() }; // pf1: Two calls to pf2, one depending on pf1's TypeArg, the other not - let inner = pf1.call(pf2.handle(), &[sv(0), arr2u().into()], pf1.input_wires()).unwrap(); - let elem = pf1.call( - pf2.handle(), - &[TypeArg::BoundedNat { n: 2 }, usize_t().into()], - inner.outputs(), - ).unwrap(); + let inner = pf1 + .call(pf2.handle(), &[sv(0), arr2u().into()], pf1.input_wires()) + .unwrap(); + let elem = pf1 + .call( + pf2.handle(), + &[TypeArg::BoundedNat { n: 2 }, usize_t().into()], + inner.outputs(), + ) + .unwrap(); let pf1 = pf1.finish_with_outputs(elem.outputs()).unwrap(); // Outer: two calls to pf1 with different TypeArgs let [e1] = outer - .call(pf1.handle(), &[sa(n)], outer.input_wires()).unwrap() + .call(pf1.handle(), &[sa(n)], outer.input_wires()) + .unwrap() .outputs_arr(); let popleft = ArrayOpDef::pop_left.to_concrete(arr2u(), n); - let ar2 = outer.add_dataflow_op(popleft.clone(), outer.input_wires()).unwrap(); + let ar2 = outer + .add_dataflow_op(popleft.clone(), outer.input_wires()) + .unwrap(); let sig = popleft.to_extension_op().unwrap().signature().into_owned(); let TypeEnum::Sum(st) = sig.output().get(0).unwrap().as_type_enum() else { panic!() @@ -504,7 +523,8 @@ mod test { .build_unwrap_sum(&STD_REG, 1, st.clone(), ar2.out_wire(0)) .unwrap(); let [e2] = outer - .call(pf1.handle(), &[sa(n - 1)], [ar2_unwrapped]).unwrap() + .call(pf1.handle(), &[sa(n - 1)], [ar2_unwrapped]) + .unwrap() .outputs_arr(); let hugr = outer.finish_hugr_with_outputs([e1, e2]).unwrap(); @@ -545,20 +565,26 @@ mod test { let sig = Signature::new_endo(vec![usize_t(), ity()]); let mut dfg = DFGBuilder::new(sig.clone()).unwrap(); let mut mono = dfg.define_function("id2", sig).unwrap(); - let pf = mono.define_function( - "id", - PolyFuncType::new( - [TypeBound::Any.into()], - Signature::new_endo(Type::new_var_use(0, TypeBound::Any)), - ), - ).unwrap(); + let pf = mono + .define_function( + "id", + PolyFuncType::new( + [TypeBound::Any.into()], + Signature::new_endo(Type::new_var_use(0, TypeBound::Any)), + ), + ) + .unwrap(); let outs = pf.input_wires(); let pf = pf.finish_with_outputs(outs).unwrap(); let [a, b] = mono.input_wires_arr(); let [a] = mono - .call(pf.handle(), &[usize_t().into()], [a]).unwrap() + .call(pf.handle(), &[usize_t().into()], [a]) + .unwrap() + .outputs_arr(); + let [b] = mono + .call(pf.handle(), &[ity().into()], [b]) + .unwrap() .outputs_arr(); - let [b] = mono.call(pf.handle(), &[ity().into()], [b]).unwrap().outputs_arr(); let mono = mono.finish_with_outputs([a, b]).unwrap(); let c = dfg.call(mono.handle(), &[], dfg.input_wires()).unwrap(); let hugr = dfg.finish_hugr_with_outputs(c.outputs()).unwrap(); From becc847fff88ef35fb8efa428c0b81602b569d07 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 08:23:52 +0000 Subject: [PATCH 56/63] lint --- hugr-passes/src/monomorphize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index ea94c6a31..9ea8263a6 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -240,7 +240,7 @@ fn instantiate( struct TypeArgsList<'a>(&'a [TypeArg]); -impl<'a> std::fmt::Display for TypeArgsList<'a> { +impl std::fmt::Display for TypeArgsList<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for arg in self.0 { f.write_str("$")?; From df38d88b5947bba0101da640f599cd55392b6de1 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 08:47:23 +0000 Subject: [PATCH 57/63] docs + tweak name mangling --- hugr-passes/src/monomorphize.rs | 59 ++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 9ea8263a6..36de396a4 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -1,5 +1,6 @@ use std::{ collections::{hash_map::Entry, HashMap}, + fmt::Write, ops::Deref, }; @@ -22,9 +23,18 @@ use itertools::Itertools as _; /// * else, the originals are removed (they are invisible from outside the Hugr). /// /// If the Hugr is [FuncDefn](OpType::FuncDefn)-rooted with polymorphic -/// signature then the hugr is untouched. +/// signature then the HUGR will not be modified. +/// +/// Monomorphic copies of polymorphic functions will be added to the HUGR as +/// children of the root node. We make best effort to ensure that names(derived +/// from parent function names concrete type args) of new functions are unique +/// whenever the names of their parents are unique, but this is not guaranteed. pub fn monomorphize(mut h: Hugr) -> Hugr { let validate = |h: &Hugr| h.validate().unwrap_or_else(|e| panic!("{e}")); + + // We clone the extension registry because we will need a reference to + // create our mutable substitutions. This is cannot cause a problem because + // we will not be adding any new types or extension ops to the HUGR. let reg = h.extensions().to_owned(); #[cfg(debug_assertions)] @@ -243,46 +253,48 @@ struct TypeArgsList<'a>(&'a [TypeArg]); impl std::fmt::Display for TypeArgsList<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for arg in self.0 { - f.write_str("$")?; + f.write_char('$')?; write_type_arg_str(arg, f)?; } Ok(()) } } +fn escape_dollar(str: impl AsRef) -> String { + str.as_ref().replace("$", "\\$") +} + fn write_type_arg_str(arg: &TypeArg, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match arg { - TypeArg::Type { ty } => f.write_str(&ty.to_string().replace("$", "\\$")), + TypeArg::Type { ty } => f.write_fmt(format_args!("t({})", escape_dollar(ty.to_string()))), TypeArg::BoundedNat { n } => f.write_fmt(format_args!("n({n})")), - TypeArg::String { arg } => f.write_fmt(format_args!("s({})", arg.replace("$", "\\$"))), - TypeArg::Sequence { elems } => { - f.write_str("seq(")?; - let mut first = true; - for arg in elems.iter() { - if first { - first = false; - } else { - f.write_str(",")?; - } - write_type_arg_str(arg, f)?; - } - f.write_str(")")?; - Ok(()) - } + TypeArg::String { arg } => f.write_fmt(format_args!("s({})", escape_dollar(arg))), + TypeArg::Sequence { elems } => f.write_fmt(format_args!("seq({})", TypeArgsList(elems))), TypeArg::Extensions { es } => f.write_fmt(format_args!( "es({})", es.iter().map(|x| x.deref()).join(",") )), + // We are monomorphizing. We will never monomorphize to a signature + // containing a variable. TypeArg::Variable { .. } => panic!("type_arg_str variable: {arg}"), _ => panic!("unknown type arg: {arg}"), } } -/// We do our best to generate unique names. -/// -/// We depend on the [Display] impl of [TypeArg]. +/// We do our best to generate unique names. Our strategy is to pick out '$' as +/// a special character. /// +/// We: +/// - construct a new name of the form `{func_name}$$arg0$arg1$arg2` etc +/// - replace any existing `$` in the function name or type args string +/// representation with `r"\$"` +/// - We depend on the `Display` impl of `Type` to generate the string +/// representation of a `TypeArg::Type`. For other constructors we do the +/// simple obvious thing. +/// - For all TypeArg Constructors we choose a short prefix (e.g. `t` for type) +/// and use "t({arg})" as the string representation of that arg. fn mangle_name(name: &str, type_args: impl AsRef<[TypeArg]>) -> String { + let name = escape_dollar(name); format!("${name}${}", TypeArgsList(type_args.as_ref())) } @@ -649,10 +661,11 @@ mod test { #[rstest] #[case::bounded_nat(vec![0.into()], "$foo$$n(0)")] - #[case::type_(vec![Type::UNIT.into()], "$foo$$Unit")] + #[case::type_unit(vec![Type::UNIT.into()], "$foo$$t(Unit)")] + #[case::type_int(vec![INT_TYPES[2].to_owned().into()], "$foo$$t(int([BoundedNat { n: 2 }]))")] #[case::string(vec!["arg".into()], "$foo$$s(arg)")] #[case::dollar_string(vec!["$arg".into()], "$foo$$s(\\$arg)")] - #[case::sequence(vec![vec![0.into(), Type::UNIT.into()].into()], "$foo$$seq(n(0),Unit)")] + #[case::sequence(vec![vec![0.into(), Type::UNIT.into()].into()], "$foo$$seq($n(0)$t(Unit))")] #[case::extensionset(vec![ExtensionSet::from_iter([PRELUDE_ID,int_types::EXTENSION_ID]).into()], "$foo$$es(arithmetic.int.types,prelude)")] // alphabetic ordering of extension names #[should_panic] From 4bde01d22506f9fa24cde0306b8508c1fb796154 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 08:51:15 +0000 Subject: [PATCH 58/63] docs --- hugr-passes/src/monomorphize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 36de396a4..2666a4571 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -58,7 +58,7 @@ pub fn monomorphize(mut h: Hugr) -> Hugr { /// first). /// /// TODO replace this with a more general remove-unused-functions pass -/// https://github.com/CQCL/hugr/issues/1753 +/// pub fn remove_polyfuncs(mut h: Hugr) -> Hugr { let mut pfs_to_delete = Vec::new(); let mut to_scan = Vec::from_iter(h.children(h.root())); From d693c81ba71d7816a0831be49f2a0ea05b9bd6c2 Mon Sep 17 00:00:00 2001 From: Douglas Wilson <141026920+doug-q@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:11:05 +0000 Subject: [PATCH 59/63] Update hugr-passes/src/monomorphize.rs Co-authored-by: Seyon Sivarajah --- hugr-passes/src/monomorphize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 2666a4571..55afb8f5b 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -27,7 +27,7 @@ use itertools::Itertools as _; /// /// Monomorphic copies of polymorphic functions will be added to the HUGR as /// children of the root node. We make best effort to ensure that names(derived -/// from parent function names concrete type args) of new functions are unique +/// 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. pub fn monomorphize(mut h: Hugr) -> Hugr { let validate = |h: &Hugr| h.validate().unwrap_or_else(|e| panic!("{e}")); From 61afb2ebb3b24c38b3232145fadc9cf4b126ef94 Mon Sep 17 00:00:00 2001 From: Douglas Wilson <141026920+doug-q@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:11:40 +0000 Subject: [PATCH 60/63] Update hugr-passes/src/monomorphize.rs Co-authored-by: Seyon Sivarajah --- hugr-passes/src/monomorphize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 55afb8f5b..a0928d8ce 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -92,7 +92,7 @@ struct Instantiating<'a> { type Instantiations = HashMap, Node>>; /// Scans a subtree for polymorphic calls and monomorphizes them by instantiating the -/// called functions (if instantations not already in `cache`). +/// called functions (if instantiations not already in `cache`). /// Optionally copies the subtree into a new location whilst applying a substitution. /// The subtree should be monomorphic after the substitution (if provided) has been applied. fn mono_scan( From 7618fa33045d6c85b0206775d7e7bfeb01828284 Mon Sep 17 00:00:00 2001 From: Douglas Wilson <141026920+doug-q@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:16:30 +0000 Subject: [PATCH 61/63] Update hugr-passes/src/monomorphize.rs --- hugr-passes/src/monomorphize.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index a0928d8ce..2dd79312b 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -137,7 +137,6 @@ fn mono_scan( ) } OpType::LoadFunction(lf) => { - eprintln!("{lf:?}"); let mono_sig = lf.instantiation.clone(); ( &lf.type_args, From 44a354ca12febf8e6e4fa116c1a71aeb70034fb9 Mon Sep 17 00:00:00 2001 From: Douglas Wilson <141026920+doug-q@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:16:47 +0000 Subject: [PATCH 62/63] Update hugr-passes/src/monomorphize.rs Co-authored-by: Seyon Sivarajah --- hugr-passes/src/monomorphize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 2dd79312b..0908c8498 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -26,7 +26,7 @@ use itertools::Itertools as _; /// signature then the HUGR will not be modified. /// /// Monomorphic copies of polymorphic functions will be added to the HUGR as -/// children of the root node. We make best effort to ensure that names(derived +/// 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. pub fn monomorphize(mut h: Hugr) -> Hugr { From 821c68e35e1ceb416b830af83fc8dbcfe0267886 Mon Sep 17 00:00:00 2001 From: Douglas Wilson Date: Thu, 12 Dec 2024 11:20:04 +0000 Subject: [PATCH 63/63] remove call_indirect helper --- hugr-core/src/builder/build_traits.rs | 22 +--------------------- hugr-passes/src/monomorphize.rs | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index ee0978e56..44f5a00ac 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -2,7 +2,7 @@ use crate::extension::prelude::MakeTuple; use crate::hugr::hugrmut::InsertionResult; use crate::hugr::views::HugrView; use crate::hugr::{NodeMetadata, ValidationError}; -use crate::ops::{self, CallIndirect, OpTag, OpTrait, OpType, Tag, TailLoop}; +use crate::ops::{self, OpTag, OpTrait, OpType, Tag, TailLoop}; use crate::utils::collect_array; use crate::{Extension, IncomingPort, Node, OutgoingPort}; @@ -710,26 +710,6 @@ pub trait Dataflow: Container { Ok(op_id) } - /// Add a [`ops::CallIndirect`] node, calling `function` with signature - /// `signature`, with inputs specified by `input_wires`. Returns a handle to - /// the corresponding Call node. - /// - /// # Errors - /// - /// This function will return an error if there is an error adding the - /// CallIndirect node. - fn call_indirect( - &mut self, - signature: Signature, - function: Wire, - input_wires: impl IntoIterator, - ) -> Result, BuildError> { - self.add_dataflow_op( - CallIndirect { signature }, - iter::once(function).chain(input_wires), - ) - } - /// For the vector of `wires`, produce a `CircuitBuilder` where ops can be /// added using indices in to the vector. fn as_circuit(&mut self, wires: impl IntoIterator) -> CircuitBuilder { diff --git a/hugr-passes/src/monomorphize.rs b/hugr-passes/src/monomorphize.rs index 2666a4571..0198392ec 100644 --- a/hugr-passes/src/monomorphize.rs +++ b/hugr-passes/src/monomorphize.rs @@ -305,6 +305,7 @@ fn mangle_inner_func(outer_name: &str, inner_name: &str) -> String { #[cfg(test)] mod test { use std::collections::HashMap; + use std::iter; use hugr_core::extension::simple_op::MakeRegisteredOp as _; use hugr_core::std_extensions::collections::array::{array_type_parametric, ArrayOpDef}; @@ -321,7 +322,7 @@ mod test { }; use hugr_core::extension::ExtensionSet; use hugr_core::ops::handle::{FuncID, NodeHandle}; - use hugr_core::ops::{DataflowOpTrait as _, FuncDefn, Tag}; + use hugr_core::ops::{CallIndirect, DataflowOpTrait as _, FuncDefn, Tag}; use hugr_core::std_extensions::arithmetic::int_types::{self, INT_TYPES}; use hugr_core::types::{ PolyFuncType, Signature, SumType, Type, TypeArg, TypeBound, TypeEnum, TypeRow, @@ -640,14 +641,17 @@ mod test { let func_ptr = builder .load_func(foo.handle(), &[Type::UNIT.into()]) .unwrap(); - let [r] = builder - .call_indirect( - Signature::new_endo(Type::UNIT), - func_ptr, - builder.input_wires(), - ) - .unwrap() - .outputs_arr(); + let [r] = { + let signature = Signature::new_endo(Type::UNIT); + builder + .add_dataflow_op( + CallIndirect { signature }, + iter::once(func_ptr).chain(builder.input_wires()), + ) + .unwrap() + .outputs_arr() + }; + builder.finish_with_outputs([r]).unwrap() }; module_builder.finish_hugr().unwrap()