hugr_core/extension/
resolution.rs

1//! Utilities for resolving operations and types present in a HUGR, and updating
2//! the list of used extensions.
3//!
4//! The functionalities of this module can be called from the type methods
5//! [`crate::ops::OpType::used_extensions`] and
6//! [`crate::types::Signature::used_extensions`].
7//!
8//! When listing "used extensions" we only care about _definitional_ extension
9//! requirements, i.e., the operations and types that are required to define the
10//! HUGR nodes and wire types. This is computed from the union of all extension
11//! required across the HUGR.
12//!
13//! Note: These procedures are only temporary until `hugr-model` is stabilized.
14//! Once that happens, hugrs will no longer be directly deserialized using serde
15//! but instead will be created by the methods in `crate::import`. As these
16//! (will) automatically resolve extensions as the operations are created, we
17//! will no longer require this post-facto resolution step.
18
19mod extension;
20mod ops;
21mod types;
22mod types_mut;
23mod weak_registry;
24
25pub use weak_registry::WeakExtensionRegistry;
26
27pub(crate) use ops::{collect_op_extension, resolve_op_extensions};
28pub(crate) use types::{collect_op_types_extensions, collect_signature_exts, collect_type_exts};
29pub(crate) use types_mut::resolve_op_types_extensions;
30use types_mut::{
31    resolve_custom_type_exts, resolve_term_exts, resolve_type_exts, resolve_value_exts,
32};
33
34use derive_more::{Display, Error, From};
35
36use super::{Extension, ExtensionId, ExtensionRegistry, ExtensionSet};
37use crate::Node;
38use crate::core::HugrNode;
39use crate::ops::constant::ValueName;
40use crate::ops::custom::OpaqueOpError;
41use crate::ops::{NamedOp, OpName, OpType, Value};
42use crate::types::{CustomType, FuncTypeBase, MaybeRV, TypeArg, TypeBase, TypeName};
43
44/// Update all weak Extension pointers inside a type.
45pub fn resolve_type_extensions<RV: MaybeRV>(
46    typ: &mut TypeBase<RV>,
47    extensions: &WeakExtensionRegistry,
48) -> Result<(), ExtensionResolutionError> {
49    let mut used_extensions = WeakExtensionRegistry::default();
50    resolve_type_exts(None, typ, extensions, &mut used_extensions)
51}
52
53/// Update all weak Extension pointers in a custom type.
54pub fn resolve_custom_type_extensions(
55    typ: &mut CustomType,
56    extensions: &WeakExtensionRegistry,
57) -> Result<(), ExtensionResolutionError> {
58    let mut used_extensions = WeakExtensionRegistry::default();
59    resolve_custom_type_exts(None, typ, extensions, &mut used_extensions)
60}
61
62/// Update all weak Extension pointers inside a type argument.
63pub fn resolve_typearg_extensions(
64    arg: &mut TypeArg,
65    extensions: &WeakExtensionRegistry,
66) -> Result<(), ExtensionResolutionError> {
67    let mut used_extensions = WeakExtensionRegistry::default();
68    resolve_term_exts(None, arg, extensions, &mut used_extensions)
69}
70
71/// Update all weak Extension pointers inside a constant value.
72pub fn resolve_value_extensions(
73    value: &mut Value,
74    extensions: &WeakExtensionRegistry,
75) -> Result<(), ExtensionResolutionError> {
76    let mut used_extensions = WeakExtensionRegistry::default();
77    resolve_value_exts(None, value, extensions, &mut used_extensions)
78}
79
80/// Errors that can occur during extension resolution.
81#[derive(Debug, Display, Clone, Error, From, PartialEq)]
82#[non_exhaustive]
83pub enum ExtensionResolutionError<N: HugrNode = Node> {
84    /// Could not resolve an opaque operation to an extension operation.
85    #[display("Error resolving opaque operation: {_0}")]
86    #[from]
87    OpaqueOpError(OpaqueOpError<N>),
88    /// An operation requires an extension that is not in the given registry.
89    #[display(
90        "{op}{} requires extension {missing_extension}, but it could not be found in the extension list used during resolution. The available extensions are: {}",
91        node.map(|n| format!(" in {n}")).unwrap_or_default(),
92        available_extensions.join(", ")
93    )]
94    MissingOpExtension {
95        /// The node that requires the extension.
96        node: Option<N>,
97        /// The operation that requires the extension.
98        op: OpName,
99        /// The missing extension
100        missing_extension: ExtensionId,
101        /// A list of available extensions.
102        available_extensions: Vec<ExtensionId>,
103    },
104    /// A type references an extension that is not in the given registry.
105    #[display(
106        "Type {ty}{} requires extension {missing_extension}, but it could not be found in the extension list used during resolution. The available extensions are: {}",
107        node.map(|n| format!(" in {n}")).unwrap_or_default(),
108        available_extensions.join(", ")
109    )]
110    MissingTypeExtension {
111        /// The node that requires the extension.
112        node: Option<N>,
113        /// The type that requires the extension.
114        ty: TypeName,
115        /// The missing extension
116        missing_extension: ExtensionId,
117        /// A list of available extensions.
118        available_extensions: Vec<ExtensionId>,
119    },
120    /// A type definition's `extension_id` does not match the extension it is in.
121    #[display(
122        "Type definition {def} in extension {extension} declares it was defined in {wrong_extension} instead."
123    )]
124    WrongTypeDefExtension {
125        /// The extension that defines the type.
126        extension: ExtensionId,
127        /// The type definition name.
128        def: TypeName,
129        /// The extension declared in the type definition's `extension_id`.
130        wrong_extension: ExtensionId,
131    },
132    /// An operation definition's `extension_id` does not match the extension it is in.
133    #[display(
134        "Operation definition {def} in extension {extension} declares it was defined in {wrong_extension} instead."
135    )]
136    WrongOpDefExtension {
137        /// The extension that defines the op.
138        extension: ExtensionId,
139        /// The op definition name.
140        def: OpName,
141        /// The extension declared in the op definition's `extension_id`.
142        wrong_extension: ExtensionId,
143    },
144    /// The type of an `OpaqueValue` has types which do not reference their defining extensions.
145    #[display(
146        "The type of the opaque value '{value}' requires extensions {missing_extensions}, but does not reference their definition."
147    )]
148    InvalidConstTypes {
149        /// The value that has invalid types.
150        value: ValueName,
151        /// The missing extension.
152        missing_extensions: ExtensionSet,
153    },
154}
155
156impl<N: HugrNode> ExtensionResolutionError<N> {
157    /// Create a new error for missing operation extensions.
158    pub fn missing_op_extension(
159        node: Option<N>,
160        op: &OpType,
161        missing_extension: &ExtensionId,
162        extensions: &ExtensionRegistry,
163    ) -> Self {
164        Self::MissingOpExtension {
165            node,
166            op: NamedOp::name(op),
167            missing_extension: missing_extension.clone(),
168            available_extensions: extensions.ids().cloned().collect(),
169        }
170    }
171
172    /// Create a new error for missing type extensions.
173    pub fn missing_type_extension(
174        node: Option<N>,
175        ty: &TypeName,
176        missing_extension: &ExtensionId,
177        extensions: &WeakExtensionRegistry,
178    ) -> Self {
179        Self::MissingTypeExtension {
180            node,
181            ty: ty.clone(),
182            missing_extension: missing_extension.clone(),
183            available_extensions: extensions.ids().cloned().collect(),
184        }
185    }
186}
187
188/// Errors that can occur when collecting extension requirements.
189// TODO: [Deprecated] Remove `From` implementation from here
190#[derive(Debug, Display, Clone, Error, From, PartialEq)]
191#[non_exhaustive]
192pub enum ExtensionCollectionError<N: HugrNode = Node> {
193    /// An operation requires an extension that is not in the given registry.
194    #[display(
195        "{op}{} contains custom types for which have lost the reference to their defining extensions. Dropped extensions: {}",
196        if let Some(node) = node { format!(" ({node})") } else { String::new() },
197        missing_extensions.join(", ")
198    )]
199    DroppedOpExtensions {
200        /// The node that is missing extensions.
201        node: Option<N>,
202        /// The operation that is missing extensions.
203        op: OpName,
204        /// The missing extensions.
205        missing_extensions: Vec<ExtensionId>,
206    },
207    /// A signature requires an extension that is not in the given registry.
208    #[display(
209        "Signature {signature} contains custom types for which have lost the reference to their defining extensions. Dropped extensions: {}",
210        missing_extensions.join(", ")
211    )]
212    DroppedSignatureExtensions {
213        /// The signature that is missing extensions.
214        signature: String,
215        /// The missing extensions.
216        missing_extensions: Vec<ExtensionId>,
217    },
218    /// A signature requires an extension that is not in the given registry.
219    #[display(
220        "Type {typ} contains custom types which have lost the reference to their defining extensions. Dropped extensions: {}",
221        missing_extensions.join(", ")
222    )]
223    #[from(ignore)]
224    DroppedTypeExtensions {
225        /// The type that is missing extensions.
226        typ: String,
227        /// The missing extensions.
228        missing_extensions: Vec<ExtensionId>,
229    },
230}
231
232impl<N: HugrNode> ExtensionCollectionError<N> {
233    /// Create a new error when operation extensions have been dropped.
234    pub fn dropped_op_extension(
235        node: Option<N>,
236        op: &OpType,
237        missing_extension: impl IntoIterator<Item = ExtensionId>,
238    ) -> Self {
239        Self::DroppedOpExtensions {
240            node,
241            op: NamedOp::name(op),
242            missing_extensions: missing_extension.into_iter().collect(),
243        }
244    }
245
246    /// Create a new error when signature extensions have been dropped.
247    pub fn dropped_signature<RV: MaybeRV>(
248        signature: &FuncTypeBase<RV>,
249        missing_extension: impl IntoIterator<Item = ExtensionId>,
250    ) -> Self {
251        Self::DroppedSignatureExtensions {
252            signature: format!("{signature}"),
253            missing_extensions: missing_extension.into_iter().collect(),
254        }
255    }
256
257    /// Create a new error when signature extensions have been dropped.
258    pub fn dropped_type<RV: MaybeRV>(
259        typ: &TypeBase<RV>,
260        missing_extension: impl IntoIterator<Item = ExtensionId>,
261    ) -> Self {
262        Self::DroppedTypeExtensions {
263            typ: format!("{typ}"),
264            missing_extensions: missing_extension.into_iter().collect(),
265        }
266    }
267}
268
269#[cfg(test)]
270mod test;