From c4a56318da36dd5aa30dfd107f3e6a879f1545a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Borgna?= <121866228+aborgna-q@users.noreply.github.com> Date: Fri, 12 Apr 2024 16:28:32 +0200 Subject: [PATCH] refactor!: Merge `CustomOp` and `ExternalOp`. (#923) Followup of #922 . Drops `ExternalOp` and makes `CustomOp` an enum with two boxed variants for `OpaqueOp` and `ExtensionOp`. The schema remains the same. --------- Co-authored-by: Seyon Sivarajah --- hugr/src/algorithm/const_fold.rs | 2 +- hugr/src/builder/circuit.rs | 4 +- hugr/src/extension/infer/test.rs | 13 ++- hugr/src/extension/op_def.rs | 5 +- hugr/src/extension/simple_op.rs | 2 +- hugr/src/hugr.rs | 2 - hugr/src/hugr/rewrite/replace.rs | 8 +- hugr/src/hugr/serialize.rs | 13 +-- hugr/src/hugr/validate.rs | 27 +++-- hugr/src/ops.rs | 3 +- hugr/src/ops/custom.rs | 178 ++++++++++++++++++------------- hugr/src/ops/leaf.rs | 57 +--------- 12 files changed, 139 insertions(+), 175 deletions(-) diff --git a/hugr/src/algorithm/const_fold.rs b/hugr/src/algorithm/const_fold.rs index bbe82a48b..67b7ffaf6 100644 --- a/hugr/src/algorithm/const_fold.rs +++ b/hugr/src/algorithm/const_fold.rs @@ -65,7 +65,7 @@ pub fn fold_leaf_op(op: &OpType, consts: &[(IncomingPort, Const)]) -> ConstFoldR ) .unwrap()]), OpType::CustomOp(op) => { - let ext_op = op.as_ref().as_extension_op()?; + let ext_op = op.as_extension_op()?; ext_op.constant_fold(consts) } _ => None, diff --git a/hugr/src/builder/circuit.rs b/hugr/src/builder/circuit.rs index 40018c096..eaaf75878 100644 --- a/hugr/src/builder/circuit.rs +++ b/hugr/src/builder/circuit.rs @@ -278,13 +278,13 @@ mod test { #[test] fn with_nonlinear_and_outputs() { - let my_custom_op = CustomOp::new(crate::ops::custom::ExternalOp::Opaque(OpaqueOp::new( + let my_custom_op = CustomOp::new_opaque(OpaqueOp::new( "MissingRsrc".try_into().unwrap(), "MyOp", "unknown op".to_string(), vec![], FunctionType::new(vec![QB, NAT], vec![QB]), - ))); + )); let build_res = build_main( FunctionType::new(type_row![QB, QB, NAT], type_row![QB, QB, BOOL_T]).into(), |mut f_build| { diff --git a/hugr/src/extension/infer/test.rs b/hugr/src/extension/infer/test.rs index cb03d6d94..5fc31eab3 100644 --- a/hugr/src/extension/infer/test.rs +++ b/hugr/src/extension/infer/test.rs @@ -9,7 +9,7 @@ use crate::extension::ExtensionId; use crate::extension::{prelude::PRELUDE_REGISTRY, ExtensionSet}; use crate::hugr::{Hugr, HugrMut, HugrView, NodeType}; use crate::macros::const_extension_ids; -use crate::ops::custom::{ExternalOp, OpaqueOp}; +use crate::ops::custom::OpaqueOp; use crate::ops::{self, dataflow::IOTrait}; use crate::ops::{CustomOp, Lift, OpType}; #[cfg(feature = "extension_inference")] @@ -439,8 +439,7 @@ fn extension_adding_sequence() -> Result<(), Box> { } fn make_opaque(extension: impl Into, signature: FunctionType) -> CustomOp { - let opaque = ops::custom::OpaqueOp::new(extension.into(), "", "".into(), vec![], signature); - ops::custom::ExternalOp::from(opaque).into() + ops::custom::OpaqueOp::new(extension.into(), "", "".into(), vec![], signature).into() } fn make_block( @@ -930,21 +929,21 @@ fn plus_on_self() -> Result<(), Box> { // While https://github.com/CQCL/hugr/issues/388 is unsolved, // most operations have empty extension_reqs (not including their own extension). // Define some that do. - let binop = CustomOp::new(ExternalOp::Opaque(OpaqueOp::new( + let binop = CustomOp::new_opaque(OpaqueOp::new( ext.clone(), "2qb_op", String::new(), vec![], ft, - ))); + )); let unary_sig = FunctionType::new_endo(type_row![QB_T]).with_extension_delta(ext.clone()); - let unop = CustomOp::new(ExternalOp::Opaque(OpaqueOp::new( + let unop = CustomOp::new_opaque(OpaqueOp::new( ext, "1qb_op", String::new(), vec![], unary_sig, - ))); + )); // Constrain q1,q2 as PLUS(ext1, inputs): let [q1, q2] = dfg .add_dataflow_op(binop.clone(), dfg.input_wires())? diff --git a/hugr/src/extension/op_def.rs b/hugr/src/extension/op_def.rs index 88b09d2fc..f38f4d0d4 100644 --- a/hugr/src/extension/op_def.rs +++ b/hugr/src/extension/op_def.rs @@ -476,7 +476,6 @@ mod test { use crate::extension::prelude::USIZE_T; use crate::extension::{ExtensionRegistry, ExtensionSet, PRELUDE}; use crate::extension::{SignatureError, EMPTY_REG, PRELUDE_REGISTRY}; - use crate::ops::custom::ExternalOp; use crate::ops::CustomOp; use crate::std_extensions::collections::{EXTENSION, LIST_TYPENAME}; use crate::types::Type; @@ -513,10 +512,10 @@ mod test { Type::new_extension(list_def.instantiate(vec![TypeArg::Type { ty: USIZE_T }])?); let mut dfg = DFGBuilder::new(FunctionType::new_endo(vec![list_usize]))?; let rev = dfg.add_dataflow_op( - CustomOp::new(ExternalOp::Extension( + CustomOp::new_extension( e.instantiate_extension_op(&OP_NAME, vec![TypeArg::Type { ty: USIZE_T }], ®) .unwrap(), - )), + ), dfg.input_wires(), )?; dfg.finish_hugr_with_outputs(rev.outputs(), ®)?; diff --git a/hugr/src/extension/simple_op.rs b/hugr/src/extension/simple_op.rs index e7fbda3a7..982303069 100644 --- a/hugr/src/extension/simple_op.rs +++ b/hugr/src/extension/simple_op.rs @@ -94,7 +94,7 @@ pub trait MakeExtensionOp: OpName { where Self: Sized, { - let ext: &ExtensionOp = op.as_custom_op()?.as_ref().as_extension_op()?; + let ext: &ExtensionOp = op.as_custom_op()?.as_extension_op()?; Self::from_extension_op(ext).ok() } diff --git a/hugr/src/hugr.rs b/hugr/src/hugr.rs index feb57ae16..3687b9094 100644 --- a/hugr/src/hugr.rs +++ b/hugr/src/hugr.rs @@ -203,8 +203,6 @@ impl Hugr { /// Infer extension requirements and add new information to `op_types` field /// (if the "extension_inference" feature is on; otherwise, do nothing) - /// - /// See [`infer_extensions`] for details on the "closure" value pub fn infer_extensions(&mut self) -> Result<(), InferExtensionError> { #[cfg(feature = "extension_inference")] { diff --git a/hugr/src/hugr/rewrite/replace.rs b/hugr/src/hugr/rewrite/replace.rs index acd47db5c..a5f891817 100644 --- a/hugr/src/hugr/rewrite/replace.rs +++ b/hugr/src/hugr/rewrite/replace.rs @@ -455,10 +455,10 @@ mod test { use crate::hugr::hugrmut::sealed::HugrMutInternals; use crate::hugr::rewrite::replace::WhichHugr; use crate::hugr::{HugrMut, NodeType, Rewrite}; - use crate::ops::custom::{ExternalOp, OpaqueOp}; + use crate::ops::custom::{CustomOp, OpaqueOp}; use crate::ops::dataflow::DataflowOpTrait; use crate::ops::handle::{BasicBlockID, ConstID, NodeHandle}; - use crate::ops::{self, Case, CustomOp, DataflowBlock, OpTag, OpType, DFG}; + use crate::ops::{self, Case, DataflowBlock, OpTag, OpType, DFG}; use crate::std_extensions::collections; use crate::types::{FunctionType, Type, TypeArg, TypeRow}; use crate::{type_row, Direction, Hugr, HugrView, OutgoingPort}; @@ -650,13 +650,13 @@ mod test { fn test_invalid() -> Result<(), Box> { let utou = FunctionType::new_endo(vec![USIZE_T]); let mk_op = |s| { - CustomOp::new(ExternalOp::Opaque(OpaqueOp::new( + CustomOp::new_opaque(OpaqueOp::new( ExtensionId::new("unknown_ext").unwrap(), s, String::new(), vec![], utou.clone(), - ))) + )) }; let mut h = DFGBuilder::new(FunctionType::new( type_row![USIZE_T, BOOL_T], diff --git a/hugr/src/hugr/serialize.rs b/hugr/src/hugr/serialize.rs index fdf8c8270..88334e654 100644 --- a/hugr/src/hugr/serialize.rs +++ b/hugr/src/hugr/serialize.rs @@ -347,18 +347,7 @@ pub mod test { for node in new_hugr.nodes() { let new_op = new_hugr.get_optype(node); let old_op = h_canon.get_optype(node); - if let OpType::CustomOp(new_op) = new_op { - if let OpType::CustomOp(old_op) = old_op { - assert_eq!( - new_op.as_ref().clone().into_opaque(), - old_op.as_ref().clone().into_opaque() - ); - } else { - panic!("Expected old_op to be a custom op"); - } - } else { - assert_eq!(new_op, old_op); - } + assert_eq!(new_op, old_op); } // Check that the graphs are equivalent up to port renumbering. diff --git a/hugr/src/hugr/validate.rs b/hugr/src/hugr/validate.rs index 55f0487b6..30a7dce09 100644 --- a/hugr/src/hugr/validate.rs +++ b/hugr/src/hugr/validate.rs @@ -16,7 +16,7 @@ use crate::extension::{ }; use crate::ops::custom::CustomOpError; -use crate::ops::custom::{resolve_opaque_op, ExternalOp}; +use crate::ops::custom::{resolve_opaque_op, CustomOp}; use crate::ops::validate::{ChildrenEdgeData, ChildrenValidationError, EdgeValidationError}; use crate::ops::{FuncDefn, OpTag, OpTrait, OpType, ValidateOp}; use crate::types::type_param::TypeParam; @@ -525,33 +525,32 @@ impl<'a, 'b> ValidationContext<'a, 'b> { // The op_type must be defined only in terms of type variables defined outside the node // TODO consider turning this match into a trait method? match op_type { - OpType::CustomOp(b) => { + OpType::CustomOp(op) => { // Try to resolve serialized names to actual OpDefs in Extensions. - let temp: ExternalOp; - let external = b.as_ref(); - let resolved = match external { - ExternalOp::Opaque(op) => { + let temp: CustomOp; + let resolved = match op { + CustomOp::Opaque(opaque) => { // If resolve_extension_ops has been called first, this would always return Ok(None) - match resolve_opaque_op(node, op, self.extension_registry)? { + match resolve_opaque_op(node, opaque, self.extension_registry)? { Some(exten) => { - temp = ExternalOp::Extension(exten); + temp = CustomOp::new_extension(exten); &temp } - None => external, + None => op, } } - ExternalOp::Extension(_) => external, + CustomOp::Extension(_) => op, }; // Check TypeArgs are valid, and if we can, fit the declared TypeParams match resolved { - ExternalOp::Extension(exten) => exten + CustomOp::Extension(exten) => exten .def() .validate_args(exten.args(), self.extension_registry, var_decls) .map_err(|cause| ValidationError::SignatureError { node, cause })?, - ExternalOp::Opaque(opaq) => { + CustomOp::Opaque(opaque) => { // Best effort. Just check TypeArgs are valid in themselves, allowing any of them // to contain type vars (we don't know how many are binary params, so accept if in doubt) - for arg in opaq.args() { + for arg in opaque.args() { arg.validate(self.extension_registry, var_decls) .map_err(|cause| ValidationError::SignatureError { node, cause })?; } @@ -699,7 +698,7 @@ pub enum ValidationError { /// Error in a [CustomOp] serialized as an [Opaque] /// /// [CustomOp]: crate::ops::CustomOp - /// [Opaque]: crate::ops::custom::ExternalOp::Opaque + /// [Opaque]: crate::ops::CustomOp::Opaque #[error(transparent)] CustomOpError(#[from] CustomOpError), } diff --git a/hugr/src/ops.rs b/hugr/src/ops.rs index ec20b94f7..07a59d116 100644 --- a/hugr/src/ops.rs +++ b/hugr/src/ops.rs @@ -22,8 +22,9 @@ use enum_dispatch::enum_dispatch; pub use constant::Const; pub use controlflow::{BasicBlock, Case, Conditional, DataflowBlock, ExitBlock, TailLoop, CFG}; +pub use custom::CustomOp; pub use dataflow::{Call, CallIndirect, DataflowParent, Input, LoadConstant, Output, DFG}; -pub use leaf::{CustomOp, Lift, MakeTuple, Noop, Tag, UnpackTuple}; +pub use leaf::{Lift, MakeTuple, Noop, Tag, UnpackTuple}; pub use module::{AliasDecl, AliasDefn, FuncDecl, FuncDefn, Module}; pub use tag::OpTag; diff --git a/hugr/src/ops/custom.rs b/hugr/src/ops/custom.rs index 287b90959..54c6c1789 100644 --- a/hugr/src/ops/custom.rs +++ b/hugr/src/ops/custom.rs @@ -7,28 +7,57 @@ use thiserror::Error; use crate::extension::{ConstFoldResult, ExtensionId, ExtensionRegistry, OpDef, SignatureError}; use crate::hugr::hugrmut::sealed::HugrMutInternals; use crate::hugr::{HugrView, NodeType}; +use crate::types::EdgeKind; use crate::types::{type_param::TypeArg, FunctionType}; use crate::{ops, Hugr, IncomingPort, Node}; use super::dataflow::DataflowOpTrait; use super::tag::OpTag; -use super::{CustomOp, OpTrait, OpType}; +use super::{OpName, OpTrait, OpType}; -/// An instantiation of an operation (declared by a extension) with values for the type arguments -#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +/// A user-defined operation defined in an extension. +/// +/// Any custom operation can be encoded as a serializable [`OpaqueOp`]. If the +/// operation's extension is loaded in the current context, the operation can be +/// resolved into an [`ExtensionOp`] containing a reference to its definition. +/// +/// [`OpaqueOp`]: crate::ops::custom::OpaqueOp +/// [`ExtensionOp`]: crate::ops::custom::ExtensionOp +#[derive(Clone, Debug, Eq, serde::Serialize, serde::Deserialize)] #[serde(into = "OpaqueOp", from = "OpaqueOp")] -pub enum ExternalOp { +pub enum CustomOp { /// When we've found (loaded) the [Extension] definition and identified the [OpDef] /// /// [Extension]: crate::Extension - Extension(ExtensionOp), + Extension(Box), /// When we either haven't tried to identify the [Extension] or failed to find it. /// /// [Extension]: crate::Extension - Opaque(OpaqueOp), + Opaque(Box), +} + +impl PartialEq for CustomOp { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Extension(l0), Self::Extension(r0)) => l0 == r0, + (Self::Opaque(l0), Self::Opaque(r0)) => l0 == r0, + (Self::Extension(l0), Self::Opaque(r0)) => &l0.make_opaque() == r0.as_ref(), + (Self::Opaque(l0), Self::Extension(r0)) => l0.as_ref() == &r0.make_opaque(), + } + } } -impl ExternalOp { +impl CustomOp { + /// Create a new CustomOp from an [ExtensionOp]. + pub fn new_extension(op: ExtensionOp) -> Self { + Self::Extension(Box::new(op)) + } + + /// Create a new CustomOp from an [OpaqueOp]. + pub fn new_opaque(op: OpaqueOp) -> Self { + Self::Opaque(Box::new(op)) + } + /// Return the argument values for this operation. pub fn args(&self) -> &[TypeArg] { match self { @@ -37,80 +66,95 @@ impl ExternalOp { } } - /// Name of the ExternalOp - pub fn name(&self) -> SmolStr { - let (res_id, op_name) = match self { - Self::Opaque(op) => (&op.extension, &op.op_name), - Self::Extension(ExtensionOp { def, .. }) => (def.extension(), def.name()), - }; - qualify_name(res_id, op_name) - } - /// If the operation is an instance of [ExtensionOp], return a reference to it. /// If the operation is opaque, return None. pub fn as_extension_op(&self) -> Option<&ExtensionOp> { match self { - ExternalOp::Extension(e) => Some(e), - ExternalOp::Opaque(_) => None, + Self::Extension(e) => Some(e), + Self::Opaque(_) => None, } } - /// Downgrades this [ExternalOp] into an OpaqueOp. + /// Downgrades this opaque operation into an [`OpaqueOp`]. pub fn into_opaque(self) -> OpaqueOp { match self { - Self::Opaque(op) => op, - Self::Extension(op) => op.into(), + Self::Opaque(op) => *op, + Self::Extension(op) => (*op).into(), } } -} -impl From for OpaqueOp { - fn from(value: ExternalOp) -> Self { - match value { - ExternalOp::Opaque(op) => op, - ExternalOp::Extension(op) => op.into(), - } + /// Returns `true` if this operation is an instance of [`ExtensionOp`]. + pub fn is_extension_op(&self) -> bool { + matches!(self, Self::Extension(_)) } -} -impl From for ExternalOp { - fn from(op: OpaqueOp) -> Self { - Self::Opaque(op) + /// Returns `true` if this operation is an instance of [`OpaqueOp`]. + pub fn is_opaque(&self) -> bool { + matches!(self, Self::Opaque(_)) } } -impl From for CustomOp { - fn from(value: ExternalOp) -> Self { - Self::new(value) - } -} - -impl From for OpType { - fn from(value: ExternalOp) -> Self { - OpType::CustomOp(CustomOp::new(value)) +impl OpName for CustomOp { + /// The name of the operation. + fn name(&self) -> SmolStr { + let (res_id, op_name) = match self { + Self::Opaque(op) => (&op.extension, &op.op_name), + Self::Extension(ext) => (ext.def.extension(), ext.def.name()), + }; + qualify_name(res_id, op_name) } } -impl DataflowOpTrait for ExternalOp { +impl DataflowOpTrait for CustomOp { const TAG: OpTag = OpTag::Leaf; + /// A human-readable description of the operation. fn description(&self) -> &str { match self { - Self::Opaque(op) => DataflowOpTrait::description(op), - Self::Extension(ext_op) => DataflowOpTrait::description(ext_op), + Self::Opaque(op) => DataflowOpTrait::description(op.as_ref()), + Self::Extension(ext_op) => DataflowOpTrait::description(ext_op.as_ref()), } } + /// The signature of the operation. fn signature(&self) -> FunctionType { match self { Self::Opaque(op) => op.signature.clone(), Self::Extension(ext_op) => ext_op.signature(), } } + + fn other_input(&self) -> Option { + Some(EdgeKind::StateOrder) + } + + fn other_output(&self) -> Option { + Some(EdgeKind::StateOrder) + } +} + +impl From for CustomOp { + fn from(op: OpaqueOp) -> Self { + Self::new_opaque(op) + } +} + +impl From for OpaqueOp { + fn from(value: CustomOp) -> Self { + value.into_opaque() + } +} + +impl From for CustomOp { + fn from(op: ExtensionOp) -> Self { + Self::new_extension(op) + } } /// An operation defined by an [OpDef] from a loaded [Extension]. -/// Note *not* Serializable: container ([ExternalOp]) is serialized as an [OpaqueOp] instead. +/// +/// Extension ops are not serializable. They must be downgraded into an [OpaqueOp] instead. +/// See [ExtensionOp::make_opaque]. /// /// [Extension]: crate::Extension #[derive(Clone, Debug)] @@ -157,7 +201,7 @@ impl ExtensionOp { /// Regenerating the [`ExtensionOp`] back from the [`OpaqueOp`] requires a /// registry with the appropriate extension. See [`resolve_opaque_op`]. /// - /// For a non-cloning version of this operation, see [`ExternalOp::from`]. + /// For a non-cloning version of this operation, use [`OpaqueOp::from`]. pub fn make_opaque(&self) -> OpaqueOp { OpaqueOp { extension: self.def.extension().clone(), @@ -186,12 +230,6 @@ impl From for OpaqueOp { } } -impl From for CustomOp { - fn from(value: ExtensionOp) -> Self { - CustomOp::new(ExternalOp::Extension(value)) - } -} - impl From for OpType { fn from(value: ExtensionOp) -> Self { OpType::CustomOp(value.into()) @@ -204,6 +242,8 @@ impl PartialEq for ExtensionOp { } } +impl Eq for ExtensionOp {} + impl DataflowOpTrait for ExtensionOp { const TAG: OpTag = OpTag::Leaf; @@ -216,8 +256,6 @@ impl DataflowOpTrait for ExtensionOp { } } -impl Eq for ExtensionOp {} - /// An opaquely-serialized op that refers to an as-yet-unresolved [`OpDef`] #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct OpaqueOp { @@ -268,12 +306,6 @@ impl OpaqueOp { } } -impl From for CustomOp { - fn from(value: OpaqueOp) -> Self { - CustomOp::new(ExternalOp::Opaque(value)) - } -} - impl From for OpType { fn from(value: OpaqueOp) -> Self { OpType::CustomOp(value.into()) @@ -299,11 +331,9 @@ pub fn resolve_extension_ops( ) -> Result<(), CustomOpError> { let mut replacements = Vec::new(); for n in h.nodes() { - if let OpType::CustomOp(op) = h.get_optype(n) { - if let ExternalOp::Opaque(opaque) = op.as_ref() { - if let Some(resolved) = resolve_opaque_op(n, opaque, extension_registry)? { - replacements.push((n, resolved)) - } + if let OpType::CustomOp(CustomOp::Opaque(opaque)) = h.get_optype(n) { + if let Some(resolved) = resolve_opaque_op(n, opaque, extension_registry)? { + replacements.push((n, resolved)) } } } @@ -317,14 +347,16 @@ pub fn resolve_extension_ops( Ok(()) } -/// Try to resolve an [`ExternalOp::Opaque`] to a [`ExternalOp::Extension`] +/// Try to resolve a [`OpaqueOp`] to a [`ExtensionOp`] by looking the op up in +/// the registry. /// /// # Return -/// Some if the serialized opaque resolves to an extension-defined op and all is ok; -/// None if the serialized opaque doesn't identify an extension +/// Some if the serialized opaque resolves to an extension-defined op and all is +/// ok; None if the serialized opaque doesn't identify an extension /// /// # Errors -/// If the serialized opaque resolves to a definition that conflicts with what was serialized +/// If the serialized opaque resolves to a definition that conflicts with what +/// was serialized pub fn resolve_opaque_op( _n: Node, opaque: &OpaqueOp, @@ -382,17 +414,19 @@ mod test { #[test] fn new_opaque_op() { let sig = FunctionType::new_endo(vec![QB_T]); - let op = OpaqueOp::new( + let op: CustomOp = OpaqueOp::new( "res".try_into().unwrap(), "op", "desc".into(), vec![TypeArg::Type { ty: USIZE_T }], sig.clone(), - ); - let op: ExternalOp = op.into(); + ) + .into(); assert_eq!(op.name(), "res.op"); assert_eq!(DataflowOpTrait::description(&op), "desc"); assert_eq!(op.args(), &[TypeArg::Type { ty: USIZE_T }]); assert_eq!(op.signature(), sig); + assert!(op.is_opaque()); + assert!(!op.is_extension_op()); } } diff --git a/hugr/src/ops/leaf.rs b/hugr/src/ops/leaf.rs index c667abfc5..178ded03a 100644 --- a/hugr/src/ops/leaf.rs +++ b/hugr/src/ops/leaf.rs @@ -1,10 +1,7 @@ //! Definition of dataflow operations with no children. -use smol_str::SmolStr; - -use super::custom::ExternalOp; use super::dataflow::DataflowOpTrait; -use super::{impl_op_name, OpName, OpTag}; +use super::{impl_op_name, OpTag}; use crate::extension::ExtensionSet; @@ -13,30 +10,6 @@ use crate::{ types::{EdgeKind, FunctionType, Type, TypeRow}, }; -/// A user-defined operation defined in an extension. -/// -/// Any custom operation can be encoded as a serializable [`OpaqueOp`]. If the -/// operation's extension is loaded in the current context, the operation can be -/// resolved into an [`ExtensionOp`] containing a reference to its definition. -/// -/// [`OpaqueOp`]: crate::ops::custom::OpaqueOp -/// [`ExtensionOp`]: crate::ops::custom::ExtensionOp -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct CustomOp(Box); - -impl CustomOp { - /// Create a new custom operation. - pub fn new(op: ExternalOp) -> Self { - Self(Box::new(op)) - } -} - -impl AsRef for CustomOp { - fn as_ref(&self) -> &ExternalOp { - &self.0 - } -} - /// A no-op operation. #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[non_exhaustive] @@ -88,40 +61,12 @@ pub struct Lift { pub new_extension: ExtensionId, } -impl OpName for CustomOp { - /// The name of the operation. - fn name(&self) -> SmolStr { - self.0.name() - } -} impl_op_name!(Noop); impl_op_name!(MakeTuple); impl_op_name!(UnpackTuple); impl_op_name!(Tag); impl_op_name!(Lift); -impl DataflowOpTrait for CustomOp { - const TAG: OpTag = OpTag::Leaf; - - /// A human-readable description of the operation. - fn description(&self) -> &str { - self.0.description() - } - - /// The signature of the operation. - fn signature(&self) -> FunctionType { - self.0.signature() - } - - fn other_input(&self) -> Option { - Some(EdgeKind::StateOrder) - } - - fn other_output(&self) -> Option { - Some(EdgeKind::StateOrder) - } -} - impl DataflowOpTrait for Noop { const TAG: OpTag = OpTag::Leaf;