1pub mod constant;
4pub mod controlflow;
5pub mod custom;
6pub mod dataflow;
7pub mod handle;
8pub mod module;
9pub mod sum;
10pub mod tag;
11pub mod validate;
12use crate::core::HugrNode;
13use crate::extension::resolution::{
14 ExtensionCollectionError, collect_op_extension, collect_op_types_extensions,
15};
16use std::borrow::Cow;
17
18use crate::extension::simple_op::MakeExtensionOp;
19use crate::extension::{ExtensionId, ExtensionRegistry};
20use crate::types::{EdgeKind, Signature, Substitution};
21use crate::{Direction, Node, OutgoingPort, Port};
22use crate::{IncomingPort, PortIndex};
23use handle::NodeHandle;
24use pastey::paste;
25
26use enum_dispatch::enum_dispatch;
27
28pub use constant::{Const, Value};
29pub use controlflow::{BasicBlock, CFG, Case, Conditional, DataflowBlock, ExitBlock, TailLoop};
30pub use custom::{ExtensionOp, OpaqueOp};
31pub use dataflow::{
32 Call, CallIndirect, DFG, DataflowOpTrait, DataflowParent, Input, LoadConstant, LoadFunction,
33 Output,
34};
35pub use module::{AliasDecl, AliasDefn, FuncDecl, FuncDefn, Module};
36use smol_str::SmolStr;
37pub use sum::Tag;
38pub use tag::OpTag;
39
40#[enum_dispatch(OpTrait, NamedOp, ValidateOp, OpParent)]
41#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
42#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
43#[non_exhaustive]
45#[allow(missing_docs)]
46#[serde(tag = "op")]
47pub enum OpType {
48 Module,
49 FuncDefn,
50 FuncDecl,
51 AliasDecl,
52 AliasDefn,
53 Const,
54 Input,
55 Output,
56 Call,
57 CallIndirect,
58 LoadConstant,
59 LoadFunction,
60 DFG,
61 #[serde(skip_deserializing, rename = "Extension")]
62 ExtensionOp,
63 #[serde(rename = "Extension")]
64 OpaqueOp,
65 Tag,
66 DataflowBlock,
67 ExitBlock,
68 TailLoop,
69 CFG,
70 Conditional,
71 Case,
72}
73
74macro_rules! impl_op_ref_try_into {
75 ($Op: tt, $sname:ident) => {
76 paste! {
77 impl OpType {
78 #[doc = "If is an instance of `" $Op "` return a reference to it."]
79 #[must_use] pub fn [<as_ $sname:snake>](&self) -> Option<&$Op> {
80 TryInto::<&$Op>::try_into(self).ok()
81 }
82
83 #[doc = "Returns `true` if the operation is an instance of `" $Op "`."]
84 #[must_use] pub fn [<is_ $sname:snake>](&self) -> bool {
85 self.[<as_ $sname:snake>]().is_some()
86 }
87 }
88
89 impl<'a> TryFrom<&'a OpType> for &'a $Op {
90 type Error = ();
91 fn try_from(optype: &'a OpType) -> Result<Self, Self::Error> {
92 if let OpType::$Op(l) = optype {
93 Ok(l)
94 } else {
95 Err(())
96 }
97 }
98 }
99 }
100 };
101 ($Op:tt) => {
102 impl_op_ref_try_into!($Op, $Op);
103 };
104}
105
106impl_op_ref_try_into!(Module);
107impl_op_ref_try_into!(FuncDefn);
108impl_op_ref_try_into!(FuncDecl);
109impl_op_ref_try_into!(AliasDecl);
110impl_op_ref_try_into!(AliasDefn);
111impl_op_ref_try_into!(Const);
112impl_op_ref_try_into!(Input);
113impl_op_ref_try_into!(Output);
114impl_op_ref_try_into!(Call);
115impl_op_ref_try_into!(CallIndirect);
116impl_op_ref_try_into!(LoadConstant);
117impl_op_ref_try_into!(LoadFunction);
118impl_op_ref_try_into!(DFG, dfg);
119impl_op_ref_try_into!(ExtensionOp);
120impl_op_ref_try_into!(Tag);
121impl_op_ref_try_into!(DataflowBlock);
122impl_op_ref_try_into!(ExitBlock);
123impl_op_ref_try_into!(TailLoop);
124impl_op_ref_try_into!(CFG, cfg);
125impl_op_ref_try_into!(Conditional);
126impl_op_ref_try_into!(Case);
127
128pub const DEFAULT_OPTYPE: OpType = OpType::Module(Module::new());
130
131impl Default for OpType {
132 fn default() -> Self {
133 DEFAULT_OPTYPE
134 }
135}
136
137impl std::fmt::Display for OpType {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 write!(f, "{}", self.name())
140 }
141}
142
143impl OpType {
144 #[inline]
150 #[must_use]
151 pub fn other_port_kind(&self, dir: Direction) -> Option<EdgeKind> {
152 match dir {
153 Direction::Incoming => self.other_input(),
154 Direction::Outgoing => self.other_output(),
155 }
156 }
157
158 #[inline]
165 #[must_use]
166 pub fn static_port_kind(&self, dir: Direction) -> Option<EdgeKind> {
167 match dir {
168 Direction::Incoming => self.static_input(),
169 Direction::Outgoing => self.static_output(),
170 }
171 }
172
173 pub fn port_kind(&self, port: impl Into<Port>) -> Option<EdgeKind> {
179 let signature = self.dataflow_signature().unwrap_or_default();
180 let port: Port = port.into();
181 let dir = port.direction();
182 let port_count = signature.port_count(dir);
183
184 if port.index() < port_count {
186 return signature.port_type(port).cloned().map(EdgeKind::Value);
187 }
188
189 let static_kind = self.static_port_kind(dir);
191 if port.index() == port_count {
192 if let Some(kind) = static_kind {
193 return Some(kind);
194 }
195 }
196
197 self.other_port_kind(dir)
199 }
200
201 #[must_use]
206 pub fn other_port(&self, dir: Direction) -> Option<Port> {
207 let df_count = self.value_port_count(dir);
208 let non_df_count = self.non_df_port_count(dir);
209 let static_input =
211 usize::from(dir == Direction::Incoming && OpTag::StaticInput.is_superset(self.tag()));
212 if self.other_port_kind(dir).is_some() && non_df_count >= 1 {
213 Some(Port::new(dir, df_count + static_input))
214 } else {
215 None
216 }
217 }
218
219 #[inline]
222 #[must_use]
223 pub fn other_input_port(&self) -> Option<IncomingPort> {
224 self.other_port(Direction::Incoming)
225 .map(|p| p.as_incoming().unwrap())
226 }
227
228 #[inline]
231 #[must_use]
232 pub fn other_output_port(&self) -> Option<OutgoingPort> {
233 self.other_port(Direction::Outgoing)
234 .map(|p| p.as_outgoing().unwrap())
235 }
236
237 #[inline]
241 #[must_use]
242 pub fn static_port(&self, dir: Direction) -> Option<Port> {
243 self.static_port_kind(dir)?;
244 Some(Port::new(dir, self.value_port_count(dir)))
245 }
246
247 #[inline]
250 #[must_use]
251 pub fn static_input_port(&self) -> Option<IncomingPort> {
252 self.static_port(Direction::Incoming)
253 .map(|p| p.as_incoming().unwrap())
254 }
255
256 #[inline]
258 #[must_use]
259 pub fn static_output_port(&self) -> Option<OutgoingPort> {
260 self.static_port(Direction::Outgoing)
261 .map(|p| p.as_outgoing().unwrap())
262 }
263
264 #[inline]
266 #[must_use]
267 pub fn value_port_count(&self, dir: portgraph::Direction) -> usize {
268 self.dataflow_signature()
269 .map_or(0, |sig| sig.port_count(dir))
270 }
271
272 #[inline]
274 #[must_use]
275 pub fn value_input_count(&self) -> usize {
276 self.value_port_count(Direction::Incoming)
277 }
278
279 #[inline]
281 #[must_use]
282 pub fn value_output_count(&self) -> usize {
283 self.value_port_count(Direction::Outgoing)
284 }
285
286 #[inline]
288 #[must_use]
289 pub fn port_count(&self, dir: Direction) -> usize {
290 let has_static_port = self.static_port_kind(dir).is_some();
291 let non_df_count = self.non_df_port_count(dir);
292 self.value_port_count(dir) + usize::from(has_static_port) + non_df_count
293 }
294
295 #[inline]
297 #[must_use]
298 pub fn input_count(&self) -> usize {
299 self.port_count(Direction::Incoming)
300 }
301
302 #[inline]
304 #[must_use]
305 pub fn output_count(&self) -> usize {
306 self.port_count(Direction::Outgoing)
307 }
308
309 #[inline]
311 #[must_use]
312 pub fn is_container(&self) -> bool {
313 self.validity_flags::<Node>().allowed_children != OpTag::None
314 }
315
316 pub fn cast<T: MakeExtensionOp>(&self) -> Option<T> {
320 self.as_extension_op().and_then(ExtensionOp::cast)
321 }
322
323 #[must_use]
325 pub fn extension_id(&self) -> Option<&ExtensionId> {
326 match self {
327 OpType::OpaqueOp(opaque) => Some(opaque.extension()),
328 OpType::ExtensionOp(e) => Some(e.def().extension_id()),
329 _ => None,
330 }
331 }
332
333 pub fn used_extensions(&self) -> Result<ExtensionRegistry, ExtensionCollectionError> {
338 let mut reg = collect_op_types_extensions(None, self)?;
340 if let Some(ext) = collect_op_extension(None, self)? {
342 reg.register_updated(ext);
343 }
344 Ok(reg)
345 }
346}
347
348macro_rules! impl_op_name {
351 ($i: ident) => {
352 impl $crate::ops::NamedOp for $i {
353 fn name(&self) -> $crate::ops::OpName {
354 stringify!($i).into()
355 }
356 }
357 };
358}
359
360use impl_op_name;
361
362pub type OpName = SmolStr;
364
365pub type OpNameRef = str;
367
368#[enum_dispatch]
369pub(crate) trait NamedOp {
372 fn name(&self) -> OpName;
374}
375
376pub trait StaticTag {
381 const TAG: OpTag;
383}
384
385#[enum_dispatch]
386pub trait OpTrait: Sized + Clone {
388 fn description(&self) -> &str;
390
391 fn tag(&self) -> OpTag;
393
394 fn try_node_handle<N, H>(&self, node: N) -> Option<H>
400 where
401 N: HugrNode,
402 H: NodeHandle<N> + From<N>,
403 {
404 H::TAG.is_superset(self.tag()).then(|| node.into())
405 }
406
407 fn dataflow_signature(&self) -> Option<Cow<'_, Signature>> {
411 None
412 }
413
414 fn other_input(&self) -> Option<EdgeKind> {
420 None
421 }
422
423 fn other_output(&self) -> Option<EdgeKind> {
429 None
430 }
431
432 fn static_input(&self) -> Option<EdgeKind> {
438 None
439 }
440
441 fn static_output(&self) -> Option<EdgeKind> {
447 None
448 }
449
450 fn non_df_port_count(&self, dir: Direction) -> usize {
452 usize::from(
453 match dir {
454 Direction::Incoming => self.other_input(),
455 Direction::Outgoing => self.other_output(),
456 }
457 .is_some(),
458 )
459 }
460
461 fn substitute(&self, _subst: &Substitution) -> Self {
464 self.clone()
465 }
466}
467
468#[enum_dispatch]
470pub trait OpParent {
471 fn inner_function_type(&self) -> Option<Cow<'_, Signature>> {
476 None
477 }
478}
479
480impl<T: DataflowParent> OpParent for T {
481 fn inner_function_type(&self) -> Option<Cow<'_, Signature>> {
482 Some(DataflowParent::inner_signature(self))
483 }
484}
485
486impl OpParent for Module {}
487impl OpParent for AliasDecl {}
488impl OpParent for AliasDefn {}
489impl OpParent for Const {}
490impl OpParent for Input {}
491impl OpParent for Output {}
492impl OpParent for Call {}
493impl OpParent for CallIndirect {}
494impl OpParent for LoadConstant {}
495impl OpParent for LoadFunction {}
496impl OpParent for ExtensionOp {}
497impl OpParent for OpaqueOp {}
498impl OpParent for Tag {}
499impl OpParent for CFG {}
500impl OpParent for Conditional {}
501impl OpParent for FuncDecl {}
502impl OpParent for ExitBlock {}
503
504#[enum_dispatch]
505pub trait ValidateOp {
507 #[inline]
509 fn validity_flags<N: HugrNode>(&self) -> validate::OpValidityFlags<N> {
510 Default::default()
511 }
512
513 #[inline]
515 fn validate_op_children<'a, N: HugrNode>(
516 &self,
517 _children: impl DoubleEndedIterator<Item = (N, &'a OpType)>,
518 ) -> Result<(), validate::ChildrenValidationError<N>> {
519 Ok(())
520 }
521}
522
523macro_rules! impl_validate_op {
525 ($i: ident) => {
526 impl $crate::ops::ValidateOp for $i {}
527 };
528}
529
530use impl_validate_op;