hugr_model/v0/table/
mod.rs

1//! Table representation of hugr modules.
2//!
3//! Instead of directly nesting data structures, we store them in tables and
4//! refer to them by their id in the table. Variables, symbols and links are
5//! fully resolved: uses refer to the id of the declaration. This allows the
6//! table representation to be read from the [binary format] and imported into
7//! the core data structures without having to perform potentially costly name
8//! resolutions.
9//!
10//! The tabling is also used for deduplication of terms. In practice, many terms
11//! will share the same subterms, and we can save memory and validation time by
12//! storing them only once. However we allow non-deduplicated terms for cases in
13//! which terms carry additional identity over just their structure. For
14//! instance, structurally identical terms could originate from different
15//! locations in a text file and therefore should be treated differently when
16//! locating type errors.
17//!
18//! This format is intended to be used as an intermediary data structure to
19//! convert between different representations (such as the [binary format], the
20//! [text format] or internal compiler data structures). To make this efficient,
21//! we use arena allocation via the [`bumpalo`] crate to efficiently construct and
22//! tear down this representation. The data structures in this module therefore carry
23//! a lifetime parameter that indicates the lifetime of the arena.
24//!
25//! [binary format]: crate::v0::binary
26//! [text format]: crate::v0::ast
27
28use smol_str::SmolStr;
29use thiserror::Error;
30
31mod view;
32use super::{ast, Literal, RegionKind};
33pub use view::View;
34
35/// A package consisting of a sequence of [`Module`]s.
36///
37/// See [`ast::Package`] for the AST representation.
38///
39/// [`ast::Package`]: crate::v0::ast::Package
40#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
41pub struct Package<'a> {
42    /// The modules in the package.
43    pub modules: Vec<Module<'a>>,
44}
45
46impl Package<'_> {
47    /// Convert the package to the [ast] representation.
48    ///
49    /// [ast]: crate::v0::ast
50    pub fn as_ast(&self) -> Option<ast::Package> {
51        let modules = self
52            .modules
53            .iter()
54            .map(|module| module.as_ast())
55            .collect::<Option<_>>()?;
56        Some(ast::Package { modules })
57    }
58}
59
60/// A module consisting of a hugr graph together with terms.
61///
62/// See [`ast::Module`] for the AST representation.
63///
64/// [`ast::Module`]: crate::v0::ast::Module
65#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
66pub struct Module<'a> {
67    /// The id of the root region.
68    pub root: RegionId,
69    /// Table of [`Node`]s.
70    pub nodes: Vec<Node<'a>>,
71    /// Table of [`Region`]s.
72    pub regions: Vec<Region<'a>>,
73    /// Table of [`Term`]s.
74    pub terms: Vec<Term<'a>>,
75}
76
77impl<'a> Module<'a> {
78    /// Return the node data for a given node id.
79    #[inline]
80    pub fn get_node(&self, node_id: NodeId) -> Option<&Node<'a>> {
81        self.nodes.get(node_id.index())
82    }
83
84    /// Return a mutable reference to the node data for a given node id.
85    #[inline]
86    pub fn get_node_mut(&mut self, node_id: NodeId) -> Option<&mut Node<'a>> {
87        self.nodes.get_mut(node_id.index())
88    }
89
90    /// Insert a new node into the module and return its id.
91    pub fn insert_node(&mut self, node: Node<'a>) -> NodeId {
92        let id = NodeId::new(self.nodes.len());
93        self.nodes.push(node);
94        id
95    }
96
97    /// Return the term data for a given term id.
98    #[inline]
99    pub fn get_term(&self, term_id: TermId) -> Option<&Term<'a>> {
100        self.terms.get(term_id.index())
101    }
102
103    /// Return a mutable reference to the term data for a given term id.
104    #[inline]
105    pub fn get_term_mut(&mut self, term_id: TermId) -> Option<&mut Term<'a>> {
106        self.terms.get_mut(term_id.index())
107    }
108
109    /// Insert a new term into the module and return its id.
110    pub fn insert_term(&mut self, term: Term<'a>) -> TermId {
111        let id = TermId::new(self.terms.len());
112        self.terms.push(term);
113        id
114    }
115
116    /// Return the region data for a given region id.
117    #[inline]
118    pub fn get_region(&self, region_id: RegionId) -> Option<&Region<'a>> {
119        self.regions.get(region_id.index())
120    }
121
122    /// Return a mutable reference to the region data for a given region id.
123    #[inline]
124    pub fn get_region_mut(&mut self, region_id: RegionId) -> Option<&mut Region<'a>> {
125        self.regions.get_mut(region_id.index())
126    }
127
128    /// Insert a new region into the module and return its id.
129    pub fn insert_region(&mut self, region: Region<'a>) -> RegionId {
130        let id = RegionId::new(self.regions.len());
131        self.regions.push(region);
132        id
133    }
134
135    /// Attempt to view a part of this module via a [`View`] instance.
136    pub fn view<S, V: View<'a, S>>(&'a self, src: S) -> Option<V> {
137        V::view(self, src)
138    }
139
140    /// Convert the module to the [ast] representation.
141    ///
142    /// [ast]: crate::v0::ast
143    pub fn as_ast(&self) -> Option<ast::Module> {
144        let root = self.view(self.root)?;
145        Some(ast::Module { root })
146    }
147}
148
149/// Nodes in the hugr graph.
150///
151/// See [`ast::Node`] for the AST representation.
152///
153/// [`ast::Node`]: crate::v0::ast::Node
154#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
155pub struct Node<'a> {
156    /// The operation that the node performs.
157    pub operation: Operation<'a>,
158    /// The input ports of the node.
159    pub inputs: &'a [LinkIndex],
160    /// The output ports of the node.
161    pub outputs: &'a [LinkIndex],
162    /// The regions of the node.
163    pub regions: &'a [RegionId],
164    /// The meta information attached to the node.
165    pub meta: &'a [TermId],
166    /// The signature of the node.
167    ///
168    /// Can be `None` to indicate that the node's signature should be inferred,
169    /// or for nodes with operations that do not have a signature.
170    pub signature: Option<TermId>,
171}
172
173/// Operations that nodes can perform.
174///
175/// See [`ast::Operation`] for the AST representation.
176///
177/// [`ast::Operation`]: crate::v0::ast::Operation
178#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
179pub enum Operation<'a> {
180    /// Invalid operation to be used as a placeholder.
181    /// This is useful for modules that have non-contiguous node ids, or modules
182    /// that have not yet been fully constructed.
183    #[default]
184    Invalid,
185    /// Data flow graphs.
186    Dfg,
187    /// Control flow graphs.
188    Cfg,
189    /// Basic blocks in a control flow graph.
190    Block,
191    /// Function definitions.
192    DefineFunc(&'a Symbol<'a>),
193    /// Function declarations.
194    DeclareFunc(&'a Symbol<'a>),
195    /// Custom operation.
196    Custom(TermId),
197    /// Alias definitions.
198    DefineAlias(&'a Symbol<'a>, TermId),
199    /// Alias declarations.
200    DeclareAlias(&'a Symbol<'a>),
201    /// Tail controlled loop.
202    /// Nodes with this operation contain a dataflow graph that is executed in a loop.
203    /// The loop body is executed at least once, producing a result that indicates whether
204    /// to continue the loop or return the result.
205    ///
206    /// # Port Types
207    ///
208    /// - **Inputs**: `inputs` + `rest`
209    /// - **Outputs**: `outputs` + `rest`
210    /// - **Sources**: `inputs` + `rest`
211    /// - **Targets**: `(adt [inputs outputs])` + `rest`
212    TailLoop,
213
214    /// Conditional operation.
215    ///
216    /// # Port types
217    ///
218    /// - **Inputs**: `[(adt inputs)]` + `context`
219    /// - **Outputs**: `outputs`
220    Conditional,
221
222    /// Declaration for a term constructor.
223    ///
224    /// Nodes with this operation must be within a module region.
225    DeclareConstructor(&'a Symbol<'a>),
226
227    /// Declaration for a operation.
228    ///
229    /// Nodes with this operation must be within a module region.
230    DeclareOperation(&'a Symbol<'a>),
231
232    /// Import a symbol.
233    Import {
234        /// The name of the symbol to be imported.
235        name: &'a str,
236    },
237}
238
239impl<'a> Operation<'a> {
240    /// Returns the symbol introduced by the operation, if any.
241    pub fn symbol(&self) -> Option<&'a str> {
242        match self {
243            Operation::DefineFunc(symbol) => Some(symbol.name),
244            Operation::DeclareFunc(symbol) => Some(symbol.name),
245            Operation::DefineAlias(symbol, _) => Some(symbol.name),
246            Operation::DeclareAlias(symbol) => Some(symbol.name),
247            Operation::DeclareConstructor(symbol) => Some(symbol.name),
248            Operation::DeclareOperation(symbol) => Some(symbol.name),
249            Operation::Import { name } => Some(name),
250            _ => None,
251        }
252    }
253}
254
255/// A region in the hugr.
256///
257/// See [`ast::Region`] for the AST representation.
258///
259/// [`ast::Region`]: crate::v0::ast::Region
260#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
261pub struct Region<'a> {
262    /// The kind of the region. See [`RegionKind`] for details.
263    pub kind: RegionKind,
264    /// The source ports of the region.
265    pub sources: &'a [LinkIndex],
266    /// The target ports of the region.
267    pub targets: &'a [LinkIndex],
268    /// The nodes in the region. The order of the nodes is not significant.
269    pub children: &'a [NodeId],
270    /// The metadata attached to the region.
271    pub meta: &'a [TermId],
272    /// The signature of the region.
273    pub signature: Option<TermId>,
274    /// Information about the scope defined by this region, if the region is closed.
275    pub scope: Option<RegionScope>,
276}
277
278/// Information about the scope defined by a closed region.
279#[derive(Debug, Clone, PartialEq, Eq, Hash)]
280pub struct RegionScope {
281    /// The number of links in the scope.
282    pub links: u32,
283    /// The number of ports in the scope.
284    pub ports: u32,
285}
286
287/// A symbol.
288///
289/// See [`ast::Symbol`] for the AST representation.
290///
291/// [`ast::Symbol`]: crate::v0::ast::Symbol
292#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
293pub struct Symbol<'a> {
294    /// The name of the symbol.
295    pub name: &'a str,
296    /// The static parameters.
297    pub params: &'a [Param<'a>],
298    /// The constraints on the static parameters.
299    pub constraints: &'a [TermId],
300    /// The signature of the symbol.
301    pub signature: TermId,
302}
303
304/// An index of a variable within a node's parameter list.
305pub type VarIndex = u16;
306
307/// A term in the compile time meta language.
308///
309/// See [`ast::Term`] for the AST representation.
310///
311/// [`ast::Term`]: crate::v0::ast::Term
312#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
313pub enum Term<'a> {
314    /// Standin for any term.
315    #[default]
316    Wildcard,
317
318    /// A local variable.
319    Var(VarId),
320
321    /// Apply a symbol to a sequence of arguments.
322    ///
323    /// The symbol is defined by a node in the same graph. The type of this term
324    /// is derived from instantiating the symbol's parameters in the symbol's
325    /// signature.
326    Apply(NodeId, &'a [TermId]),
327
328    /// List of static data.
329    ///
330    /// Lists can include individual items or other lists to be spliced in.
331    ///
332    /// **Type:** `(core.list ?t)`
333    List(&'a [SeqPart]),
334
335    /// A static literal value.
336    Literal(Literal),
337
338    /// A constant anonymous function.
339    ///
340    /// **Type:** `(core.const (core.fn ?ins ?outs ?ext) (ext))`
341    Func(RegionId),
342
343    /// Tuple of static data.
344    ///
345    /// Tuples can include individual items or other tuples to be spliced in.
346    ///
347    /// **Type:** `(core.tuple ?types)`
348    Tuple(&'a [SeqPart]),
349}
350
351impl From<Literal> for Term<'_> {
352    fn from(value: Literal) -> Self {
353        Self::Literal(value)
354    }
355}
356
357/// A part of a list/tuple term.
358///
359/// See [`ast::SeqPart`] for the AST representation.
360///
361/// [`ast::SeqPart`]: crate::v0::ast::SeqPart
362#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
363pub enum SeqPart {
364    /// A single item.
365    Item(TermId),
366    /// A list to be spliced into the parent list/tuple.
367    Splice(TermId),
368}
369
370/// A parameter to a function or alias.
371///
372/// Parameter names must be unique within a parameter list.
373///
374/// See [`ast::Param`] for the AST representation.
375///
376/// [`ast::Param`]: crate::v0::ast::Param
377#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
378pub struct Param<'a> {
379    /// The name of the parameter.
380    pub name: &'a str,
381    /// The type of the parameter.
382    pub r#type: TermId,
383}
384
385macro_rules! define_index {
386    ($(#[$meta:meta])* $vis:vis struct $name:ident(pub u32);) => {
387        #[repr(transparent)]
388        $(#[$meta])*
389        $vis struct $name(pub u32);
390
391        impl $name {
392            /// Create a new index.
393            ///
394            /// # Panics
395            ///
396            /// Panics if the index is 2^32 or larger.
397            pub fn new(index: usize) -> Self {
398                assert!(index < u32::MAX as usize, "index out of bounds");
399                Self(index as u32)
400            }
401
402            /// Returns the index as a `usize` to conveniently use it as a slice index.
403            #[inline]
404            pub fn index(self) -> usize {
405                self.0 as usize
406            }
407
408            /// Convert a slice of this index type into a slice of `u32`s.
409            pub fn unwrap_slice(slice: &[Self]) -> &[u32] {
410                // SAFETY: This type is just a newtype around `u32`.
411                unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const u32, slice.len()) }
412            }
413
414            /// Convert a slice of `u32`s into a slice of this index type.
415            pub fn wrap_slice(slice: &[u32]) -> &[Self] {
416                // SAFETY: This type is just a newtype around `u32`.
417                unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const Self, slice.len()) }
418            }
419        }
420    };
421}
422
423define_index! {
424    /// Id of a node in a hugr graph.
425    #[derive(Debug, derive_more::Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
426    pub struct NodeId(pub u32);
427}
428
429define_index! {
430    /// Index of a link in a hugr graph.
431    #[derive(Debug, derive_more::Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
432    pub struct LinkIndex(pub u32);
433}
434
435define_index! {
436    /// Id of a region in a hugr graph.
437    #[derive(Debug, derive_more::Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
438    pub struct RegionId(pub u32);
439}
440
441define_index! {
442    /// Id of a term in a hugr graph.
443    #[derive(Debug, derive_more::Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
444    pub struct TermId(pub u32);
445}
446
447/// The id of a link consisting of its region and the link index.
448#[derive(Debug, derive_more::Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
449#[display("{_0}#{_1}")]
450pub struct LinkId(pub RegionId, pub LinkIndex);
451
452/// The id of a variable consisting of its node and the variable index.
453#[derive(Debug, derive_more::Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
454#[display("{_0}#{_1}")]
455pub struct VarId(pub NodeId, pub VarIndex);
456
457/// Errors that can occur when traversing and interpreting the model.
458#[derive(Debug, Clone, Error)]
459pub enum ModelError {
460    /// There is a reference to a node that does not exist.
461    #[error("node not found: {0}")]
462    NodeNotFound(NodeId),
463    /// There is a reference to a term that does not exist.
464    #[error("term not found: {0}")]
465    TermNotFound(TermId),
466    /// There is a reference to a region that does not exist.
467    #[error("region not found: {0}")]
468    RegionNotFound(RegionId),
469    /// Invalid variable reference.
470    #[error("variable {0} invalid")]
471    InvalidVar(VarId),
472    /// Invalid symbol reference.
473    #[error("symbol reference {0} invalid")]
474    InvalidSymbol(NodeId),
475    /// The model contains an operation in a place where it is not allowed.
476    #[error("unexpected operation on node: {0}")]
477    UnexpectedOperation(NodeId),
478    /// There is a term that is not well-typed.
479    #[error("type error in term: {0}")]
480    TypeError(TermId),
481    /// There is a node whose regions are not well-formed according to the node's operation.
482    #[error("node has invalid regions: {0}")]
483    InvalidRegions(NodeId),
484    /// There is a name that is not well-formed.
485    #[error("malformed name: {0}")]
486    MalformedName(SmolStr),
487    /// There is a condition node that lacks a case for a tag or
488    /// defines two cases for the same tag.
489    #[error("condition node is malformed: {0}")]
490    MalformedCondition(NodeId),
491    /// There is a node that is not well-formed or has the invalid operation.
492    #[error("invalid operation on node: {0}")]
493    InvalidOperation(NodeId),
494}