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::extension::resolution::{
13 collect_op_extension, collect_op_types_extensions, ExtensionCollectionError,
14};
15use std::borrow::Cow;
16
17use crate::extension::simple_op::MakeExtensionOp;
18use crate::extension::{ExtensionId, ExtensionRegistry, ExtensionSet};
19use crate::types::{EdgeKind, Signature, Substitution};
20use crate::{Direction, OutgoingPort, Port};
21use crate::{IncomingPort, PortIndex};
22use derive_more::Display;
23use paste::paste;
24use portgraph::NodeIndex;
25
26use enum_dispatch::enum_dispatch;
27
28pub use constant::{Const, Value};
29pub use controlflow::{BasicBlock, Case, Conditional, DataflowBlock, ExitBlock, TailLoop, CFG};
30pub use custom::{ExtensionOp, OpaqueOp};
31pub use dataflow::{
32 Call, CallIndirect, DataflowOpTrait, DataflowParent, Input, LoadConstant, LoadFunction, Output,
33 DFG,
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]
46#[allow(missing_docs)]
47#[serde(tag = "op")]
48pub enum OpType {
49 Module,
50 FuncDefn,
51 FuncDecl,
52 AliasDecl,
53 AliasDefn,
54 Const,
55 Input,
56 Output,
57 Call,
58 CallIndirect,
59 LoadConstant,
60 LoadFunction,
61 DFG,
62 #[serde(skip_deserializing, rename = "Extension")]
63 ExtensionOp,
64 #[serde(rename = "Extension")]
65 OpaqueOp,
66 Tag,
67 DataflowBlock,
68 ExitBlock,
69 TailLoop,
70 CFG,
71 Conditional,
72 Case,
73}
74
75macro_rules! impl_op_ref_try_into {
76 ($Op: tt, $sname:ident) => {
77 paste! {
78 impl OpType {
79 #[doc = "If is an instance of `" $Op "` return a reference to it."]
80 pub fn [<as_ $sname:snake>](&self) -> Option<&$Op> {
81 TryInto::<&$Op>::try_into(self).ok()
82 }
83
84 #[doc = "Returns `true` if the operation is an instance of `" $Op "`."]
85 pub fn [<is_ $sname:snake>](&self) -> bool {
86 self.[<as_ $sname:snake>]().is_some()
87 }
88 }
89
90 impl<'a> TryFrom<&'a OpType> for &'a $Op {
91 type Error = ();
92 fn try_from(optype: &'a OpType) -> Result<Self, Self::Error> {
93 if let OpType::$Op(l) = optype {
94 Ok(l)
95 } else {
96 Err(())
97 }
98 }
99 }
100 }
101 };
102 ($Op:tt) => {
103 impl_op_ref_try_into!($Op, $Op);
104 };
105}
106
107impl_op_ref_try_into!(Module);
108impl_op_ref_try_into!(FuncDefn);
109impl_op_ref_try_into!(FuncDecl);
110impl_op_ref_try_into!(AliasDecl);
111impl_op_ref_try_into!(AliasDefn);
112impl_op_ref_try_into!(Const);
113impl_op_ref_try_into!(Input);
114impl_op_ref_try_into!(Output);
115impl_op_ref_try_into!(Call);
116impl_op_ref_try_into!(CallIndirect);
117impl_op_ref_try_into!(LoadConstant);
118impl_op_ref_try_into!(LoadFunction);
119impl_op_ref_try_into!(DFG, dfg);
120impl_op_ref_try_into!(ExtensionOp);
121impl_op_ref_try_into!(Tag);
122impl_op_ref_try_into!(DataflowBlock);
123impl_op_ref_try_into!(ExitBlock);
124impl_op_ref_try_into!(TailLoop);
125impl_op_ref_try_into!(CFG, cfg);
126impl_op_ref_try_into!(Conditional);
127impl_op_ref_try_into!(Case);
128
129pub const DEFAULT_OPTYPE: OpType = OpType::Module(Module::new());
131
132impl Default for OpType {
133 fn default() -> Self {
134 DEFAULT_OPTYPE
135 }
136}
137
138impl Display for OpType {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 write!(f, "{}", self.name())
141 }
142}
143
144impl OpType {
145 #[inline]
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 pub fn static_port_kind(&self, dir: Direction) -> Option<EdgeKind> {
166 match dir {
167 Direction::Incoming => self.static_input(),
168 Direction::Outgoing => self.static_output(),
169 }
170 }
171
172 pub fn port_kind(&self, port: impl Into<Port>) -> Option<EdgeKind> {
178 let signature = self.dataflow_signature().unwrap_or_default();
179 let port: Port = port.into();
180 let dir = port.direction();
181 let port_count = signature.port_count(dir);
182
183 if port.index() < port_count {
185 return signature.port_type(port).cloned().map(EdgeKind::Value);
186 }
187
188 let static_kind = self.static_port_kind(dir);
190 if port.index() == port_count {
191 if let Some(kind) = static_kind {
192 return Some(kind);
193 }
194 }
195
196 self.other_port_kind(dir)
198 }
199
200 pub fn other_port(&self, dir: Direction) -> Option<Port> {
205 let df_count = self.value_port_count(dir);
206 let non_df_count = self.non_df_port_count(dir);
207 let static_input =
209 (dir == Direction::Incoming && OpTag::StaticInput.is_superset(self.tag())) as usize;
210 if self.other_port_kind(dir).is_some() && non_df_count >= 1 {
211 Some(Port::new(dir, df_count + static_input))
212 } else {
213 None
214 }
215 }
216
217 #[inline]
220 pub fn other_input_port(&self) -> Option<IncomingPort> {
221 self.other_port(Direction::Incoming)
222 .map(|p| p.as_incoming().unwrap())
223 }
224
225 #[inline]
228 pub fn other_output_port(&self) -> Option<OutgoingPort> {
229 self.other_port(Direction::Outgoing)
230 .map(|p| p.as_outgoing().unwrap())
231 }
232
233 #[inline]
237 pub fn static_port(&self, dir: Direction) -> Option<Port> {
238 self.static_port_kind(dir)?;
239 Some(Port::new(dir, self.value_port_count(dir)))
240 }
241
242 #[inline]
245 pub fn static_input_port(&self) -> Option<IncomingPort> {
246 self.static_port(Direction::Incoming)
247 .map(|p| p.as_incoming().unwrap())
248 }
249
250 #[inline]
252 pub fn static_output_port(&self) -> Option<OutgoingPort> {
253 self.static_port(Direction::Outgoing)
254 .map(|p| p.as_outgoing().unwrap())
255 }
256
257 #[inline]
259 pub fn value_port_count(&self, dir: portgraph::Direction) -> usize {
260 self.dataflow_signature()
261 .map(|sig| sig.port_count(dir))
262 .unwrap_or(0)
263 }
264
265 #[inline]
267 pub fn value_input_count(&self) -> usize {
268 self.value_port_count(Direction::Incoming)
269 }
270
271 #[inline]
273 pub fn value_output_count(&self) -> usize {
274 self.value_port_count(Direction::Outgoing)
275 }
276
277 #[inline]
279 pub fn port_count(&self, dir: Direction) -> usize {
280 let has_static_port = self.static_port_kind(dir).is_some();
281 let non_df_count = self.non_df_port_count(dir);
282 self.value_port_count(dir) + has_static_port as usize + non_df_count
283 }
284
285 #[inline]
287 pub fn input_count(&self) -> usize {
288 self.port_count(Direction::Incoming)
289 }
290
291 #[inline]
293 pub fn output_count(&self) -> usize {
294 self.port_count(Direction::Outgoing)
295 }
296
297 #[inline]
299 pub fn is_container(&self) -> bool {
300 self.validity_flags().allowed_children != OpTag::None
301 }
302
303 pub fn cast<T: MakeExtensionOp>(&self) -> Option<T> {
305 self.as_extension_op()
306 .and_then(|o| T::from_extension_op(o).ok())
307 }
308
309 pub fn extension_id(&self) -> Option<&ExtensionId> {
311 match self {
312 OpType::OpaqueOp(opaque) => Some(opaque.extension()),
313 OpType::ExtensionOp(e) => Some(e.def().extension_id()),
314 _ => None,
315 }
316 }
317
318 pub fn used_extensions(&self) -> Result<ExtensionRegistry, ExtensionCollectionError> {
323 let mut reg = collect_op_types_extensions(None, self)?;
325 if let Some(ext) = collect_op_extension(None, self)? {
327 reg.register_updated(ext);
328 }
329 Ok(reg)
330 }
331}
332
333macro_rules! impl_op_name {
336 ($i: ident) => {
337 impl $crate::ops::NamedOp for $i {
338 fn name(&self) -> $crate::ops::OpName {
339 stringify!($i).into()
340 }
341 }
342 };
343}
344
345use impl_op_name;
346
347pub type OpName = SmolStr;
349
350pub type OpNameRef = str;
352
353#[enum_dispatch]
354pub trait NamedOp {
357 fn name(&self) -> OpName;
359}
360
361pub trait StaticTag {
366 const TAG: OpTag;
368}
369
370#[enum_dispatch]
371pub trait OpTrait: Sized + Clone {
373 fn description(&self) -> &str;
375
376 fn tag(&self) -> OpTag;
378
379 fn dataflow_signature(&self) -> Option<Cow<'_, Signature>> {
383 None
384 }
385
386 fn extension_delta(&self) -> ExtensionSet {
389 ExtensionSet::new()
390 }
391
392 fn other_input(&self) -> Option<EdgeKind> {
398 None
399 }
400
401 fn other_output(&self) -> Option<EdgeKind> {
407 None
408 }
409
410 fn static_input(&self) -> Option<EdgeKind> {
416 None
417 }
418
419 fn static_output(&self) -> Option<EdgeKind> {
425 None
426 }
427
428 fn non_df_port_count(&self, dir: Direction) -> usize {
430 match dir {
431 Direction::Incoming => self.other_input(),
432 Direction::Outgoing => self.other_output(),
433 }
434 .is_some() as usize
435 }
436
437 fn substitute(&self, _subst: &Substitution) -> Self {
440 self.clone()
441 }
442}
443
444#[enum_dispatch]
446pub trait OpParent {
447 fn inner_function_type(&self) -> Option<Cow<'_, Signature>> {
452 None
453 }
454}
455
456impl<T: DataflowParent> OpParent for T {
457 fn inner_function_type(&self) -> Option<Cow<'_, Signature>> {
458 Some(DataflowParent::inner_signature(self))
459 }
460}
461
462impl OpParent for Module {}
463impl OpParent for AliasDecl {}
464impl OpParent for AliasDefn {}
465impl OpParent for Const {}
466impl OpParent for Input {}
467impl OpParent for Output {}
468impl OpParent for Call {}
469impl OpParent for CallIndirect {}
470impl OpParent for LoadConstant {}
471impl OpParent for LoadFunction {}
472impl OpParent for ExtensionOp {}
473impl OpParent for OpaqueOp {}
474impl OpParent for Tag {}
475impl OpParent for CFG {}
476impl OpParent for Conditional {}
477impl OpParent for FuncDecl {}
478impl OpParent for ExitBlock {}
479
480#[enum_dispatch]
481pub trait ValidateOp {
483 #[inline]
485 fn validity_flags(&self) -> validate::OpValidityFlags {
486 Default::default()
487 }
488
489 #[inline]
491 fn validate_op_children<'a>(
492 &self,
493 _children: impl DoubleEndedIterator<Item = (NodeIndex, &'a OpType)>,
494 ) -> Result<(), validate::ChildrenValidationError> {
495 Ok(())
496 }
497}
498
499macro_rules! impl_validate_op {
501 ($i: ident) => {
502 impl $crate::ops::ValidateOp for $i {}
503 };
504}
505
506use impl_validate_op;