diff --git a/hugr-core/src/builder/dataflow.rs b/hugr-core/src/builder/dataflow.rs index 35232227e..0c3ab57fc 100644 --- a/hugr-core/src/builder/dataflow.rs +++ b/hugr-core/src/builder/dataflow.rs @@ -8,14 +8,9 @@ use std::marker::PhantomData; use crate::hugr::internal::HugrMutInternals; use crate::hugr::{HugrView, ValidationError}; -use crate::ops::{self, OpParent}; -use crate::ops::{DataflowParent, Input, Output}; -use crate::{Direction, IncomingPort, OutgoingPort, Wire}; - +use crate::ops::{self, DataflowParent, FuncDefn, Input, OpParent, Output}; use crate::types::{PolyFuncType, Signature, Type}; - -use crate::Node; -use crate::{Hugr, hugr::HugrMut}; +use crate::{Direction, Hugr, IncomingPort, Node, OutgoingPort, Visibility, Wire, hugr::HugrMut}; /// Builder for a [`ops::DFG`] node. #[derive(Debug, Clone, PartialEq)] @@ -152,7 +147,9 @@ impl DFGWrapper { pub type FunctionBuilder = DFGWrapper>>; impl FunctionBuilder { - /// Initialize a builder for a `FuncDefn` rooted HUGR + /// Initialize a builder for a [`FuncDefn`](ops::FuncDefn)-rooted HUGR; + /// the function will be private. (See also [Self::new_vis].) + /// /// # Errors /// /// Error in adding DFG child nodes. @@ -160,9 +157,25 @@ impl FunctionBuilder { name: impl Into, signature: impl Into, ) -> Result { - let signature: PolyFuncType = signature.into(); - let body = signature.body().clone(); - let op = ops::FuncDefn::new(name, signature); + Self::new_with_op(FuncDefn::new(name, signature)) + } + + /// Initialize a builder for a FuncDefn-rooted HUGR, with the specified + /// [Visibility]. + /// + /// # Errors + /// + /// Error in adding DFG child nodes. + pub fn new_vis( + name: impl Into, + signature: impl Into, + visibility: Visibility, + ) -> Result { + Self::new_with_op(FuncDefn::new_vis(name, signature, visibility)) + } + + fn new_with_op(op: FuncDefn) -> Result { + let body = op.signature().body().clone(); let base = Hugr::new_with_entrypoint(op).expect("FuncDefn entrypoint should be valid"); let root = base.entrypoint(); diff --git a/hugr-core/src/builder/module.rs b/hugr-core/src/builder/module.rs index 9e045bc2a..699e112a8 100644 --- a/hugr-core/src/builder/module.rs +++ b/hugr-core/src/builder/module.rs @@ -4,15 +4,14 @@ use super::{ dataflow::{DFGBuilder, FunctionBuilder}, }; -use crate::hugr::ValidationError; use crate::hugr::internal::HugrMutInternals; use crate::hugr::views::HugrView; use crate::ops; -use crate::types::{PolyFuncType, Type, TypeBound}; - use crate::ops::handle::{AliasID, FuncID, NodeHandle}; +use crate::types::{PolyFuncType, Type, TypeBound}; +use crate::{Hugr, Node, Visibility}; +use crate::{hugr::ValidationError, ops::FuncDefn}; -use crate::{Hugr, Node}; use smol_str::SmolStr; /// Builder for a HUGR module. @@ -69,25 +68,61 @@ impl + AsRef> ModuleBuilder { f_id: &FuncID, ) -> Result, BuildError> { let f_node = f_id.node(); - let decl = - self.hugr() - .get_optype(f_node) - .as_func_decl() - .ok_or(BuildError::UnexpectedType { - node: f_node, - op_desc: "crate::ops::OpType::FuncDecl", - })?; - let name = decl.func_name().clone(); - let sig = decl.signature().clone(); - let body = sig.body().clone(); - self.hugr_mut() - .replace_op(f_node, ops::FuncDefn::new(name, sig)); + let opty = self.hugr_mut().optype_mut(f_node); + let ops::OpType::FuncDecl(decl) = opty else { + return Err(BuildError::UnexpectedType { + node: f_node, + op_desc: "crate::ops::OpType::FuncDecl", + }); + }; + + let body = decl.signature().body().clone(); + *opty = ops::FuncDefn::new_vis( + decl.func_name(), + decl.signature().clone(), + decl.visibility().clone(), + ) + .into(); + + let db = DFGBuilder::create_with_io(self.hugr_mut(), f_node, body)?; + Ok(FunctionBuilder::from_dfg_builder(db)) + } + + /// Add a [`ops::FuncDefn`] node of the specified visibility. + /// Returns a builder to define the function body graph. + /// + /// # Errors + /// + /// This function will return an error if there is an error in adding the + /// [`ops::FuncDefn`] node. + pub fn define_function_vis( + &mut self, + name: impl Into, + signature: impl Into, + visibility: Visibility, + ) -> Result, BuildError> { + self.define_function_op(FuncDefn::new_vis(name, signature, visibility)) + } + + fn define_function_op( + &mut self, + op: FuncDefn, + ) -> Result, BuildError> { + let body = op.signature().body().clone(); + let f_node = self.add_child_node(op); + + // Add the extensions used by the function types. + self.use_extensions( + body.used_extensions().unwrap_or_else(|e| { + panic!("Build-time signatures should have valid extensions. {e}") + }), + ); let db = DFGBuilder::create_with_io(self.hugr_mut(), f_node, body)?; Ok(FunctionBuilder::from_dfg_builder(db)) } - /// Declare a function with `signature` and return a handle to the declaration. + /// Declare a [Visibility::Public] function with `signature` and return a handle to the declaration. /// /// # Errors /// @@ -97,10 +132,26 @@ impl + AsRef> ModuleBuilder { &mut self, name: impl Into, signature: PolyFuncType, + ) -> Result, BuildError> { + self.declare_vis(name, signature, Visibility::Public) + } + + /// Declare a function with the specified `signature` and [Visibility], + /// and return a handle to the declaration. + /// + /// # Errors + /// + /// This function will return an error if there is an error in adding the + /// [`crate::ops::OpType::FuncDecl`] node. + pub fn declare_vis( + &mut self, + name: impl Into, + signature: PolyFuncType, + visibility: Visibility, ) -> Result, BuildError> { let body = signature.body().clone(); // TODO add param names to metadata - let declare_n = self.add_child_node(ops::FuncDecl::new(name, signature)); + let declare_n = self.add_child_node(ops::FuncDecl::new_vis(name, signature, visibility)); // Add the extensions used by the function types. self.use_extensions( @@ -112,8 +163,8 @@ impl + AsRef> ModuleBuilder { Ok(declare_n.into()) } - /// Add a [`ops::FuncDefn`] node and returns a builder to define the function - /// body graph. + /// Adds a [`ops::FuncDefn`] node and returns a builder to define the function + /// body graph. The function will be private. (See [Self::define_function_vis].) /// /// # Errors /// @@ -124,19 +175,7 @@ impl + AsRef> ModuleBuilder { name: impl Into, signature: impl Into, ) -> Result, BuildError> { - let signature: PolyFuncType = signature.into(); - let body = signature.body().clone(); - let f_node = self.add_child_node(ops::FuncDefn::new(name, signature)); - - // Add the extensions used by the function types. - self.use_extensions( - body.used_extensions().unwrap_or_else(|e| { - panic!("Build-time signatures should have valid extensions. {e}") - }), - ); - - let db = DFGBuilder::create_with_io(self.hugr_mut(), f_node, body)?; - Ok(FunctionBuilder::from_dfg_builder(db)) + self.define_function_op(FuncDefn::new(name, signature)) } /// Add a [`crate::ops::OpType::AliasDefn`] node and return a handle to the Alias. diff --git a/hugr-core/src/core.rs b/hugr-core/src/core.rs index 300210223..67a03eb49 100644 --- a/hugr-core/src/core.rs +++ b/hugr-core/src/core.rs @@ -276,6 +276,46 @@ impl std::fmt::Display for Wire { } } +/// Marks [FuncDefn](crate::ops::FuncDefn)s and [FuncDecl](crate::ops::FuncDecl)s as +/// to whether they should be considered for linking. +#[derive( + Clone, + Debug, + derive_more::Display, + PartialEq, + Eq, + PartialOrd, + Ord, + serde::Serialize, + serde::Deserialize, +)] +#[cfg_attr(test, derive(proptest_derive::Arbitrary))] +#[non_exhaustive] +pub enum Visibility { + /// Function is visible or exported + Public, + /// Function is hidden, for use within the hugr only + Private, +} + +impl From for Visibility { + fn from(value: hugr_model::v0::Visibility) -> Self { + match value { + hugr_model::v0::Visibility::Private => Self::Private, + hugr_model::v0::Visibility::Public => Self::Public, + } + } +} + +impl From for hugr_model::v0::Visibility { + fn from(value: Visibility) -> Self { + match value { + Visibility::Public => hugr_model::v0::Visibility::Public, + Visibility::Private => hugr_model::v0::Visibility::Private, + } + } +} + /// Enum for uniquely identifying the origin of linear wires in a circuit-like /// dataflow region. /// diff --git a/hugr-core/src/export.rs b/hugr-core/src/export.rs index ecd650693..72eddf974 100644 --- a/hugr-core/src/export.rs +++ b/hugr-core/src/export.rs @@ -20,6 +20,7 @@ use crate::{ }; use fxhash::{FxBuildHasher, FxHashMap}; +use hugr_model::v0::Visibility; use hugr_model::v0::{ self as model, bumpalo::{Bump, collections::String as BumpString, collections::Vec as BumpVec}, @@ -230,16 +231,6 @@ impl<'a> Context<'a> { } } - /// Get the name of a function definition or declaration node. Returns `None` if not - /// one of those operations. - fn get_func_name(&self, func_node: Node) -> Option<&'a str> { - match self.hugr.get_optype(func_node) { - OpType::FuncDecl(func_decl) => Some(func_decl.func_name()), - OpType::FuncDefn(func_defn) => Some(func_defn.func_name()), - _ => None, - } - } - fn with_local_scope(&mut self, node: table::NodeId, f: impl FnOnce(&mut Self) -> T) -> T { let prev_local_scope = self.local_scope.replace(node); let prev_local_constraints = std::mem::take(&mut self.local_constraints); @@ -338,8 +329,11 @@ impl<'a> Context<'a> { } OpType::FuncDefn(func) => self.with_local_scope(node_id, |this| { - let name = this.get_func_name(node).unwrap(); - let symbol = this.export_poly_func_type(name, func.signature()); + let symbol = this.export_poly_func_type( + func.func_name(), + func.visibility().clone().into(), + func.signature(), + ); regions = this.bump.alloc_slice_copy(&[this.export_dfg( node, model::ScopeClosure::Closed, @@ -349,15 +343,21 @@ impl<'a> Context<'a> { }), OpType::FuncDecl(func) => self.with_local_scope(node_id, |this| { - let name = this.get_func_name(node).unwrap(); - let symbol = this.export_poly_func_type(name, func.signature()); + let symbol = this.export_poly_func_type( + func.func_name(), + func.visibility().clone().into(), + func.signature(), + ); table::Operation::DeclareFunc(symbol) }), OpType::AliasDecl(alias) => self.with_local_scope(node_id, |this| { // TODO: We should support aliases with different types and with parameters let signature = this.make_term_apply(model::CORE_TYPE, &[]); + // Visibility is not spec'd in hugr-core + let visibility = this.bump.alloc(Visibility::default()); // good to common up!? let symbol = this.bump.alloc(table::Symbol { + visibility, name: &alias.name, params: &[], constraints: &[], @@ -370,7 +370,10 @@ impl<'a> Context<'a> { let value = this.export_type(&alias.definition); // TODO: We should support aliases with different types and with parameters let signature = this.make_term_apply(model::CORE_TYPE, &[]); + // Visibility is not spec'd in hugr-core + let visibility = this.bump.alloc(Visibility::default()); // good to common up!? let symbol = this.bump.alloc(table::Symbol { + visibility, name: &alias.name, params: &[], constraints: &[], @@ -545,7 +548,8 @@ impl<'a> Context<'a> { let symbol = self.with_local_scope(node, |this| { let name = this.make_qualified_name(opdef.extension_id(), opdef.name()); - this.export_poly_func_type(name, poly_func_type) + // Visibility of OpDef's has no effect + this.export_poly_func_type(name, Visibility::default(), poly_func_type) }); let meta = { @@ -796,13 +800,14 @@ impl<'a> Context<'a> { pub fn export_poly_func_type( &mut self, name: &'a str, + visibility: Visibility, t: &PolyFuncTypeBase, ) -> &'a table::Symbol<'a> { let mut params = BumpVec::with_capacity_in(t.params().len(), self.bump); let scope = self .local_scope .expect("exporting poly func type outside of local scope"); - + let visibility = self.bump.alloc(visibility); for (i, param) in t.params().iter().enumerate() { let name = self.bump.alloc_str(&i.to_string()); let r#type = self.export_term(param, Some((scope, i as _))); @@ -814,6 +819,7 @@ impl<'a> Context<'a> { let body = self.export_func_type(t.body()); self.bump.alloc(table::Symbol { + visibility, name, params: params.into_bump_slice(), constraints, diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 51ebc709f..cd555bee9 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -553,8 +553,8 @@ pub(crate) mod test { use crate::extension::prelude::bool_t; use crate::ops::OpaqueOp; use crate::ops::handle::NodeHandle; - use crate::test_file; use crate::types::Signature; + use crate::{Visibility, test_file}; use cool_asserts::assert_matches; use itertools::Either; use portgraph::LinkView; @@ -674,6 +674,26 @@ pub(crate) mod test { assert_matches!(&hugr, Ok(_)); } + #[test] + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + fn load_funcs_no_visibility() { + let hugr = Hugr::load( + BufReader::new(File::open(test_file!("hugr-no-visibility.hugr")).unwrap()), + None, + ) + .unwrap(); + + let [_mod, decl, defn] = hugr.nodes().take(3).collect_array().unwrap(); + assert_eq!( + hugr.get_optype(decl).as_func_decl().unwrap().visibility(), + &Visibility::Public + ); + assert_eq!( + hugr.get_optype(defn).as_func_defn().unwrap().visibility(), + &Visibility::Private + ); + } + fn hugr_failing_2262() -> Hugr { let sig = Signature::new(vec![bool_t(); 2], bool_t()); let mut mb = ModuleBuilder::new(); diff --git a/hugr-core/src/hugr/serialize/test.rs b/hugr-core/src/hugr/serialize/test.rs index c7bb27fa5..2b7b27ba7 100644 --- a/hugr-core/src/hugr/serialize/test.rs +++ b/hugr-core/src/hugr/serialize/test.rs @@ -24,7 +24,7 @@ use crate::types::{ FuncValueType, PolyFuncType, PolyFuncTypeRV, Signature, SumType, Type, TypeArg, TypeBound, TypeRV, }; -use crate::{OutgoingPort, type_row}; +use crate::{OutgoingPort, Visibility, type_row}; use itertools::Itertools; use jsonschema::{Draft, Validator}; @@ -236,6 +236,7 @@ fn test_schema_val() -> serde_json::Value { "optype":{ "name":"polyfunc1", "op":"FuncDefn", + "visibility": "Public", "parent":0, "signature":{ "body":{ @@ -597,8 +598,8 @@ fn roundtrip_polyfunctype_varlen(#[case] poly_func_type: PolyFuncTypeRV) { #[rstest] #[case(ops::Module::new())] -#[case(ops::FuncDefn::new("polyfunc1", polyfunctype1()))] -#[case(ops::FuncDecl::new("polyfunc2", polyfunctype1()))] +#[case(ops::FuncDefn::new_vis("polyfunc1", polyfunctype1(), Visibility::Private))] +#[case(ops::FuncDefn::new_vis("pubfunc1", polyfunctype1(), Visibility::Public))] #[case(ops::AliasDefn { name: "aliasdefn".into(), definition: Type::new_unit_sum(4)})] #[case(ops::AliasDecl { name: "aliasdecl".into(), bound: TypeBound::Any})] #[case(ops::Const::new(Value::false_val()))] diff --git a/hugr-core/src/hugr/serialize/upgrade/testcases/hugr_with_named_op.json b/hugr-core/src/hugr/serialize/upgrade/testcases/hugr_with_named_op.json index ca3965d87..112581e94 100644 --- a/hugr-core/src/hugr/serialize/upgrade/testcases/hugr_with_named_op.json +++ b/hugr-core/src/hugr/serialize/upgrade/testcases/hugr_with_named_op.json @@ -3,67 +3,8 @@ "nodes": [ { "parent": 0, - "op": "Module" - }, - { - "parent": 0, - "op": "FuncDefn", - "name": "main", - "signature": { - "params": [], - "body": { - "input": [ - { - "t": "Sum", - "s": "Unit", - "size": 2 - }, - { - "t": "Sum", - "s": "Unit", - "size": 2 - } - ], - "output": [ - { - "t": "Sum", - "s": "Unit", - "size": 2 - } - ] - } - } - }, - { - "parent": 1, - "op": "Input", - "types": [ - { - "t": "Sum", - "s": "Unit", - "size": 2 - }, - { - "t": "Sum", - "s": "Unit", - "size": 2 - } - ] - }, - { - "parent": 1, - "op": "Output", - "types": [ - { - "t": "Sum", - "s": "Unit", - "size": 2 - } - ] - }, - { - "parent": 1, "op": "DFG", + "name": "main", "signature": { "input": [ { @@ -87,7 +28,7 @@ } }, { - "parent": 4, + "parent": 0, "op": "Input", "types": [ { @@ -103,7 +44,7 @@ ] }, { - "parent": 4, + "parent": 0, "op": "Output", "types": [ { @@ -114,7 +55,7 @@ ] }, { - "parent": 4, + "parent": 0, "op": "Extension", "extension": "logic", "name": "And", @@ -146,27 +87,7 @@ "edges": [ [ [ - 2, - 0 - ], - [ - 4, - 0 - ] - ], - [ - [ - 2, - 1 - ], - [ - 4, - 1 - ] - ], - [ - [ - 4, + 1, 0 ], [ @@ -176,45 +97,30 @@ ], [ [ - 5, - 0 - ], - [ - 7, - 0 - ] - ], - [ - [ - 5, + 1, 1 ], [ - 7, + 3, 1 ] ], [ [ - 7, + 3, 0 ], [ - 6, + 2, 0 ] ] ], "metadata": [ - null, - null, - null, - null, null, null, null, null ], - "encoder": "hugr-rs v0.15.4", - "entrypoint": 4 + "encoder": "hugr-rs v0.15.4" } diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index c8c861e88..2b7901050 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -1,6 +1,7 @@ //! HUGR invariant checks. use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::iter; use itertools::Itertools; @@ -19,7 +20,7 @@ use crate::ops::validate::{ use crate::ops::{NamedOp, OpName, OpTag, OpTrait, OpType, ValidateOp}; use crate::types::EdgeKind; use crate::types::type_param::TypeParam; -use crate::{Direction, Port}; +use crate::{Direction, Port, Visibility}; use super::internal::PortgraphNodeMap; use super::views::HugrView; @@ -59,6 +60,7 @@ impl<'a, H: HugrView> ValidationContext<'a, H> { // Hierarchy and children. No type variables declared outside the root. self.validate_subtree(self.hugr.entrypoint(), &[])?; + self.validate_linkage()?; // In tests we take the opportunity to verify that the hugr // serialization round-trips. We verify the schema of the serialization // format only when an environment variable is set. This allows @@ -80,6 +82,44 @@ impl<'a, H: HugrView> ValidationContext<'a, H> { Ok(()) } + fn validate_linkage(&self) -> Result<(), ValidationError> { + // Map from func_name, for visible funcs only, to *tuple of* + // Node with that func_name, + // Signature, + // bool - true for FuncDefn + let mut node_sig_defn = HashMap::new(); + + for c in self.hugr.children(self.hugr.module_root()) { + let (func_name, sig, is_defn) = match self.hugr.get_optype(c) { + OpType::FuncDecl(fd) if fd.visibility() == &Visibility::Public => { + (fd.func_name(), fd.signature(), false) + } + OpType::FuncDefn(fd) if fd.visibility() == &Visibility::Public => { + (fd.func_name(), fd.signature(), true) + } + _ => continue, + }; + match node_sig_defn.entry(func_name) { + Entry::Vacant(ve) => { + ve.insert((c, sig, is_defn)); + } + Entry::Occupied(oe) => { + // Allow two decls of the same sig (aliasing - we are allowing some laziness here). + // Reject if at least one Defn - either two conflicting impls, + // or Decl+Defn which should have been linked + let (prev_c, prev_sig, prev_defn) = oe.get(); + if prev_sig != &sig || is_defn || *prev_defn { + return Err(ValidationError::DuplicateExport { + link_name: func_name.clone(), + children: [*prev_c, c], + }); + }; + } + } + } + Ok(()) + } + /// Compute the dominator tree for a CFG region, identified by its container /// node. /// @@ -664,6 +704,16 @@ pub enum ValidationError { parent_optype: OpType, source: ChildrenValidationError, }, + /// Multiple, incompatible, nodes with [Visibility::Public] use the same `func_name` + /// in a [Module](super::Module). (Multiple [`FuncDecl`](crate::ops::FuncDecl)s with + /// the same signature are allowed) + #[error("FuncDefn/Decl {} is exported under same name {link_name} as earlier node {}", children[0], children[1])] + DuplicateExport { + /// The `func_name` of a public `FuncDecl` or `FuncDefn` + link_name: String, + /// Two nodes using that name + children: [N; 2], + }, /// The children graph has invalid edges. #[error( "An operation {parent_optype} contains invalid edges between its children: {source}. In parent {parent}, edge from {from:?} port {from_port:?} to {to:?} port {to_port:?}", diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 2162a2ba3..4a9ed6641 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -1,8 +1,10 @@ +use std::borrow::Cow; use std::fs::File; use std::io::BufReader; use std::sync::Arc; use cool_asserts::assert_matches; +use rstest::rstest; use super::*; use crate::builder::test::closed_dfg_root_hugr; @@ -15,9 +17,9 @@ use crate::extension::prelude::{bool_t, qb_t, usize_t}; use crate::extension::{Extension, ExtensionRegistry, PRELUDE, TypeDefBound}; use crate::hugr::HugrMut; use crate::hugr::internal::HugrMutInternals; -use crate::ops::dataflow::IOTrait; +use crate::ops::dataflow::{DataflowParent, IOTrait}; use crate::ops::handle::NodeHandle; -use crate::ops::{self, OpType, Value}; +use crate::ops::{self, FuncDecl, FuncDefn, OpType, Value}; use crate::std_extensions::logic::LogicOp; use crate::std_extensions::logic::test::{and_op, or_op}; use crate::types::type_param::{TermTypeError, TypeArg}; @@ -27,12 +29,16 @@ use crate::types::{ }; use crate::{Direction, Hugr, IncomingPort, Node, const_extension_ids, test_file, type_row}; -/// Creates a hugr with a single function definition that copies a bit `copies` times. +/// Creates a hugr with a single, public, function definition that copies a bit `copies` times. /// /// Returns the hugr and the node index of the definition. fn make_simple_hugr(copies: usize) -> (Hugr, Node) { - let def_op: OpType = - ops::FuncDefn::new("main", Signature::new(bool_t(), vec![bool_t(); copies])).into(); + let def_op: OpType = FuncDefn::new_vis( + "main", + Signature::new(bool_t(), vec![bool_t(); copies]), + Visibility::Public, + ) + .into(); let mut b = Hugr::default(); let root = b.entrypoint(); @@ -126,7 +132,7 @@ fn children_restrictions() { // Add a definition without children let def_sig = Signature::new(vec![bool_t()], vec![bool_t(), bool_t()]); - let new_def = b.add_node_with_parent(root, ops::FuncDefn::new("main", def_sig)); + let new_def = b.add_node_with_parent(root, FuncDefn::new("main", def_sig)); assert_matches!( b.validate(), Err(ValidationError::ContainerWithoutChildren { node, .. }) => assert_eq!(node, new_def) @@ -276,7 +282,7 @@ fn identity_hugr_with_type(t: Type) -> (Hugr, Node) { let def = b.add_node_with_parent( b.entrypoint(), - ops::FuncDefn::new("main", Signature::new_endo(row.clone())), + FuncDefn::new("main", Signature::new_endo(row.clone())), ); let input = b.add_node_with_parent(def, ops::Input::new(row.clone())); @@ -754,3 +760,74 @@ fn cfg_entry_io_bug() -> Result<(), Box> { Ok(()) } + +fn sig1() -> Signature { + Signature::new_endo(bool_t()) +} + +fn sig2() -> Signature { + Signature::new_endo(usize_t()) +} + +#[rstest] +// Private FuncDefns never conflict even if different sig +#[case( + FuncDefn::new_vis("foo", sig1(), Visibility::Public), + FuncDefn::new("foo", sig2()), + None +)] +#[case(FuncDefn::new("foo", sig1()), FuncDecl::new("foo", sig2()), None)] +// Public FuncDefn conflicts with anything Public even if same sig +#[case( + FuncDefn::new_vis("foo", sig1(), Visibility::Public), + FuncDefn::new_vis("foo", sig1(), Visibility::Public), + Some("foo") +)] +#[case( + FuncDefn::new_vis("foo", sig1(), Visibility::Public), + FuncDecl::new("foo", sig1()), + Some("foo") +)] +// Two public FuncDecls are ok with same sig +#[case(FuncDecl::new("foo", sig1()), FuncDecl::new("foo", sig1()), None)] +// But two public FuncDecls not ok if different sigs +#[case( + FuncDecl::new("foo", sig1()), + FuncDecl::new("foo", sig2()), + Some("foo") +)] +fn validate_linkage( + #[case] f1: impl Into, + #[case] f2: impl Into, + #[case] err: Option<&str>, +) { + let mut h = Hugr::new(); + let [n1, n2] = [f1.into(), f2.into()].map(|f| { + let def_sig = f + .as_func_defn() + .map(FuncDefn::inner_signature) + .map(Cow::into_owned); + let n = h.add_node_with_parent(h.module_root(), f); + if let Some(Signature { input, output }) = def_sig { + let i = h.add_node_with_parent(n, ops::Input::new(input)); + let o = h.add_node_with_parent(n, ops::Output::new(output)); + h.connect(i, 0, o, 0); // Assume all sig's used in test are 1-ary endomorphic + } + n + }); + let r = h.validate(); + match err { + None => r.unwrap(), + Some(name) => { + let Err(ValidationError::DuplicateExport { + link_name, + children, + }) = r + else { + panic!("validate() should have produced DuplicateExport error not {r:?}") + }; + assert_eq!(link_name, name); + assert!(children == [n1, n2] || children == [n2, n1]); + } + } +} diff --git a/hugr-core/src/hugr/views/root_checked/dfg.rs b/hugr-core/src/hugr/views/root_checked/dfg.rs index 684f3f003..160b4a311 100644 --- a/hugr-core/src/hugr/views/root_checked/dfg.rs +++ b/hugr-core/src/hugr/views/root_checked/dfg.rs @@ -9,8 +9,7 @@ use crate::{ IncomingPort, OutgoingPort, PortIndex, hugr::HugrMut, ops::{ - DFG, FuncDefn, Input, OpTrait, OpType, Output, - dataflow::IOTrait, + OpTrait, OpType, handle::{DataflowParentID, DfgID}, }, types::{NoRV, Signature, TypeBase}, @@ -179,20 +178,19 @@ fn disconnect_all(hugr: &mut H, node: H::Node) { } fn update_signature(hugr: &mut H, node: H::Node, new_sig: &Signature) { - let new_op: OpType = match hugr.get_optype(node) { - OpType::DFG(_) => DFG { - signature: new_sig.clone(), + match hugr.optype_mut(node) { + OpType::DFG(dfg) => { + dfg.signature = new_sig.clone(); } - .into(), - OpType::FuncDefn(fn_def_op) => { - FuncDefn::new(fn_def_op.func_name().clone(), new_sig.clone()).into() + OpType::FuncDefn(fn_def_op) => *fn_def_op.signature_mut() = new_sig.clone().into(), + OpType::Input(inp) => { + inp.types = new_sig.input().clone(); } - OpType::Input(_) => Input::new(new_sig.input().clone()).into(), - OpType::Output(_) => Output::new(new_sig.output().clone()).into(), + OpType::Output(out) => out.types = new_sig.output().clone(), _ => panic!("only update signature of DFG, FuncDefn, Input, or Output"), }; + let new_op = hugr.get_optype(node); hugr.set_num_ports(node, new_op.input_count(), new_op.output_count()); - hugr.replace_op(node, new_op); } fn check_valid_inputs( diff --git a/hugr-core/src/import.rs b/hugr-core/src/import.rs index fbad01434..91feada92 100644 --- a/hugr-core/src/import.rs +++ b/hugr-core/src/import.rs @@ -939,7 +939,11 @@ impl<'a> Context<'a> { parent: Node, ) -> Result { self.import_poly_func_type(node_id, *symbol, |ctx, signature| { - let optype = OpType::FuncDefn(FuncDefn::new(symbol.name, signature)); + let optype = OpType::FuncDefn(FuncDefn::new_vis( + symbol.name, + signature, + symbol.visibility.clone().into(), + )); let node = ctx.make_node(node_id, optype, parent)?; @@ -964,7 +968,11 @@ impl<'a> Context<'a> { parent: Node, ) -> Result { self.import_poly_func_type(node_id, *symbol, |ctx, signature| { - let optype = OpType::FuncDecl(FuncDecl::new(symbol.name, signature)); + let optype = OpType::FuncDecl(FuncDecl::new_vis( + symbol.name, + signature, + symbol.visibility.clone().into(), + )); let node = ctx.make_node(node_id, optype, parent)?; Ok(node) }) diff --git a/hugr-core/src/lib.rs b/hugr-core/src/lib.rs index e5f57d2a8..862b8dee8 100644 --- a/hugr-core/src/lib.rs +++ b/hugr-core/src/lib.rs @@ -24,7 +24,8 @@ pub mod types; pub mod utils; pub use crate::core::{ - CircuitUnit, Direction, IncomingPort, Node, NodeIndex, OutgoingPort, Port, PortIndex, Wire, + CircuitUnit, Direction, IncomingPort, Node, NodeIndex, OutgoingPort, Port, PortIndex, + Visibility, Wire, }; pub use crate::extension::Extension; pub use crate::hugr::{Hugr, HugrView, SimpleReplacement}; diff --git a/hugr-core/src/ops/module.rs b/hugr-core/src/ops/module.rs index db2b81f9f..eda121f23 100644 --- a/hugr-core/src/ops/module.rs +++ b/hugr-core/src/ops/module.rs @@ -9,12 +9,11 @@ use { ::proptest_derive::Arbitrary, }; -use crate::types::{EdgeKind, PolyFuncType, Signature}; -use crate::types::{Type, TypeBound}; +use crate::Visibility; +use crate::types::{EdgeKind, PolyFuncType, Signature, Type, TypeBound}; -use super::StaticTag; use super::dataflow::DataflowParent; -use super::{OpTag, OpTrait, impl_op_name}; +use super::{OpTag, OpTrait, StaticTag, impl_op_name}; /// The root of a module, parent of all other `OpType`s. #[derive(Debug, Clone, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)] @@ -57,14 +56,31 @@ pub struct FuncDefn { #[cfg_attr(test, proptest(strategy = "any_nonempty_string()"))] name: String, signature: PolyFuncType, + #[serde(default = "priv_vis")] // sadly serde does not pick this up from the schema + visibility: Visibility, +} + +fn priv_vis() -> Visibility { + Visibility::Private } impl FuncDefn { - /// Create a new instance with the given name and signature + /// Create a new, [Visibility::Private], instance with the given name and signature. + /// See also [Self::new_vis]. pub fn new(name: impl Into, signature: impl Into) -> Self { + Self::new_vis(name, signature, Visibility::Private) + } + + /// Create a new instance with the specified name and visibility + pub fn new_vis( + name: impl Into, + signature: impl Into, + visibility: Visibility, + ) -> Self { Self { name: name.into(), signature: signature.into(), + visibility, } } @@ -87,6 +103,16 @@ impl FuncDefn { pub fn signature_mut(&mut self) -> &mut PolyFuncType { &mut self.signature } + + /// The visibility of the function, e.g. for linking + pub fn visibility(&self) -> &Visibility { + &self.visibility + } + + /// Allows changing [Self::visibility] + pub fn visibility_mut(&mut self) -> &mut Visibility { + &mut self.visibility + } } impl_op_name!(FuncDefn); @@ -123,14 +149,32 @@ pub struct FuncDecl { #[cfg_attr(test, proptest(strategy = "any_nonempty_string()"))] name: String, signature: PolyFuncType, + // (again) sadly serde does not pick this up from the schema + #[serde(default = "pub_vis")] // Note opposite of FuncDefn + visibility: Visibility, +} + +fn pub_vis() -> Visibility { + Visibility::Public } impl FuncDecl { - /// Create a new instance with the given name and signature + /// Create a new [Visibility::Public] instance with the given name and signature. + /// See also [Self::new_vis] pub fn new(name: impl Into, signature: impl Into) -> Self { + Self::new_vis(name, signature, Visibility::Public) + } + + /// Create a new instance with the given name, signature and visibility + pub fn new_vis( + name: impl Into, + signature: impl Into, + visibility: Visibility, + ) -> Self { Self { name: name.into(), signature: signature.into(), + visibility, } } @@ -139,11 +183,21 @@ impl FuncDecl { &self.name } + /// The visibility of the function, e.g. for linking + pub fn visibility(&self) -> &Visibility { + &self.visibility + } + /// Allows mutating the name of the function (as per [Self::func_name]) pub fn func_name_mut(&mut self) -> &mut String { &mut self.name } + /// Allows mutating the [Self::visibility] of the function + pub fn visibility_mut(&mut self) -> &mut Visibility { + &mut self.visibility + } + /// Gets the signature of the function pub fn signature(&self) -> &PolyFuncType { &self.signature diff --git a/hugr-core/tests/snapshots/model__roundtrip_alias.snap b/hugr-core/tests/snapshots/model__roundtrip_alias.snap index 5f0b44daf..6174d8c74 100644 --- a/hugr-core/tests/snapshots/model__roundtrip_alias.snap +++ b/hugr-core/tests/snapshots/model__roundtrip_alias.snap @@ -1,6 +1,6 @@ --- source: hugr-core/tests/model.rs -expression: "roundtrip(include_str!(\"../../hugr-model/tests/fixtures/model-alias.edn\"))" +expression: ast --- (hugr 0) diff --git a/hugr-model/capnp/hugr-v0.capnp b/hugr-model/capnp/hugr-v0.capnp index f69beb18f..6fbd03a58 100644 --- a/hugr-model/capnp/hugr-v0.capnp +++ b/hugr-model/capnp/hugr-v0.capnp @@ -61,6 +61,7 @@ struct Operation { } struct Symbol { + visibility @4 :Visibility; name @0 :Text; params @1 :List(Param); constraints @2 :List(TermId); @@ -120,3 +121,8 @@ struct Param { name @0 :Text; type @1 :TermId; } + +enum Visibility { + private @0; + public @1; +} diff --git a/hugr-model/src/capnp/hugr_v0_capnp.rs b/hugr-model/src/capnp/hugr_v0_capnp.rs index aea608cfd..8723327ac 100644 --- a/hugr-model/src/capnp/hugr_v0_capnp.rs +++ b/hugr-model/src/capnp/hugr_v0_capnp.rs @@ -2004,6 +2004,10 @@ pub mod symbol { pub fn get_signature(self) -> u32 { self.reader.get_data_field::(0) } + #[inline] + pub fn get_visibility(self) -> ::core::result::Result { + ::core::convert::TryInto::try_into(self.reader.get_data_field::(2)) + } } pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> } @@ -2114,6 +2118,14 @@ pub mod symbol { pub fn set_signature(&mut self, value: u32) { self.builder.set_data_field::(0, value); } + #[inline] + pub fn get_visibility(self) -> ::core::result::Result { + ::core::convert::TryInto::try_into(self.builder.get_data_field::(2)) + } + #[inline] + pub fn set_visibility(&mut self, value: crate::hugr_v0_capnp::Visibility) { + self.builder.set_data_field::(2, value as u16); + } } pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline } @@ -2125,7 +2137,7 @@ pub mod symbol { impl Pipeline { } mod _private { - pub static ENCODED_NODE: [::capnp::Word; 88] = [ + pub static ENCODED_NODE: [::capnp::Word; 104] = [ ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0), ::capnp::word(63, 209, 84, 70, 225, 154, 206, 223), ::capnp::word(20, 0, 0, 0, 1, 0, 1, 0), @@ -2135,7 +2147,7 @@ pub mod symbol { ::capnp::word(21, 0, 0, 0, 218, 0, 0, 0), ::capnp::word(33, 0, 0, 0, 7, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(29, 0, 0, 0, 231, 0, 0, 0), + ::capnp::word(29, 0, 0, 0, 31, 1, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), ::capnp::word(99, 97, 112, 110, 112, 47, 104, 117), @@ -2143,35 +2155,42 @@ pub mod symbol { ::capnp::word(112, 110, 112, 58, 83, 121, 109, 98), ::capnp::word(111, 108, 0, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0), - ::capnp::word(16, 0, 0, 0, 3, 0, 4, 0), - ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(20, 0, 0, 0, 3, 0, 4, 0), + ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(97, 0, 0, 0, 42, 0, 0, 0), + ::capnp::word(125, 0, 0, 0, 42, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(92, 0, 0, 0, 3, 0, 1, 0), - ::capnp::word(104, 0, 0, 0, 2, 0, 1, 0), - ::capnp::word(1, 0, 0, 0, 1, 0, 0, 0), + ::capnp::word(120, 0, 0, 0, 3, 0, 1, 0), + ::capnp::word(132, 0, 0, 0, 2, 0, 1, 0), + ::capnp::word(2, 0, 0, 0, 1, 0, 0, 0), ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(101, 0, 0, 0, 58, 0, 0, 0), + ::capnp::word(129, 0, 0, 0, 58, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(96, 0, 0, 0, 3, 0, 1, 0), - ::capnp::word(124, 0, 0, 0, 2, 0, 1, 0), - ::capnp::word(2, 0, 0, 0, 2, 0, 0, 0), + ::capnp::word(124, 0, 0, 0, 3, 0, 1, 0), + ::capnp::word(152, 0, 0, 0, 2, 0, 1, 0), + ::capnp::word(3, 0, 0, 0, 2, 0, 0, 0), ::capnp::word(0, 0, 1, 0, 2, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(121, 0, 0, 0, 98, 0, 0, 0), + ::capnp::word(149, 0, 0, 0, 98, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(120, 0, 0, 0, 3, 0, 1, 0), - ::capnp::word(148, 0, 0, 0, 2, 0, 1, 0), - ::capnp::word(3, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(148, 0, 0, 0, 3, 0, 1, 0), + ::capnp::word(176, 0, 0, 0, 2, 0, 1, 0), + ::capnp::word(4, 0, 0, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 1, 0, 3, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(145, 0, 0, 0, 82, 0, 0, 0), + ::capnp::word(173, 0, 0, 0, 82, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), - ::capnp::word(144, 0, 0, 0, 3, 0, 1, 0), - ::capnp::word(156, 0, 0, 0, 2, 0, 1, 0), + ::capnp::word(172, 0, 0, 0, 3, 0, 1, 0), + ::capnp::word(184, 0, 0, 0, 2, 0, 1, 0), + ::capnp::word(0, 0, 0, 0, 2, 0, 0, 0), + ::capnp::word(0, 0, 1, 0, 4, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(181, 0, 0, 0, 90, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(180, 0, 0, 0, 3, 0, 1, 0), + ::capnp::word(192, 0, 0, 0, 2, 0, 1, 0), ::capnp::word(110, 97, 109, 101, 0, 0, 0, 0), ::capnp::word(12, 0, 0, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), @@ -2214,6 +2233,15 @@ pub mod symbol { ::capnp::word(8, 0, 0, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(118, 105, 115, 105, 98, 105, 108, 105), + ::capnp::word(116, 121, 0, 0, 0, 0, 0, 0), + ::capnp::word(15, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(1, 131, 104, 122, 242, 21, 131, 141), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(15, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), ]; pub fn get_field_types(index: u16) -> ::capnp::introspect::Type { match index { @@ -2221,6 +2249,7 @@ pub mod symbol { 1 => <::capnp::struct_list::Owned as ::capnp::introspect::Introspect>::introspect(), 2 => <::capnp::primitive_list::Owned as ::capnp::introspect::Introspect>::introspect(), 3 => ::introspect(), + 4 => ::introspect(), _ => panic!("invalid field index {}", index), } } @@ -2233,9 +2262,9 @@ pub mod symbol { members_by_discriminant: MEMBERS_BY_DISCRIMINANT, members_by_name: MEMBERS_BY_NAME, }; - pub static NONUNION_MEMBERS : &[u16] = &[0,1,2,3]; + pub static NONUNION_MEMBERS : &[u16] = &[0,1,2,3,4]; pub static MEMBERS_BY_DISCRIMINANT : &[u16] = &[]; - pub static MEMBERS_BY_NAME : &[u16] = &[2,0,1,3]; + pub static MEMBERS_BY_NAME : &[u16] = &[2,0,1,3,4]; pub const TYPE_ID: u64 = 0xdfce_9ae1_4654_d13f; } } @@ -4491,3 +4520,67 @@ pub mod param { pub const TYPE_ID: u64 = 0xd335_a781_55c7_49e8; } } + +#[repr(u16)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Visibility { + Private = 0, + Public = 1, +} + +impl ::capnp::introspect::Introspect for Visibility { + fn introspect() -> ::capnp::introspect::Type { ::capnp::introspect::TypeVariant::Enum(::capnp::introspect::RawEnumSchema { encoded_node: &visibility::ENCODED_NODE, annotation_types: visibility::get_annotation_types }).into() } +} +impl ::core::convert::From for ::capnp::dynamic_value::Reader<'_> { + fn from(e: Visibility) -> Self { ::capnp::dynamic_value::Enum::new(e.into(), ::capnp::introspect::RawEnumSchema { encoded_node: &visibility::ENCODED_NODE, annotation_types: visibility::get_annotation_types }.into()).into() } +} +impl ::core::convert::TryFrom for Visibility { + type Error = ::capnp::NotInSchema; + fn try_from(value: u16) -> ::core::result::Result>::Error> { + match value { + 0 => ::core::result::Result::Ok(Self::Private), + 1 => ::core::result::Result::Ok(Self::Public), + n => ::core::result::Result::Err(::capnp::NotInSchema(n)), + } + } +} +impl From for u16 { + #[inline] + fn from(x: Visibility) -> u16 { x as u16 } +} +impl ::capnp::traits::HasTypeId for Visibility { + const TYPE_ID: u64 = 0x8d83_15f2_7a68_8301u64; +} +mod visibility { +pub static ENCODED_NODE: [::capnp::Word; 26] = [ + ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0), + ::capnp::word(1, 131, 104, 122, 242, 21, 131, 141), + ::capnp::word(20, 0, 0, 0, 2, 0, 0, 0), + ::capnp::word(1, 150, 80, 40, 197, 50, 43, 224), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(21, 0, 0, 0, 250, 0, 0, 0), + ::capnp::word(33, 0, 0, 0, 7, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(29, 0, 0, 0, 55, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(99, 97, 112, 110, 112, 47, 104, 117), + ::capnp::word(103, 114, 45, 118, 48, 46, 99, 97), + ::capnp::word(112, 110, 112, 58, 86, 105, 115, 105), + ::capnp::word(98, 105, 108, 105, 116, 121, 0, 0), + ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0), + ::capnp::word(8, 0, 0, 0, 1, 0, 2, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(17, 0, 0, 0, 66, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(9, 0, 0, 0, 58, 0, 0, 0), + ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0), + ::capnp::word(112, 114, 105, 118, 97, 116, 101, 0), + ::capnp::word(112, 117, 98, 108, 105, 99, 0, 0), +]; +pub fn get_annotation_types(child_index: Option, index: u32) -> ::capnp::introspect::Type { + panic!("invalid annotation indices ({:?}, {}) ", child_index, index) +} +} diff --git a/hugr-model/src/v0/ast/hugr.pest b/hugr-model/src/v0/ast/hugr.pest index 698f32b05..8c70ad4dc 100644 --- a/hugr-model/src/v0/ast/hugr.pest +++ b/hugr-model/src/v0/ast/hugr.pest @@ -16,6 +16,7 @@ reserved = @{ | "list" | "meta" | "signature" + | "pub" | "dfg" | "cfg" | "block" @@ -79,7 +80,7 @@ node_cond = { "(" ~ "cond" ~ port_lists? ~ signature? ~ meta* ~ reg node_import = { "(" ~ "import" ~ symbol_name ~ meta* ~ ")" } node_custom = { "(" ~ term ~ port_lists? ~ signature? ~ meta* ~ region* ~ ")" } -symbol = { symbol_name ~ param* ~ where_clause* ~ term } +symbol = { "pub"? ~ symbol_name ~ param* ~ where_clause* ~ term } signature = { "(" ~ "signature" ~ term ~ ")" } param = { "(" ~ "param" ~ term_var ~ term ~ ")" } diff --git a/hugr-model/src/v0/ast/mod.rs b/hugr-model/src/v0/ast/mod.rs index faee6f827..51d327e54 100644 --- a/hugr-model/src/v0/ast/mod.rs +++ b/hugr-model/src/v0/ast/mod.rs @@ -25,7 +25,7 @@ use std::sync::Arc; use bumpalo::Bump; use super::table::{self}; -use super::{LinkName, Literal, RegionKind, SymbolName, VarName}; +use super::{LinkName, Literal, RegionKind, SymbolName, VarName, Visibility}; mod parse; mod print; @@ -194,6 +194,8 @@ impl Operation { /// [`table::Symbol`]: crate::v0::table::Symbol #[derive(Debug, Clone, PartialEq, Eq)] pub struct Symbol { + /// The visibility of the symbol. + pub visibility: Visibility, /// The name of the symbol. pub name: SymbolName, /// The parameters of the symbol. diff --git a/hugr-model/src/v0/ast/parse.rs b/hugr-model/src/v0/ast/parse.rs index d1d5dff74..56fb2a0f2 100644 --- a/hugr-model/src/v0/ast/parse.rs +++ b/hugr-model/src/v0/ast/parse.rs @@ -28,7 +28,7 @@ use thiserror::Error; use crate::v0::ast::{LinkName, Module, Operation, SeqPart}; use crate::v0::{Literal, RegionKind}; -use super::{Node, Package, Param, Region, Symbol, VarName}; +use super::{Node, Package, Param, Region, Symbol, VarName, Visibility}; use super::{SymbolName, Term}; mod pest_parser { @@ -292,6 +292,14 @@ fn parse_param(pair: Pair) -> ParseResult { fn parse_symbol(pair: Pair) -> ParseResult { debug_assert_eq!(Rule::symbol, pair.as_rule()); + // The optional "pub" in the grammar seems to disappear in the Rule, making this + // a bit hacky. If we defined a rule for visibility as a choice between different + // specifiers (i.e. pub / empty), then presumably we could avoid this `starts-with`. + let visibility = if pair.as_str().starts_with("pub") { + Visibility::Public + } else { + Visibility::Private + }; let mut pairs = pair.into_inner(); let name = parse_symbol_name(pairs.next().unwrap())?; let params = parse_params(&mut pairs)?; @@ -299,6 +307,7 @@ fn parse_symbol(pair: Pair) -> ParseResult { let signature = parse_term(pairs.next().unwrap())?; Ok(Symbol { + visibility, name, params, constraints, diff --git a/hugr-model/src/v0/ast/print.rs b/hugr-model/src/v0/ast/print.rs index dd47602a4..1db9a928b 100644 --- a/hugr-model/src/v0/ast/print.rs +++ b/hugr-model/src/v0/ast/print.rs @@ -7,7 +7,7 @@ use crate::v0::{Literal, RegionKind}; use super::{ LinkName, Module, Node, Operation, Package, Param, Region, SeqPart, Symbol, SymbolName, Term, - VarName, + VarName, Visibility, }; struct Printer<'a> { @@ -369,6 +369,11 @@ fn print_region<'a>(printer: &mut Printer<'a>, region: &'a Region) { } fn print_symbol<'a>(printer: &mut Printer<'a>, symbol: &'a Symbol) { + match symbol.visibility { + Visibility::Private => (), + Visibility::Public => printer.text("pub"), + } + print_symbol_name(printer, &symbol.name); for param in &symbol.params { diff --git a/hugr-model/src/v0/ast/python.rs b/hugr-model/src/v0/ast/python.rs index 90ef22a81..b70d9447c 100644 --- a/hugr-model/src/v0/ast/python.rs +++ b/hugr-model/src/v0/ast/python.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use crate::v0::Visibility; + use super::{Module, Node, Operation, Package, Param, Region, SeqPart, Symbol, Term}; use pyo3::{ Bound, PyAny, PyResult, @@ -139,13 +141,41 @@ impl<'py> pyo3::IntoPyObject<'py> for &Param { } } +impl<'py> pyo3::FromPyObject<'py> for Visibility { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + match ob.str()?.to_str()? { + "Public" => Ok(Visibility::Public), + "Private" => Ok(Visibility::Private), + s => Err(PyTypeError::new_err(format!( + "Expected \"Public\" or \"Private\", got {s}", + ))), + } + } +} + +impl<'py> pyo3::IntoPyObject<'py> for &Visibility { + type Target = pyo3::types::PyAny; + type Output = pyo3::Bound<'py, Self::Target>; + type Error = pyo3::PyErr; + + fn into_pyobject(self, py: pyo3::Python<'py>) -> Result { + let s = match self { + Visibility::Private => "Private", + Visibility::Public => "Public", + }; + Ok(pyo3::types::PyString::new(py, s).into_any()) + } +} + impl<'py> pyo3::FromPyObject<'py> for Symbol { fn extract_bound(symbol: &Bound<'py, PyAny>) -> PyResult { let name = symbol.getattr("name")?.extract()?; let params: Vec<_> = symbol.getattr("params")?.extract()?; + let visibility = symbol.getattr("visibility")?.extract()?; let constraints: Vec<_> = symbol.getattr("constraints")?.extract()?; let signature = symbol.getattr("signature")?.extract()?; Ok(Self { + visibility, name, signature, params: params.into(), @@ -164,6 +194,7 @@ impl<'py> pyo3::IntoPyObject<'py> for &Symbol { let py_class = py_module.getattr("Symbol")?; py_class.call1(( self.name.as_ref(), + &self.visibility, self.params.as_ref(), self.constraints.as_ref(), &self.signature, @@ -425,5 +456,6 @@ impl_into_pyobject_owned!(Symbol); impl_into_pyobject_owned!(Module); impl_into_pyobject_owned!(Package); impl_into_pyobject_owned!(Node); +impl_into_pyobject_owned!(Visibility); impl_into_pyobject_owned!(Region); impl_into_pyobject_owned!(Operation); diff --git a/hugr-model/src/v0/ast/resolve.rs b/hugr-model/src/v0/ast/resolve.rs index 806e02176..faf825117 100644 --- a/hugr-model/src/v0/ast/resolve.rs +++ b/hugr-model/src/v0/ast/resolve.rs @@ -289,11 +289,13 @@ impl<'a> Context<'a> { fn resolve_symbol(&mut self, symbol: &'a Symbol) -> BuildResult<&'a table::Symbol<'a>> { let name = symbol.name.as_ref(); + let visibility = &symbol.visibility; let params = self.resolve_params(&symbol.params)?; let constraints = self.resolve_terms(&symbol.constraints)?; let signature = self.resolve_term(&symbol.signature)?; Ok(self.bump.alloc(table::Symbol { + visibility, name, params, constraints, diff --git a/hugr-model/src/v0/ast/view.rs b/hugr-model/src/v0/ast/view.rs index 8feb15853..8c3803810 100644 --- a/hugr-model/src/v0/ast/view.rs +++ b/hugr-model/src/v0/ast/view.rs @@ -91,11 +91,13 @@ impl<'a> View<'a, table::SeqPart> for SeqPart { impl<'a> View<'a, table::Symbol<'a>> for Symbol { fn view(module: &'a table::Module<'a>, id: table::Symbol<'a>) -> Option { + let visibility = id.visibility.clone(); let name = SymbolName::new(id.name); let params = module.view(id.params)?; let constraints = module.view(id.constraints)?; let signature = module.view(id.signature)?; Some(Symbol { + visibility, name, params, constraints, diff --git a/hugr-model/src/v0/binary/read.rs b/hugr-model/src/v0/binary/read.rs index 001a805cc..c16c8f3b7 100644 --- a/hugr-model/src/v0/binary/read.rs +++ b/hugr-model/src/v0/binary/read.rs @@ -126,88 +126,21 @@ fn read_operation<'a>( Which::Dfg(()) => table::Operation::Dfg, Which::Cfg(()) => table::Operation::Cfg, Which::Block(()) => table::Operation::Block, - Which::FuncDefn(reader) => { - let reader = reader?; - let name = bump.alloc_str(reader.get_name()?.to_str()?); - let params = read_list!(bump, reader.get_params()?, read_param); - let constraints = read_scalar_list!(bump, reader, get_constraints, table::TermId); - let signature = table::TermId(reader.get_signature()); - let symbol = bump.alloc(table::Symbol { - name, - params, - constraints, - signature, - }); - table::Operation::DefineFunc(symbol) - } - Which::FuncDecl(reader) => { - let reader = reader?; - let name = bump.alloc_str(reader.get_name()?.to_str()?); - let params = read_list!(bump, reader.get_params()?, read_param); - let constraints = read_scalar_list!(bump, reader, get_constraints, table::TermId); - let signature = table::TermId(reader.get_signature()); - let symbol = bump.alloc(table::Symbol { - name, - params, - constraints, - signature, - }); - table::Operation::DeclareFunc(symbol) - } + Which::FuncDefn(reader) => table::Operation::DefineFunc(read_symbol(bump, reader?, None)?), + Which::FuncDecl(reader) => table::Operation::DeclareFunc(read_symbol(bump, reader?, None)?), Which::AliasDefn(reader) => { let symbol = reader.get_symbol()?; let value = table::TermId(reader.get_value()); - let name = bump.alloc_str(symbol.get_name()?.to_str()?); - let params = read_list!(bump, symbol.get_params()?, read_param); - let signature = table::TermId(symbol.get_signature()); - let symbol = bump.alloc(table::Symbol { - name, - params, - constraints: &[], - signature, - }); - table::Operation::DefineAlias(symbol, value) + table::Operation::DefineAlias(read_symbol(bump, symbol, Some(&[]))?, value) } Which::AliasDecl(reader) => { - let reader = reader?; - let name = bump.alloc_str(reader.get_name()?.to_str()?); - let params = read_list!(bump, reader.get_params()?, read_param); - let signature = table::TermId(reader.get_signature()); - let symbol = bump.alloc(table::Symbol { - name, - params, - constraints: &[], - signature, - }); - table::Operation::DeclareAlias(symbol) + table::Operation::DeclareAlias(read_symbol(bump, reader?, Some(&[]))?) } Which::ConstructorDecl(reader) => { - let reader = reader?; - let name = bump.alloc_str(reader.get_name()?.to_str()?); - let params = read_list!(bump, reader.get_params()?, read_param); - let constraints = read_scalar_list!(bump, reader, get_constraints, table::TermId); - let signature = table::TermId(reader.get_signature()); - let symbol = bump.alloc(table::Symbol { - name, - params, - constraints, - signature, - }); - table::Operation::DeclareConstructor(symbol) + table::Operation::DeclareConstructor(read_symbol(bump, reader?, None)?) } Which::OperationDecl(reader) => { - let reader = reader?; - let name = bump.alloc_str(reader.get_name()?.to_str()?); - let params = read_list!(bump, reader.get_params()?, read_param); - let constraints = read_scalar_list!(bump, reader, get_constraints, table::TermId); - let signature = table::TermId(reader.get_signature()); - let symbol = bump.alloc(table::Symbol { - name, - params, - constraints, - signature, - }); - table::Operation::DeclareOperation(symbol) + table::Operation::DeclareOperation(read_symbol(bump, reader?, None)?) } Which::Custom(operation) => table::Operation::Custom(table::TermId(operation)), Which::TailLoop(()) => table::Operation::TailLoop, @@ -257,6 +190,34 @@ fn read_region_scope(reader: hugr_capnp::region_scope::Reader) -> ReadResult( + bump: &'a Bump, + reader: hugr_capnp::symbol::Reader, + constraints: Option<&'a [table::TermId]>, +) -> ReadResult<&'a mut table::Symbol<'a>> { + let name = bump.alloc_str(reader.get_name()?.to_str()?); + let visibility = match reader.get_visibility() { + Ok(hugr_capnp::Visibility::Private) => model::Visibility::Private, + Ok(hugr_capnp::Visibility::Public) => model::Visibility::Public, + Err(_) => model::Visibility::default(), + }; + let visibility = bump.alloc(visibility); + let params = read_list!(bump, reader.get_params()?, read_param); + let constraints = match constraints { + Some(cs) => cs, + None => read_scalar_list!(bump, reader, get_constraints, table::TermId), + }; + let signature = table::TermId(reader.get_signature()); + Ok(bump.alloc(table::Symbol { + visibility, + name, + params, + constraints, + signature, + })) +} + fn read_term<'a>(bump: &'a Bump, reader: hugr_capnp::term::Reader) -> ReadResult> { use hugr_capnp::term::Which; Ok(match reader.which()? { diff --git a/hugr-model/src/v0/binary/write.rs b/hugr-model/src/v0/binary/write.rs index e9b76eca1..02e40dd9b 100644 --- a/hugr-model/src/v0/binary/write.rs +++ b/hugr-model/src/v0/binary/write.rs @@ -110,6 +110,10 @@ fn write_operation(mut builder: hugr_capnp::operation::Builder, operation: &tabl fn write_symbol(mut builder: hugr_capnp::symbol::Builder, symbol: &table::Symbol) { builder.set_name(symbol.name); + builder.set_visibility(match symbol.visibility { + model::Visibility::Private => hugr_capnp::Visibility::Private, + model::Visibility::Public => hugr_capnp::Visibility::Public, + }); write_list!(builder, init_params, write_param, symbol.params); let _ = builder.set_constraints(table::TermId::unwrap_slice(symbol.constraints)); builder.set_signature(symbol.signature.0); diff --git a/hugr-model/src/v0/mod.rs b/hugr-model/src/v0/mod.rs index 27e6605ae..7ccefb3cd 100644 --- a/hugr-model/src/v0/mod.rs +++ b/hugr-model/src/v0/mod.rs @@ -91,6 +91,16 @@ use smol_str::SmolStr; use std::sync::Arc; use table::LinkIndex; +/// Describes how a function or symbol should be acted upon by a linker +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Visibility { + /// The linker should ignore this function or symbol + #[default] + Private, + /// The linker should act upon this function or symbol + Public, +} + /// Core function types. /// /// - **Parameter:** `?inputs : (core.list core.type)` diff --git a/hugr-model/src/v0/table/mod.rs b/hugr-model/src/v0/table/mod.rs index 501305510..f12149908 100644 --- a/hugr-model/src/v0/table/mod.rs +++ b/hugr-model/src/v0/table/mod.rs @@ -29,7 +29,7 @@ use smol_str::SmolStr; use thiserror::Error; mod view; -use super::{Literal, RegionKind, ast}; +use super::{Literal, RegionKind, Visibility, ast}; pub use view::View; /// A package consisting of a sequence of [`Module`]s. @@ -303,6 +303,8 @@ pub struct RegionScope { /// [`ast::Symbol`]: crate::v0::ast::Symbol #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Symbol<'a> { + /// The visibility of the symbol. + pub visibility: &'a Visibility, /// The name of the symbol. pub name: &'a str, /// The static parameters. diff --git a/hugr-persistent/src/persistent_hugr/snapshots/hugr_persistent__persistent_hugr__serial__tests__serde_persistent_hugr.snap b/hugr-persistent/src/persistent_hugr/snapshots/hugr_persistent__persistent_hugr__serial__tests__serde_persistent_hugr.snap index 1a33586a2..e7f544586 100644 --- a/hugr-persistent/src/persistent_hugr/snapshots/hugr_persistent__persistent_hugr__serial__tests__serde_persistent_hugr.snap +++ b/hugr-persistent/src/persistent_hugr/snapshots/hugr_persistent__persistent_hugr__serial__tests__serde_persistent_hugr.snap @@ -6,49 +6,42 @@ expression: "serde_json::to_string_pretty(&ser_per_hugr).unwrap()" "state_space": { "graph": { "nodes": { - "13ee17d11683008e": { + "3fd58bd8c5f2494a": { + "value": { + "Base": { + "hugr": "HUGRiHJv?@{\"modules\":[{\"version\":\"live\",\"nodes\":[{\"parent\":0,\"op\":\"Module\"},{\"parent\":0,\"op\":\"FuncDefn\",\"name\":\"main\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2},{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"visibility\":\"Private\"},{\"parent\":1,\"op\":\"Input\",\"types\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2},{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]},{\"parent\":1,\"op\":\"Output\",\"types\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]},{\"parent\":1,\"op\":\"DFG\",\"signature\":{\"input\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2},{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},{\"parent\":4,\"op\":\"Input\",\"types\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2},{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]},{\"parent\":4,\"op\":\"Output\",\"types\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]},{\"parent\":4,\"op\":\"Extension\",\"extension\":\"logic\",\"name\":\"Not\",\"args\":[],\"signature\":{\"input\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},{\"parent\":4,\"op\":\"Extension\",\"extension\":\"logic\",\"name\":\"Not\",\"args\":[],\"signature\":{\"input\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},{\"parent\":4,\"op\":\"Extension\",\"extension\":\"logic\",\"name\":\"And\",\"args\":[],\"signature\":{\"input\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2},{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}}],\"edges\":[[[2,0],[4,0]],[[2,1],[4,1]],[[4,0],[3,0]],[[5,0],[7,0]],[[5,1],[8,0]],[[7,0],[9,0]],[[8,0],[9,1]],[[9,0],[6,0]]],\"metadata\":[null,null,null,null,null,null,null,null,null,null],\"entrypoint\":4}],\"extensions\":[{\"version\":\"0.1.0\",\"name\":\"arithmetic.conversions\",\"types\":{},\"operations\":{\"bytecast_float64_to_int64\":{\"extension\":\"arithmetic.conversions\",\"name\":\"bytecast_float64_to_int64\",\"description\":\"reinterpret an float64 as an int based on its bytes, with the same endianness\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"BoundedNat\",\"n\":6}],\"bound\":\"C\"}]}},\"binary\":false},\"bytecast_int64_to_float64\":{\"extension\":\"arithmetic.conversions\",\"name\":\"bytecast_int64_to_float64\",\"description\":\"reinterpret an int64 as a float64 based on its bytes, with the same endianness\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"BoundedNat\",\"n\":6}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"convert_s\":{\"extension\":\"arithmetic.conversions\",\"name\":\"convert_s\",\"description\":\"signed int to float\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"convert_u\":{\"extension\":\"arithmetic.conversions\",\"name\":\"convert_u\",\"description\":\"unsigned int to float\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"ifrombool\":{\"extension\":\"arithmetic.conversions\",\"name\":\"ifrombool\",\"description\":\"convert from bool into a 1-bit integer (1 is true, 0 is false)\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"BoundedNat\",\"n\":0}],\"bound\":\"C\"}]}},\"binary\":false},\"ifromusize\":{\"extension\":\"arithmetic.conversions\",\"name\":\"ifromusize\",\"description\":\"convert a usize to a 64b unsigned integer\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"I\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"BoundedNat\",\"n\":6}],\"bound\":\"C\"}]}},\"binary\":false},\"itobool\":{\"extension\":\"arithmetic.conversions\",\"name\":\"itobool\",\"description\":\"convert a 1-bit integer to bool (1 is true, 0 is false)\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"BoundedNat\",\"n\":0}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"binary\":false},\"itostring_s\":{\"extension\":\"arithmetic.conversions\",\"name\":\"itostring_s\",\"description\":\"convert a signed integer to its string representation\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"prelude\",\"id\":\"string\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"itostring_u\":{\"extension\":\"arithmetic.conversions\",\"name\":\"itostring_u\",\"description\":\"convert an unsigned integer to its string representation\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"prelude\",\"id\":\"string\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"itousize\":{\"extension\":\"arithmetic.conversions\",\"name\":\"itousize\",\"description\":\"convert a 64b unsigned integer to its usize representation\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"BoundedNat\",\"n\":6}],\"bound\":\"C\"}],\"output\":[{\"t\":\"I\"}]}},\"binary\":false},\"trunc_s\":{\"extension\":\"arithmetic.conversions\",\"name\":\"trunc_s\",\"description\":\"float to signed int\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"General\",\"rows\":[[{\"t\":\"Opaque\",\"extension\":\"prelude\",\"id\":\"error\",\"args\":[],\"bound\":\"C\"}],[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]]}]}},\"binary\":false},\"trunc_u\":{\"extension\":\"arithmetic.conversions\",\"name\":\"trunc_u\",\"description\":\"float to unsigned int\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"General\",\"rows\":[[{\"t\":\"Opaque\",\"extension\":\"prelude\",\"id\":\"error\",\"args\":[],\"bound\":\"C\"}],[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]]}]}},\"binary\":false}}},{\"version\":\"0.1.0\",\"name\":\"arithmetic.float\",\"types\":{},\"operations\":{\"fabs\":{\"extension\":\"arithmetic.float\",\"name\":\"fabs\",\"description\":\"absolute value\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fadd\":{\"extension\":\"arithmetic.float\",\"name\":\"fadd\",\"description\":\"addition\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fceil\":{\"extension\":\"arithmetic.float\",\"name\":\"fceil\",\"description\":\"ceiling\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fdiv\":{\"extension\":\"arithmetic.float\",\"name\":\"fdiv\",\"description\":\"division\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"feq\":{\"extension\":\"arithmetic.float\",\"name\":\"feq\",\"description\":\"equality test\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"binary\":false},\"ffloor\":{\"extension\":\"arithmetic.float\",\"name\":\"ffloor\",\"description\":\"floor\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fge\":{\"extension\":\"arithmetic.float\",\"name\":\"fge\",\"description\":\"\\\"greater than or equal\\\"\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"binary\":false},\"fgt\":{\"extension\":\"arithmetic.float\",\"name\":\"fgt\",\"description\":\"\\\"greater than\\\"\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"binary\":false},\"fle\":{\"extension\":\"arithmetic.float\",\"name\":\"fle\",\"description\":\"\\\"less than or equal\\\"\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"binary\":false},\"flt\":{\"extension\":\"arithmetic.float\",\"name\":\"flt\",\"description\":\"\\\"less than\\\"\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"binary\":false},\"fmax\":{\"extension\":\"arithmetic.float\",\"name\":\"fmax\",\"description\":\"maximum\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fmin\":{\"extension\":\"arithmetic.float\",\"name\":\"fmin\",\"description\":\"minimum\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fmul\":{\"extension\":\"arithmetic.float\",\"name\":\"fmul\",\"description\":\"multiplication\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fne\":{\"extension\":\"arithmetic.float\",\"name\":\"fne\",\"description\":\"inequality test\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"Unit\",\"size\":2}]}},\"binary\":false},\"fneg\":{\"extension\":\"arithmetic.float\",\"name\":\"fneg\",\"description\":\"negation\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fpow\":{\"extension\":\"arithmetic.float\",\"name\":\"fpow\",\"description\":\"exponentiation\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fround\":{\"extension\":\"arithmetic.float\",\"name\":\"fround\",\"description\":\"round\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"fsub\":{\"extension\":\"arithmetic.float\",\"name\":\"fsub\",\"description\":\"subtraction\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false},\"ftostring\":{\"extension\":\"arithmetic.float\",\"name\":\"ftostring\",\"description\":\"string representation\",\"signature\":{\"params\":[],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.float.types\",\"id\":\"float64\",\"args\":[],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"prelude\",\"id\":\"string\",\"args\":[],\"bound\":\"C\"}]}},\"binary\":false}}},{\"version\":\"0.1.0\",\"name\":\"arithmetic.float.types\",\"types\":{\"float64\":{\"extension\":\"arithmetic.float.types\",\"name\":\"float64\",\"params\":[],\"description\":\"64-bit IEEE 754-2019 floating-point value\",\"bound\":{\"b\":\"Explicit\",\"bound\":\"C\"}}},\"operations\":{}},{\"version\":\"0.1.0\",\"name\":\"arithmetic.int\",\"types\":{},\"operations\":{\"iabs\":{\"extension\":\"arithmetic.int\",\"name\":\"iabs\",\"description\":\"convert signed to unsigned by taking absolute value\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]}},\"binary\":false},\"iadd\":{\"extension\":\"arithmetic.int\",\"name\":\"iadd\",\"description\":\"addition modulo 2^N (signed and unsigned versions are the same op)\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]}},\"binary\":false},\"iand\":{\"extension\":\"arithmetic.int\",\"name\":\"iand\",\"description\":\"bitwise AND\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]}},\"binary\":false},\"idiv_checked_s\":{\"extension\":\"arithmetic.int\",\"name\":\"idiv_checked_s\",\"description\":\"as idivmod_checked_s but discarding the second output\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"General\",\"rows\":[[{\"t\":\"Opaque\",\"extension\":\"prelude\",\"id\":\"error\",\"args\":[],\"bound\":\"C\"}],[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]]}]}},\"binary\":false},\"idiv_checked_u\":{\"extension\":\"arithmetic.int\",\"name\":\"idiv_checked_u\",\"description\":\"as idivmod_checked_u but discarding the second output\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Sum\",\"s\":\"General\",\"rows\":[[{\"t\":\"Opaque\",\"extension\":\"prelude\",\"id\":\"error\",\"args\":[],\"bound\":\"C\"}],[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]]}]}},\"binary\":false},\"idiv_s\":{\"extension\":\"arithmetic.int\",\"name\":\"idiv_s\",\"description\":\"as idivmod_s but discarding the second output\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]}},\"binary\":false},\"idiv_u\":{\"extension\":\"arithmetic.int\",\"name\":\"idiv_u\",\"description\":\"as idivmod_u but discarding the second output\",\"signature\":{\"params\":[{\"tp\":\"BoundedNat\",\"bound\":7}],\"body\":{\"input\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"},{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}],\"output\":[{\"t\":\"Opaque\",\"extension\":\"arithmetic.int.types\",\"id\":\"int\",\"args\":[{\"tya\":\"Variable\",\"idx\":0,\"cached_decl\":{\"tp\":\"BoundedNat\",\"bound\":7}}],\"bound\":\"C\"}]}},\"binary\":false},\"idivmod_checked_s\":{\"extension\":\"arithmetic.int\",\"name\":\"idivmod_checked_s\",\"description\":\"given signed integer -2^{N-1} <= n < 2^{N-1} and unsigned 0 <= m < 2^N, generates signed q and unsigned r where q*m+r=n, 0<=r ops.FuncDefn: poly_func = self.signature.deserialize() return ops.FuncDefn( - self.name, inputs=poly_func.body.input, _outputs=poly_func.body.output + self.name, + params=poly_func.params, + inputs=poly_func.body.input, + _outputs=poly_func.body.output, + visibility=self.visibility, ) @@ -89,9 +95,12 @@ class FuncDecl(BaseOp): op: Literal["FuncDecl"] = "FuncDecl" name: str signature: PolyFuncType + visibility: Visibility = Field(default="Public") def deserialize(self) -> ops.FuncDecl: - return ops.FuncDecl(self.name, self.signature.deserialize()) + return ops.FuncDecl( + self.name, self.signature.deserialize(), visibility=self.visibility + ) class CustomConst(ConfiguredBaseModel): diff --git a/hugr-py/src/hugr/model/__init__.py b/hugr-py/src/hugr/model/__init__.py index d7688e011..1791d5a00 100644 --- a/hugr-py/src/hugr/model/__init__.py +++ b/hugr-py/src/hugr/model/__init__.py @@ -6,6 +6,7 @@ from typing import Protocol import hugr._hugr as rust +from hugr.tys import Visibility class Term(Protocol): @@ -101,6 +102,7 @@ class Symbol: """A named symbol.""" name: str + visibility: Visibility params: Sequence[Param] = field(default_factory=list) constraints: Sequence[Term] = field(default_factory=list) signature: Term = field(default_factory=Wildcard) diff --git a/hugr-py/src/hugr/model/export.py b/hugr-py/src/hugr/model/export.py index a1b3fd532..c3d994aae 100644 --- a/hugr-py/src/hugr/model/export.py +++ b/hugr-py/src/hugr/model/export.py @@ -29,7 +29,15 @@ Tag, TailLoop, ) -from hugr.tys import ConstKind, FunctionKind, Type, TypeBound, TypeParam, TypeTypeParam +from hugr.tys import ( + ConstKind, + FunctionKind, + Type, + TypeBound, + TypeParam, + TypeTypeParam, + Visibility, +) class ModelExport: @@ -146,7 +154,7 @@ def export_node( case FuncDefn() as op: name = _mangle_name(node, op.f_name) symbol = self.export_symbol( - name, op.signature.params, op.signature.body + name, op.visibility, op.signature.params, op.signature.body ) region = self.export_region_dfg(node) @@ -157,17 +165,25 @@ def export_node( case FuncDecl() as op: name = _mangle_name(node, op.f_name) symbol = self.export_symbol( - name, op.signature.params, op.signature.body + name, op.visibility, op.signature.params, op.signature.body ) return model.Node(operation=model.DeclareFunc(symbol), meta=meta) case AliasDecl() as op: - symbol = model.Symbol(name=op.alias, signature=model.Apply("core.type")) + symbol = model.Symbol( + name=op.alias, + visibility="Public", + signature=model.Apply("core.type"), + ) return model.Node(operation=model.DeclareAlias(symbol), meta=meta) case AliasDefn() as op: - symbol = model.Symbol(name=op.alias, signature=model.Apply("core.type")) + symbol = model.Symbol( + name=op.alias, + visibility="Public", + signature=model.Apply("core.type"), + ) alias_value = cast(model.Term, op.definition.to_model()) @@ -509,7 +525,11 @@ def export_region_cfg(self, node: Node) -> model.Region: ) def export_symbol( - self, name: str, param_types: Sequence[TypeParam], body: Type + self, + name: str, + visibility: Visibility, + param_types: Sequence[TypeParam], + body: Type, ) -> model.Symbol: """Export a symbol.""" constraints = [] @@ -530,6 +550,7 @@ def export_symbol( return model.Symbol( name=name, + visibility=visibility, params=params, constraints=constraints, signature=cast(model.Term, body.to_model()), diff --git a/hugr-py/src/hugr/ops.py b/hugr-py/src/hugr/ops.py index 1d11a3cfe..25d79fbf2 100644 --- a/hugr-py/src/hugr/ops.py +++ b/hugr-py/src/hugr/ops.py @@ -26,6 +26,7 @@ from hugr import ext from hugr._serialization.ops import BaseOp + from hugr.tys import Visibility @dataclass @@ -1150,6 +1151,8 @@ class FuncDefn(DfParentOp): params: list[tys.TypeParam] = field(default_factory=list) _outputs: tys.TypeRow | None = field(default=None, repr=False) num_out: int = field(default=1, repr=False) + #: Visibility (for linking). + visibility: Visibility = "Private" @property def outputs(self) -> tys.TypeRow: @@ -1176,6 +1179,7 @@ def _to_serial(self, parent: Node) -> sops.FuncDefn: parent=parent.idx, name=self.f_name, signature=self.signature._to_serial(), + visibility=self.visibility, ) def inner_signature(self) -> tys.FunctionType: @@ -1207,12 +1211,15 @@ class FuncDecl(Op): #: polymorphic function signature signature: tys.PolyFuncType num_out: int = field(default=1, repr=False) + #: Visibility (for linking). + visibility: Visibility = "Public" def _to_serial(self, parent: Node) -> sops.FuncDecl: return sops.FuncDecl( parent=parent.idx, name=self.f_name, signature=self.signature._to_serial(), + visibility=self.visibility, ) def port_kind(self, port: InPort | OutPort) -> tys.Kind: diff --git a/hugr-py/src/hugr/tys.py b/hugr-py/src/hugr/tys.py index 3010a2285..03cb62cb8 100644 --- a/hugr-py/src/hugr/tys.py +++ b/hugr-py/src/hugr/tys.py @@ -4,7 +4,7 @@ import base64 from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Protocol, cast, runtime_checkable +from typing import TYPE_CHECKING, Literal, Protocol, cast, runtime_checkable import hugr._serialization.tys as stys import hugr.model as model @@ -19,6 +19,7 @@ ExtensionId = stys.ExtensionId ExtensionSet = stys.ExtensionSet TypeBound = stys.TypeBound +Visibility = Literal["Public", "Private"] @runtime_checkable diff --git a/hugr-py/tests/__snapshots__/test_hugr_build.ambr b/hugr-py/tests/__snapshots__/test_hugr_build.ambr index d1b34184d..616e2be1b 100644 --- a/hugr-py/tests/__snapshots__/test_hugr_build.ambr +++ b/hugr-py/tests/__snapshots__/test_hugr_build.ambr @@ -940,7 +940,7 @@ + COLOR="black">Const(Function(body=Hugr(module_root=Node(0), entrypoint=Node(4), _nodes=[NodeData(op=Module(), parent=None, metadata={}), NodeData(op=FuncDefn(f_name='main', inputs=[Qubit], params=[], visibility='Private'), parent=Node(0), metadata={}), NodeData(op=Input(types=[Qubit]), parent=Node(1), metadata={}), NodeData(op=Output(), parent=Node(1), metadata={}), NodeData(op=DFG(inputs=[Qubit]), parent=Node(1), metadata={}), NodeData(op=Input(types=[Qubit]), parent=Node(4), metadata={}), NodeData(op=Output(), parent=Node(4), metadata={}), NodeData(op=Noop(Qubit), parent=Node(4), metadata={})], _links=BiMap({_SubPort(port=OutPort(Node(2), 0), sub_offset=0): _SubPort(port=InPort(Node(4), 0), sub_offset=0), _SubPort(port=OutPort(Node(5), 0), sub_offset=0): _SubPort(port=InPort(Node(7), 0), sub_offset=0), _SubPort(port=OutPort(Node(7), 0), sub_offset=0): _SubPort(port=InPort(Node(6), 0), sub_offset=0), _SubPort(port=OutPort(Node(4), 0), sub_offset=0): _SubPort(port=InPort(Node(3), 0), sub_offset=0)}), _free_nodes=[])))
Const(Function(body=Hugr(module_root=Node(0), entrypoint=Node(4), _nodes=[NodeData(op=Module(), parent=None, metadata={}), NodeData(op=FuncDefn(f_name='main', inputs=[Qubit], params=[]), parent=Node(0), metadata={}), NodeData(op=Input(types=[Qubit]), parent=Node(1), metadata={}), NodeData(op=Output(), parent=Node(1), metadata={}), NodeData(op=DFG(inputs=[Qubit]), parent=Node(1), metadata={}), NodeData(op=Input(types=[Qubit]), parent=Node(4), metadata={}), NodeData(op=Output(), parent=Node(4), metadata={}), NodeData(op=Noop(Qubit), parent=Node(4), metadata={})], _links=BiMap({_SubPort(port=OutPort(Node(2), 0), sub_offset=0): _SubPort(port=InPort(Node(4), 0), sub_offset=0), _SubPort(port=OutPort(Node(5), 0), sub_offset=0): _SubPort(port=InPort(Node(7), 0), sub_offset=0), _SubPort(port=OutPort(Node(7), 0), sub_offset=0): _SubPort(port=InPort(Node(6), 0), sub_offset=0), _SubPort(port=OutPort(Node(4), 0), sub_offset=0): _SubPort(port=InPort(Node(3), 0), sub_offset=0)}), _free_nodes=[])))
diff --git a/hugr-py/tests/test_envelope.py b/hugr-py/tests/test_envelope.py index 1e667e8ee..16ce1a26e 100644 --- a/hugr-py/tests/test_envelope.py +++ b/hugr-py/tests/test_envelope.py @@ -1,6 +1,11 @@ -from hugr import tys +from pathlib import Path + +import pytest + +from hugr import ops, tys from hugr.build.function import Module from hugr.envelope import EnvelopeConfig, EnvelopeFormat +from hugr.hugr.node_port import Node from hugr.package import Package @@ -30,3 +35,20 @@ def test_envelope(): encoded = package.to_str(EnvelopeConfig.TEXT) decoded = Package.from_str(encoded) assert decoded == package + + +def test_legacy_funcdefn(): + p = Path(__file__).parents[2] / "resources" / "test" / "hugr-no-visibility.hugr" + try: + with p.open("rb") as f: + pkg_bytes = f.read() + except FileNotFoundError: + pytest.skip("Missing test file") + decoded = Package.from_bytes(pkg_bytes) + h = decoded.modules[0] + op1 = h[Node(1)].op + assert isinstance(op1, ops.FuncDecl) + assert op1.visibility == "Public" + op2 = h[Node(2)].op + assert isinstance(op2, ops.FuncDefn) + assert op2.visibility == "Private" diff --git a/resources/test/hugr-no-visibility.hugr b/resources/test/hugr-no-visibility.hugr new file mode 100644 index 000000000..e61f93396 --- /dev/null +++ b/resources/test/hugr-no-visibility.hugr @@ -0,0 +1,52 @@ +HUGRiHJv?@{ + "modules": [ + { + "version": "live", + "nodes": [ + { + "parent": 0, + "op": "Module" + }, + { + "name":"polyfunc1", + "op":"FuncDecl", + "parent":0, + "signature":{ + "body":{ + "input":[], + "output":[] + }, + "params":[ + ] + } + }, + { + "name":"polyfunc2", + "op":"FuncDefn", + "parent":0, + "signature":{ + "body":{ + "input":[], + "output":[] + }, + "params":[ + ] + } + }, + { + "op": "Input", + "parent": 2, + "types": [] + }, + { + "op": "Output", + "parent": 2, + "types": [] + } + ], + "edges": [], + "encoder": null + } + ], + "extensions": [] +} diff --git a/specification/schema/hugr_schema_live.json b/specification/schema/hugr_schema_live.json index b4470740e..a9cbd06d0 100644 --- a/specification/schema/hugr_schema_live.json +++ b/specification/schema/hugr_schema_live.json @@ -676,6 +676,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Public", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [ @@ -706,6 +715,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Private", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [ diff --git a/specification/schema/hugr_schema_strict_live.json b/specification/schema/hugr_schema_strict_live.json index 1c6d27b8f..90bbbe392 100644 --- a/specification/schema/hugr_schema_strict_live.json +++ b/specification/schema/hugr_schema_strict_live.json @@ -676,6 +676,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Public", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [ @@ -706,6 +715,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Private", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [ diff --git a/specification/schema/testing_hugr_schema_live.json b/specification/schema/testing_hugr_schema_live.json index 5761d51a9..cace6b28a 100644 --- a/specification/schema/testing_hugr_schema_live.json +++ b/specification/schema/testing_hugr_schema_live.json @@ -676,6 +676,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Public", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [ @@ -706,6 +715,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Private", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [ diff --git a/specification/schema/testing_hugr_schema_strict_live.json b/specification/schema/testing_hugr_schema_strict_live.json index 1f086d118..5fe940e7e 100644 --- a/specification/schema/testing_hugr_schema_strict_live.json +++ b/specification/schema/testing_hugr_schema_strict_live.json @@ -676,6 +676,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Public", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [ @@ -706,6 +715,15 @@ }, "signature": { "$ref": "#/$defs/PolyFuncType" + }, + "visibility": { + "default": "Private", + "enum": [ + "Public", + "Private" + ], + "title": "Visibility", + "type": "string" } }, "required": [