hugr_core/extension/
resolution.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
//! Utilities for resolving operations and types present in a HUGR, and updating
//! the list of used extensions. The functionalities of this module can be
//! called from the type methods [`crate::Hugr::resolve_extension_defs`],
//! [`crate::ops::OpType::used_extensions`], and
//! [`crate::types::Signature::used_extensions`].
//!
//! When listing "used extensions" we only care about _definitional_ extension
//! requirements, i.e., the operations and types that are required to define the
//! HUGR nodes and wire types. This is computed from the union of all extension
//! required across the HUGR.
//!
//! This is distinct from _runtime_ extension requirements, which are defined
//! more granularly in each function signature by the `runtime_reqs`
//! field. See the `extension_inference` feature and related modules for that.
//!
//! Note: These procedures are only temporary until `hugr-model` is stabilized.
//! Once that happens, hugrs will no longer be directly deserialized using serde
//! but instead will be created by the methods in `crate::import`. As these
//! (will) automatically resolve extensions as the operations are created,
//! we will no longer require this post-facto resolution step.

mod extension;
mod ops;
mod types;
mod types_mut;
mod weak_registry;

pub use weak_registry::WeakExtensionRegistry;

pub(crate) use ops::{collect_op_extension, resolve_op_extensions};
pub(crate) use types::{collect_op_types_extensions, collect_signature_exts};
pub(crate) use types_mut::resolve_op_types_extensions;
use types_mut::{
    resolve_custom_type_exts, resolve_type_exts, resolve_typearg_exts, resolve_value_exts,
};

use derive_more::{Display, Error, From};

use super::{Extension, ExtensionId, ExtensionRegistry, ExtensionSet};
use crate::ops::constant::ValueName;
use crate::ops::custom::OpaqueOpError;
use crate::ops::{NamedOp, OpName, OpType, Value};
use crate::types::{CustomType, FuncTypeBase, MaybeRV, TypeArg, TypeBase, TypeName};
use crate::Node;

/// Update all weak Extension pointers inside a type.
pub fn resolve_type_extensions<RV: MaybeRV>(
    typ: &mut TypeBase<RV>,
    extensions: &WeakExtensionRegistry,
) -> Result<(), ExtensionResolutionError> {
    let mut used_extensions = WeakExtensionRegistry::default();
    resolve_type_exts(None, typ, extensions, &mut used_extensions)
}

/// Update all weak Extension pointers in a custom type.
pub fn resolve_custom_type_extensions(
    typ: &mut CustomType,
    extensions: &WeakExtensionRegistry,
) -> Result<(), ExtensionResolutionError> {
    let mut used_extensions = WeakExtensionRegistry::default();
    resolve_custom_type_exts(None, typ, extensions, &mut used_extensions)
}

/// Update all weak Extension pointers inside a type argument.
pub fn resolve_typearg_extensions(
    arg: &mut TypeArg,
    extensions: &WeakExtensionRegistry,
) -> Result<(), ExtensionResolutionError> {
    let mut used_extensions = WeakExtensionRegistry::default();
    resolve_typearg_exts(None, arg, extensions, &mut used_extensions)
}

/// Update all weak Extension pointers inside a constant value.
pub fn resolve_value_extensions(
    value: &mut Value,
    extensions: &WeakExtensionRegistry,
) -> Result<(), ExtensionResolutionError> {
    let mut used_extensions = WeakExtensionRegistry::default();
    resolve_value_exts(None, value, extensions, &mut used_extensions)
}

/// Errors that can occur during extension resolution.
#[derive(Debug, Display, Clone, Error, From, PartialEq)]
#[non_exhaustive]
pub enum ExtensionResolutionError {
    /// Could not resolve an opaque operation to an extension operation.
    #[display("Error resolving opaque operation: {_0}")]
    #[from]
    OpaqueOpError(OpaqueOpError),
    /// An operation requires an extension that is not in the given registry.
    #[display(
        "{op}{} requires extension {missing_extension}, but it could not be found in the extension list used during resolution. The available extensions are: {}",
        node.map(|n| format!(" in {}", n)).unwrap_or_default(),
        available_extensions.join(", ")
    )]
    MissingOpExtension {
        /// The node that requires the extension.
        node: Option<Node>,
        /// The operation that requires the extension.
        op: OpName,
        /// The missing extension
        missing_extension: ExtensionId,
        /// A list of available extensions.
        available_extensions: Vec<ExtensionId>,
    },
    /// A type references an extension that is not in the given registry.
    #[display(
        "Type {ty}{} requires extension {missing_extension}, but it could not be found in the extension list used during resolution. The available extensions are: {}",
        node.map(|n| format!(" in {}", n)).unwrap_or_default(),
        available_extensions.join(", ")
    )]
    MissingTypeExtension {
        /// The node that requires the extension.
        node: Option<Node>,
        /// The type that requires the extension.
        ty: TypeName,
        /// The missing extension
        missing_extension: ExtensionId,
        /// A list of available extensions.
        available_extensions: Vec<ExtensionId>,
    },
    /// A type definition's `extension_id` does not match the extension it is in.
    #[display(
        "Type definition {def} in extension {extension} declares it was defined in {wrong_extension} instead."
    )]
    WrongTypeDefExtension {
        /// The extension that defines the type.
        extension: ExtensionId,
        /// The type definition name.
        def: TypeName,
        /// The extension declared in the type definition's `extension_id`.
        wrong_extension: ExtensionId,
    },
    /// An operation definition's `extension_id` does not match the extension it is in.
    #[display(
        "Operation definition {def} in extension {extension} declares it was defined in {wrong_extension} instead."
    )]
    WrongOpDefExtension {
        /// The extension that defines the op.
        extension: ExtensionId,
        /// The op definition name.
        def: OpName,
        /// The extension declared in the op definition's `extension_id`.
        wrong_extension: ExtensionId,
    },
    /// The type of an `OpaqueValue` has types which do not reference their defining extensions.
    #[display("The type of the opaque value '{value}' requires extensions {missing_extensions}, but does not reference their definition.")]
    InvalidConstTypes {
        /// The value that has invalid types.
        value: ValueName,
        /// The missing extension.
        missing_extensions: ExtensionSet,
    },
}

impl ExtensionResolutionError {
    /// Create a new error for missing operation extensions.
    pub fn missing_op_extension(
        node: Option<Node>,
        op: &OpType,
        missing_extension: &ExtensionId,
        extensions: &ExtensionRegistry,
    ) -> Self {
        Self::MissingOpExtension {
            node,
            op: NamedOp::name(op),
            missing_extension: missing_extension.clone(),
            available_extensions: extensions.ids().cloned().collect(),
        }
    }

    /// Create a new error for missing type extensions.
    pub fn missing_type_extension(
        node: Option<Node>,
        ty: &TypeName,
        missing_extension: &ExtensionId,
        extensions: &WeakExtensionRegistry,
    ) -> Self {
        Self::MissingTypeExtension {
            node,
            ty: ty.clone(),
            missing_extension: missing_extension.clone(),
            available_extensions: extensions.ids().cloned().collect(),
        }
    }
}

/// Errors that can occur when collecting extension requirements.
#[derive(Debug, Display, Clone, Error, From, PartialEq)]
#[non_exhaustive]
pub enum ExtensionCollectionError {
    /// An operation requires an extension that is not in the given registry.
    #[display(
        "{op}{} contains custom types for which have lost the reference to their defining extensions. Dropped extensions: {}",
        if let Some(node) = node { format!(" ({})", node) } else { "".to_string() },
        missing_extensions.join(", ")
    )]
    DroppedOpExtensions {
        /// The node that is missing extensions.
        node: Option<Node>,
        /// The operation that is missing extensions.
        op: OpName,
        /// The missing extensions.
        missing_extensions: Vec<ExtensionId>,
    },
    /// A signature requires an extension that is not in the given registry.
    #[display(
        "Signature {signature} contains custom types for which have lost the reference to their defining extensions. Dropped extensions: {}",
        missing_extensions.join(", ")
    )]
    DroppedSignatureExtensions {
        /// The signature that is missing extensions.
        signature: String,
        /// The missing extensions.
        missing_extensions: Vec<ExtensionId>,
    },
}

impl ExtensionCollectionError {
    /// Create a new error when operation extensions have been dropped.
    pub fn dropped_op_extension(
        node: Option<Node>,
        op: &OpType,
        missing_extension: impl IntoIterator<Item = ExtensionId>,
    ) -> Self {
        Self::DroppedOpExtensions {
            node,
            op: NamedOp::name(op),
            missing_extensions: missing_extension.into_iter().collect(),
        }
    }

    /// Create a new error when signature extensions have been dropped.
    pub fn dropped_signature<RV: MaybeRV>(
        signature: &FuncTypeBase<RV>,
        missing_extension: impl IntoIterator<Item = ExtensionId>,
    ) -> Self {
        Self::DroppedSignatureExtensions {
            signature: format!("{signature}"),
            missing_extensions: missing_extension.into_iter().collect(),
        }
    }
}

#[cfg(test)]
mod test;