diff --git a/hugr-core/Cargo.toml b/hugr-core/Cargo.toml index 8de77a49c..ce5218e27 100644 --- a/hugr-core/Cargo.toml +++ b/hugr-core/Cargo.toml @@ -45,6 +45,7 @@ delegate = { workspace = true } paste = { workspace = true } strum = { workspace = true } strum_macros = { workspace = true } +semver = { version = "1.0.23", features = ["serde"] } [dev-dependencies] rstest = { workspace = true } diff --git a/hugr-core/src/extension.rs b/hugr-core/src/extension.rs index 65e74aae9..9d8dd0570 100644 --- a/hugr-core/src/extension.rs +++ b/hugr-core/src/extension.rs @@ -3,6 +3,7 @@ //! TODO: YAML declaration and parsing. This should be similar to a plugin //! system (outside the `types` module), which also parses nested [`OpDef`]s. +pub use semver::Version; use std::collections::btree_map; use std::collections::hash_map; use std::collections::{BTreeMap, BTreeSet, HashMap}; @@ -55,21 +56,17 @@ impl ExtensionRegistry { pub fn try_new( value: impl IntoIterator, ) -> Result { - let mut exts = BTreeMap::new(); + let mut res = ExtensionRegistry(BTreeMap::new()); + for ext in value.into_iter() { - let prev = exts.insert(ext.name.clone(), ext); - if let Some(prev) = prev { - return Err(ExtensionRegistryError::AlreadyRegistered( - prev.name().clone(), - )); - }; + res.register(ext)?; } + // Note this potentially asks extensions to validate themselves against other extensions that // may *not* be valid themselves yet. It'd be better to order these respecting dependencies, // or at least to validate the types first - which we don't do at all yet: // TODO https://github.com/CQCL/hugr/issues/624. However, parametrized types could be // cyclically dependent, so there is no perfect solution, and this is at least simple. - let res = ExtensionRegistry(exts); for ext in res.0.values() { ext.validate(&res) .map_err(|e| ExtensionRegistryError::InvalidSignature(ext.name().clone(), e))?; @@ -82,13 +79,35 @@ impl ExtensionRegistry { /// Returns a reference to the registered extension if successful. pub fn register(&mut self, extension: Extension) -> Result<&Extension, ExtensionRegistryError> { match self.0.entry(extension.name().clone()) { - btree_map::Entry::Occupied(_) => Err(ExtensionRegistryError::AlreadyRegistered( + btree_map::Entry::Occupied(prev) => Err(ExtensionRegistryError::AlreadyRegistered( extension.name().clone(), + prev.get().version().clone(), + extension.version().clone(), )), btree_map::Entry::Vacant(ve) => Ok(ve.insert(extension)), } } + /// Registers a new extension to the registry, keeping most up to date if extension exists. + /// + /// If extension IDs match, the extension with the higher version is kept. + /// If versions match, the original extension is kept. + /// Returns a reference to the registered extension if successful. + pub fn register_updated( + &mut self, + extension: Extension, + ) -> Result<&Extension, ExtensionRegistryError> { + match self.0.entry(extension.name().clone()) { + btree_map::Entry::Occupied(mut prev) => { + if prev.get().version() < extension.version() { + *prev.get_mut() = extension; + } + Ok(prev.into_mut()) + } + btree_map::Entry::Vacant(ve) => Ok(ve.insert(extension)), + } + } + /// Returns the number of extensions in the registry. pub fn len(&self) -> usize { self.0.len() @@ -103,6 +122,11 @@ impl ExtensionRegistry { pub fn iter(&self) -> impl Iterator { self.0.iter() } + + /// Delete an extension from the registry and return it if it was present. + pub fn remove_extension(&mut self, name: &ExtensionId) -> Option { + self.0.remove(name) + } } impl IntoIterator for ExtensionRegistry { @@ -264,6 +288,8 @@ pub type ExtensionId = IdentList; /// A extension is a set of capabilities required to execute a graph. #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct Extension { + /// Extension version, follows semver. + pub version: Version, /// Unique identifier for the extension. pub name: ExtensionId, /// Other extensions defining types used by this extension. @@ -286,21 +312,25 @@ pub struct Extension { impl Extension { /// Creates a new extension with the given name. - pub fn new(name: ExtensionId) -> Self { - Self::new_with_reqs(name, ExtensionSet::default()) - } - - /// Creates a new extension with the given name and requirements. - pub fn new_with_reqs(name: ExtensionId, extension_reqs: impl Into) -> Self { + pub fn new(name: ExtensionId, version: Version) -> Self { Self { name, - extension_reqs: extension_reqs.into(), + version, + extension_reqs: Default::default(), types: Default::default(), values: Default::default(), operations: Default::default(), } } + /// Extend the requirements of this extension with another set of extensions. + pub fn with_reqs(self, extension_reqs: impl Into) -> Self { + Self { + extension_reqs: self.extension_reqs.union(extension_reqs.into()), + ..self + } + } + /// Allows read-only access to the operations in this Extension pub fn get_op(&self, name: &OpNameRef) -> Option<&Arc> { self.operations.get(name) @@ -321,6 +351,11 @@ impl Extension { &self.name } + /// Returns the version of the extension. + pub fn version(&self) -> &Version { + &self.version + } + /// Iterator over the operations of this [`Extension`]. pub fn operations(&self) -> impl Iterator)> { self.operations.iter() @@ -382,8 +417,8 @@ impl PartialEq for Extension { #[derive(Debug, Clone, Error, PartialEq, Eq)] pub enum ExtensionRegistryError { /// Extension already defined. - #[error("The registry already contains an extension with id {0}.")] - AlreadyRegistered(ExtensionId), + #[error("The registry already contains an extension with id {0} and version {1}. New extension has version {2}.")] + AlreadyRegistered(ExtensionId, Version, Version), /// A registered extension has invalid signatures. #[error("The extension {0} contains an invalid signature, {1}.")] InvalidSignature(ExtensionId, #[source] SignatureError), @@ -544,6 +579,53 @@ pub mod test { // We re-export this here because mod op_def is private. pub use super::op_def::test::SimpleOpDef; + use super::*; + + impl Extension { + /// Create a new extension for testing, with a 0 version. + pub(crate) fn new_test(name: ExtensionId) -> Self { + Self::new(name, Version::new(0, 0, 0)) + } + } + + #[test] + fn test_register_update() { + let mut reg = ExtensionRegistry::try_new([]).unwrap(); + let ext_1_id = ExtensionId::new("ext1").unwrap(); + let ext_2_id = ExtensionId::new("ext2").unwrap(); + let ext1 = Extension::new(ext_1_id.clone(), Version::new(1, 0, 0)); + let ext1_1 = Extension::new(ext_1_id.clone(), Version::new(1, 1, 0)); + let ext1_2 = Extension::new(ext_1_id.clone(), Version::new(0, 2, 0)); + let ext2 = Extension::new(ext_2_id, Version::new(1, 0, 0)); + + reg.register(ext1.clone()).unwrap(); + assert_eq!(reg.get("ext1").unwrap().version(), &Version::new(1, 0, 0)); + + // normal registration fails + assert_eq!( + reg.register(ext1_1.clone()), + Err(ExtensionRegistryError::AlreadyRegistered( + ext_1_id.clone(), + Version::new(1, 0, 0), + Version::new(1, 1, 0) + )) + ); + + // register with update works + reg.register_updated(ext1_1.clone()).unwrap(); + assert_eq!(reg.get("ext1").unwrap().version(), &Version::new(1, 1, 0)); + + // register with lower version does not change version + reg.register_updated(ext1_2.clone()).unwrap(); + assert_eq!(reg.get("ext1").unwrap().version(), &Version::new(1, 1, 0)); + + reg.register(ext2.clone()).unwrap(); + assert_eq!(reg.get("ext2").unwrap().version(), &Version::new(1, 0, 0)); + assert_eq!(reg.len(), 2); + + assert!(reg.remove_extension(&ext_1_id).unwrap().version() == &Version::new(1, 1, 0)); + assert_eq!(reg.len(), 1); + } mod proptest { use ::proptest::{collection::hash_set, prelude::*}; diff --git a/hugr-core/src/extension/declarative.rs b/hugr-core/src/extension/declarative.rs index 5db8999cb..cb98e8215 100644 --- a/hugr-core/src/extension/declarative.rs +++ b/hugr-core/src/extension/declarative.rs @@ -89,6 +89,7 @@ struct ExtensionSetDeclaration { /// A declarative extension definition. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] struct ExtensionDeclaration { + // TODO add version /// The name of the extension. name: ExtensionId, /// A list of types that this extension provides. @@ -150,7 +151,8 @@ impl ExtensionDeclaration { imports: &ExtensionSet, ctx: DeclarationContext<'_>, ) -> Result { - let mut ext = Extension::new_with_reqs(self.name.clone(), imports.clone()); + let mut ext = Extension::new(self.name.clone(), crate::extension::Version::new(0, 0, 0)) + .with_reqs(imports.clone()); for t in &self.types { t.register(&mut ext, ctx)?; diff --git a/hugr-core/src/extension/op_def.rs b/hugr-core/src/extension/op_def.rs index 06114a582..2b628d820 100644 --- a/hugr-core/src/extension/op_def.rs +++ b/hugr-core/src/extension/op_def.rs @@ -591,7 +591,7 @@ pub(super) mod test { #[test] fn op_def_with_type_scheme() -> Result<(), Box> { let list_def = EXTENSION.get_type(&LIST_TYPENAME).unwrap(); - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); const TP: TypeParam = TypeParam::Type { b: TypeBound::Any }; let list_of_var = Type::new_extension(list_def.instantiate(vec![TypeArg::new_var_use(0, TP)])?); @@ -658,7 +658,7 @@ pub(super) mod test { MAX_NAT } } - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); let def: &mut crate::extension::OpDef = e.add_op("MyOp".into(), "".to_string(), SigFun())?; @@ -720,7 +720,7 @@ pub(super) mod test { fn type_scheme_instantiate_var() -> Result<(), Box> { // Check that we can instantiate a PolyFuncTypeRV-scheme with an (external) // type variable - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); let def = e.add_op( "SimpleOp".into(), "".into(), @@ -755,7 +755,7 @@ pub(super) mod test { fn instantiate_extension_delta() -> Result<(), Box> { use crate::extension::prelude::{BOOL_T, PRELUDE_REGISTRY}; - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); let params: Vec = vec![TypeParam::Extensions]; let db_set = ExtensionSet::type_var(0); diff --git a/hugr-core/src/extension/prelude.rs b/hugr-core/src/extension/prelude.rs index c220618fa..739caad16 100644 --- a/hugr-core/src/extension/prelude.rs +++ b/hugr-core/src/extension/prelude.rs @@ -85,9 +85,11 @@ impl SignatureFromArgs for GenericOpCustom { /// Name of prelude extension. pub const PRELUDE_ID: ExtensionId = ExtensionId::new_unchecked("prelude"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); lazy_static! { static ref PRELUDE_DEF: Extension = { - let mut prelude = Extension::new(PRELUDE_ID); + let mut prelude = Extension::new(PRELUDE_ID, VERSION); prelude .add_type( TypeName::new_inline("usize"), diff --git a/hugr-core/src/extension/simple_op.rs b/hugr-core/src/extension/simple_op.rs index 877282a52..d8dc4ec18 100644 --- a/hugr-core/src/extension/simple_op.rs +++ b/hugr-core/src/extension/simple_op.rs @@ -319,7 +319,7 @@ mod test { lazy_static! { static ref EXT: Extension = { - let mut e = Extension::new(EXT_ID.clone()); + let mut e = Extension::new_test(EXT_ID.clone()); DummyEnum::Dumb.add_to_extension(&mut e).unwrap(); e }; diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 4fc752419..76ccec8b4 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -365,7 +365,7 @@ const_extension_ids! { } #[test] fn invalid_types() { - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); e.add_type( "MyContainer".into(), vec![TypeBound::Copyable.into()], @@ -570,7 +570,7 @@ fn no_polymorphic_consts() -> Result<(), Box> { pub(crate) fn extension_with_eval_parallel() -> Extension { let rowp = TypeParam::new_list(TypeBound::Any); - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); let inputs = TypeRV::new_row_var_use(0, TypeBound::Any); let outputs = TypeRV::new_row_var_use(1, TypeBound::Any); @@ -671,7 +671,7 @@ fn row_variables() -> Result<(), Box> { #[test] fn test_polymorphic_call() -> Result<(), Box> { - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); let params: Vec = vec![ TypeBound::Any.into(), diff --git a/hugr-core/src/std_extensions/arithmetic/conversions.rs b/hugr-core/src/std_extensions/arithmetic/conversions.rs index 8f7110807..06a06abed 100644 --- a/hugr-core/src/std_extensions/arithmetic/conversions.rs +++ b/hugr-core/src/std_extensions/arithmetic/conversions.rs @@ -22,6 +22,8 @@ use lazy_static::lazy_static; mod const_fold; /// The extension identifier. pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("arithmetic.conversions"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); /// Extension for conversions between floats and integers. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, EnumIter, IntoStaticStr, EnumString)] @@ -121,8 +123,9 @@ impl MakeExtensionOp for ConvertOpType { lazy_static! { /// Extension for conversions between integers and floats. pub static ref EXTENSION: Extension = { - let mut extension = Extension::new_with_reqs( + let mut extension = Extension::new( EXTENSION_ID, + VERSION).with_reqs( ExtensionSet::from_iter(vec![ super::int_types::EXTENSION_ID, super::float_types::EXTENSION_ID, diff --git a/hugr-core/src/std_extensions/arithmetic/float_ops.rs b/hugr-core/src/std_extensions/arithmetic/float_ops.rs index f701da233..9fe86ee1f 100644 --- a/hugr-core/src/std_extensions/arithmetic/float_ops.rs +++ b/hugr-core/src/std_extensions/arithmetic/float_ops.rs @@ -17,6 +17,8 @@ use lazy_static::lazy_static; mod const_fold; /// The extension identifier. pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("arithmetic.float"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); /// Integer extension operation definitions. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, EnumIter, IntoStaticStr, EnumString)] @@ -99,8 +101,9 @@ impl MakeOpDef for FloatOps { lazy_static! { /// Extension for basic float operations. pub static ref EXTENSION: Extension = { - let mut extension = Extension::new_with_reqs( + let mut extension = Extension::new( EXTENSION_ID, + VERSION).with_reqs( ExtensionSet::singleton(&super::int_types::EXTENSION_ID), ); diff --git a/hugr-core/src/std_extensions/arithmetic/float_types.rs b/hugr-core/src/std_extensions/arithmetic/float_types.rs index 41ffbe45c..335009e92 100644 --- a/hugr-core/src/std_extensions/arithmetic/float_types.rs +++ b/hugr-core/src/std_extensions/arithmetic/float_types.rs @@ -12,6 +12,8 @@ use lazy_static::lazy_static; /// The extension identifier. pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("arithmetic.float.types"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); /// Identifier for the 64-bit IEEE 754-2019 floating-point type. const FLOAT_TYPE_ID: TypeName = TypeName::new_inline("float64"); @@ -76,7 +78,7 @@ impl CustomConst for ConstF64 { lazy_static! { /// Extension defining the float type. pub static ref EXTENSION: Extension = { - let mut extension = Extension::new(EXTENSION_ID); + let mut extension = Extension::new(EXTENSION_ID, VERSION); extension .add_type( diff --git a/hugr-core/src/std_extensions/arithmetic/int_ops.rs b/hugr-core/src/std_extensions/arithmetic/int_ops.rs index ca3094f07..e9bef39b6 100644 --- a/hugr-core/src/std_extensions/arithmetic/int_ops.rs +++ b/hugr-core/src/std_extensions/arithmetic/int_ops.rs @@ -28,6 +28,8 @@ mod const_fold; /// The extension identifier. pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("arithmetic.int"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); struct IOValidator { // whether the first type argument should be greater than or equal to the second @@ -261,9 +263,10 @@ fn iunop_sig() -> PolyFuncTypeRV { lazy_static! { /// Extension for basic integer operations. pub static ref EXTENSION: Extension = { - let mut extension = Extension::new_with_reqs( + let mut extension = Extension::new( EXTENSION_ID, - ExtensionSet::singleton(&super::int_types::EXTENSION_ID), + VERSION).with_reqs( + ExtensionSet::singleton(&super::int_types::EXTENSION_ID) ); IntOpDef::load_all_ops(&mut extension).unwrap(); diff --git a/hugr-core/src/std_extensions/arithmetic/int_types.rs b/hugr-core/src/std_extensions/arithmetic/int_types.rs index 49a4cb868..6ec3b7724 100644 --- a/hugr-core/src/std_extensions/arithmetic/int_types.rs +++ b/hugr-core/src/std_extensions/arithmetic/int_types.rs @@ -16,6 +16,8 @@ use crate::{ use lazy_static::lazy_static; /// The extension identifier. pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("arithmetic.int.types"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); /// Identifier for the integer type. pub const INT_TYPE_ID: TypeName = TypeName::new_inline("int"); @@ -185,7 +187,7 @@ impl CustomConst for ConstInt { /// Extension for basic integer types. pub fn extension() -> Extension { - let mut extension = Extension::new(EXTENSION_ID); + let mut extension = Extension::new(EXTENSION_ID, VERSION); extension .add_type( diff --git a/hugr-core/src/std_extensions/collections.rs b/hugr-core/src/std_extensions/collections.rs index 0fb809d16..c182543ca 100644 --- a/hugr-core/src/std_extensions/collections.rs +++ b/hugr-core/src/std_extensions/collections.rs @@ -31,6 +31,8 @@ pub const POP_NAME: OpName = OpName::new_inline("pop"); pub const PUSH_NAME: OpName = OpName::new_inline("push"); /// Reported unique name of the extension pub const EXTENSION_NAME: ExtensionId = ExtensionId::new_unchecked("Collections"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] /// Dynamically sized list of values, all of the same type. @@ -138,7 +140,7 @@ impl ConstFold for PushFold { const TP: TypeParam = TypeParam::Type { b: TypeBound::Any }; fn extension() -> Extension { - let mut extension = Extension::new(EXTENSION_NAME); + let mut extension = Extension::new(EXTENSION_NAME, VERSION); extension .add_type( diff --git a/hugr-core/src/std_extensions/logic.rs b/hugr-core/src/std_extensions/logic.rs index 8ad183ef5..de58a5e31 100644 --- a/hugr-core/src/std_extensions/logic.rs +++ b/hugr-core/src/std_extensions/logic.rs @@ -168,6 +168,8 @@ impl MakeOpDef for NotOp { } /// The extension identifier. pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("logic"); +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); fn logic_op_sig() -> impl SignatureFromArgs { struct LogicOpCustom; @@ -194,7 +196,7 @@ fn logic_op_sig() -> impl SignatureFromArgs { } /// Extension for basic logical operations. fn extension() -> Extension { - let mut extension = Extension::new(EXTENSION_ID); + let mut extension = Extension::new(EXTENSION_ID, VERSION); NaryLogic::load_all_ops(&mut extension).unwrap(); NotOp.add_to_extension(&mut extension).unwrap(); diff --git a/hugr-core/src/std_extensions/ptr.rs b/hugr-core/src/std_extensions/ptr.rs index 774cd6376..72c53a97f 100644 --- a/hugr-core/src/std_extensions/ptr.rs +++ b/hugr-core/src/std_extensions/ptr.rs @@ -80,9 +80,12 @@ pub const PTR_TYPE_ID: TypeName = TypeName::new_inline("ptr"); const TYPE_PARAMS: [TypeParam; 1] = [TypeParam::Type { b: TypeBound::Copyable, }]; +/// Extension version. +pub const VERSION: semver::Version = semver::Version::new(0, 1, 0); + /// Extension for pointer operations. fn extension() -> Extension { - let mut extension = Extension::new(EXTENSION_ID); + let mut extension = Extension::new(EXTENSION_ID, VERSION); extension .add_type( PTR_TYPE_ID, diff --git a/hugr-core/src/types/poly_func.rs b/hugr-core/src/types/poly_func.rs index 4f6df034b..40a92b2ea 100644 --- a/hugr-core/src/types/poly_func.rs +++ b/hugr-core/src/types/poly_func.rs @@ -313,7 +313,7 @@ pub(crate) mod test { const EXT_ID: ExtensionId = ExtensionId::new_unchecked("my_ext"); const TYPE_NAME: TypeName = TypeName::new_inline("MyType"); - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new_test(EXT_ID); e.add_type( TYPE_NAME, vec![bound.clone()], diff --git a/hugr-core/src/utils.rs b/hugr-core/src/utils.rs index 4e2d15e99..885aebf50 100644 --- a/hugr-core/src/utils.rs +++ b/hugr-core/src/utils.rs @@ -129,7 +129,7 @@ pub(crate) mod test_quantum_extension { /// The extension identifier. pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("test.quantum"); fn extension() -> Extension { - let mut extension = Extension::new(EXTENSION_ID); + let mut extension = Extension::new_test(EXTENSION_ID); extension .add_op(OpName::new_inline("H"), "Hadamard".into(), one_qb_func()) diff --git a/hugr-passes/src/merge_bbs.rs b/hugr-passes/src/merge_bbs.rs index fae6b061f..65ae5ec99 100644 --- a/hugr-passes/src/merge_bbs.rs +++ b/hugr-passes/src/merge_bbs.rs @@ -178,7 +178,7 @@ mod test { } fn extension() -> Extension { - let mut e = Extension::new(EXT_ID); + let mut e = Extension::new(EXT_ID, hugr_core::extension::Version::new(0, 1, 0)); e.add_op( "Test".into(), String::new(), diff --git a/hugr/src/lib.rs b/hugr/src/lib.rs index f142cffc8..ef9c01a0b 100644 --- a/hugr/src/lib.rs +++ b/hugr/src/lib.rs @@ -39,7 +39,7 @@ //! use hugr::{ //! extension::{ //! prelude::{BOOL_T, QB_T}, -//! ExtensionId, ExtensionRegistry, PRELUDE, +//! ExtensionId, ExtensionRegistry, PRELUDE, Version, //! }, //! ops::{CustomOp, OpName}, //! type_row, @@ -58,8 +58,9 @@ //! } //! /// The extension identifier. //! pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("mini.quantum"); +//! pub const VERSION: Version = Version::new(0, 1, 0); //! fn extension() -> Extension { -//! let mut extension = Extension::new(EXTENSION_ID); +//! let mut extension = Extension::new(EXTENSION_ID, VERSION); //! //! extension //! .add_op(OpName::new_inline("H"), "Hadamard".into(), one_qb_func())