Skip to main content

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