hugr_core/extension/
resolution.rs

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