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 && let Some(kind) = static_kind
193 {
194 return Some(kind);
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]
268 #[must_use]
269 pub fn value_ports(&self, dir: Direction) -> impl DoubleEndedIterator<Item = Port> {
270 (0..self.value_port_count(dir)).map(move |i| Port::new(dir, i))
271 }
272
273 #[inline]
275 #[must_use]
276 pub fn value_input_ports(&self) -> impl DoubleEndedIterator<Item = IncomingPort> {
277 self.value_ports(Direction::Incoming)
278 .map(|p| p.as_incoming().unwrap())
279 }
280
281 #[inline]
283 #[must_use]
284 pub fn value_output_ports(&self) -> impl DoubleEndedIterator<Item = OutgoingPort> {
285 self.value_ports(Direction::Outgoing)
286 .map(|p| p.as_outgoing().unwrap())
287 }
288
289 #[inline]
291 #[must_use]
292 pub fn value_port_count(&self, dir: portgraph::Direction) -> usize {
293 self.dataflow_signature()
294 .map_or(0, |sig| sig.port_count(dir))
295 }
296
297 #[inline]
299 #[must_use]
300 pub fn value_input_count(&self) -> usize {
301 self.value_port_count(Direction::Incoming)
302 }
303
304 #[inline]
306 #[must_use]
307 pub fn value_output_count(&self) -> usize {
308 self.value_port_count(Direction::Outgoing)
309 }
310
311 #[inline]
313 #[must_use]
314 pub fn port_count(&self, dir: Direction) -> usize {
315 let has_static_port = self.static_port_kind(dir).is_some();
316 let non_df_count = self.non_df_port_count(dir);
317 self.value_port_count(dir) + usize::from(has_static_port) + non_df_count
318 }
319
320 #[inline]
322 #[must_use]
323 pub fn input_count(&self) -> usize {
324 self.port_count(Direction::Incoming)
325 }
326
327 #[inline]
329 #[must_use]
330 pub fn output_count(&self) -> usize {
331 self.port_count(Direction::Outgoing)
332 }
333
334 #[inline]
336 #[must_use]
337 pub fn is_container(&self) -> bool {
338 self.validity_flags::<Node>().allowed_children != OpTag::None
339 }
340
341 pub fn cast<T: MakeExtensionOp>(&self) -> Option<T> {
345 self.as_extension_op().and_then(ExtensionOp::cast)
346 }
347
348 #[must_use]
350 pub fn extension_id(&self) -> Option<&ExtensionId> {
351 match self {
352 OpType::OpaqueOp(opaque) => Some(opaque.extension()),
353 OpType::ExtensionOp(e) => Some(e.def().extension_id()),
354 _ => None,
355 }
356 }
357
358 pub fn used_extensions(&self) -> Result<ExtensionRegistry, ExtensionCollectionError> {
363 let mut reg = collect_op_types_extensions(None, self)?;
365 if let Some(ext) = collect_op_extension(None, self)? {
367 reg.register_updated(ext);
368 }
369 Ok(reg)
370 }
371}
372
373macro_rules! impl_op_name {
376 ($i: ident) => {
377 impl $crate::ops::NamedOp for $i {
378 fn name(&self) -> $crate::ops::OpName {
379 stringify!($i).into()
380 }
381 }
382 };
383}
384
385use impl_op_name;
386
387pub type OpName = SmolStr;
389
390pub type OpNameRef = str;
392
393#[enum_dispatch]
394pub(crate) trait NamedOp {
397 fn name(&self) -> OpName;
399}
400
401pub trait StaticTag {
406 const TAG: OpTag;
408}
409
410#[enum_dispatch]
411pub trait OpTrait: Sized + Clone {
413 fn description(&self) -> &str;
415
416 fn tag(&self) -> OpTag;
418
419 fn try_node_handle<N, H>(&self, node: N) -> Option<H>
425 where
426 N: HugrNode,
427 H: NodeHandle<N> + From<N>,
428 {
429 H::TAG.is_superset(self.tag()).then(|| node.into())
430 }
431
432 fn dataflow_signature(&self) -> Option<Cow<'_, Signature>> {
436 None
437 }
438
439 fn other_input(&self) -> Option<EdgeKind> {
445 None
446 }
447
448 fn other_output(&self) -> Option<EdgeKind> {
454 None
455 }
456
457 fn static_input(&self) -> Option<EdgeKind> {
463 None
464 }
465
466 fn static_output(&self) -> Option<EdgeKind> {
472 None
473 }
474
475 fn non_df_port_count(&self, dir: Direction) -> usize {
477 usize::from(
478 match dir {
479 Direction::Incoming => self.other_input(),
480 Direction::Outgoing => self.other_output(),
481 }
482 .is_some(),
483 )
484 }
485
486 fn substitute(&self, _subst: &Substitution) -> Self {
489 self.clone()
490 }
491}
492
493#[enum_dispatch]
495pub trait OpParent {
496 fn inner_function_type(&self) -> Option<Cow<'_, Signature>> {
501 None
502 }
503}
504
505impl<T: DataflowParent> OpParent for T {
506 fn inner_function_type(&self) -> Option<Cow<'_, Signature>> {
507 Some(DataflowParent::inner_signature(self))
508 }
509}
510
511impl OpParent for Module {}
512impl OpParent for AliasDecl {}
513impl OpParent for AliasDefn {}
514impl OpParent for Const {}
515impl OpParent for Input {}
516impl OpParent for Output {}
517impl OpParent for Call {}
518impl OpParent for CallIndirect {}
519impl OpParent for LoadConstant {}
520impl OpParent for LoadFunction {}
521impl OpParent for ExtensionOp {}
522impl OpParent for OpaqueOp {}
523impl OpParent for Tag {}
524impl OpParent for CFG {}
525impl OpParent for Conditional {}
526impl OpParent for FuncDecl {}
527impl OpParent for ExitBlock {}
528
529#[enum_dispatch]
530pub trait ValidateOp {
532 #[inline]
534 fn validity_flags<N: HugrNode>(&self) -> validate::OpValidityFlags<N> {
535 Default::default()
536 }
537
538 #[inline]
540 fn validate_op_children<'a, N: HugrNode>(
541 &self,
542 _children: impl DoubleEndedIterator<Item = (N, &'a OpType)>,
543 ) -> Result<(), validate::ChildrenValidationError<N>> {
544 Ok(())
545 }
546}
547
548macro_rules! impl_validate_op {
550 ($i: ident) => {
551 impl $crate::ops::ValidateOp for $i {}
552 };
553}
554
555use impl_validate_op;