miden_assembly_syntax/library/
mod.rs

1use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
2
3use miden_core::{
4    AdviceMap, Kernel, Word,
5    mast::{MastForest, MastNodeExt, MastNodeId},
6    utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
7};
8use midenc_hir_type::{FunctionType, Type};
9#[cfg(feature = "arbitrary")]
10use proptest::prelude::*;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13use smallvec::SmallVec;
14
15use crate::ast::{AttributeSet, QualifiedProcedureName};
16
17mod error;
18mod module;
19mod namespace;
20mod path;
21
22pub use module::{ModuleInfo, ProcedureInfo};
23pub use semver::{Error as VersionError, Version};
24
25pub use self::{
26    error::LibraryError,
27    namespace::{LibraryNamespace, LibraryNamespaceError},
28    path::{LibraryPath, LibraryPathComponent, PathError},
29};
30
31// LIBRARY EXPORT
32// ================================================================================================
33
34/// Metadata about a procedure exported by the interface of a [Library]
35#[derive(Debug, Clone, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
38pub struct LibraryExport {
39    /// The id of the MAST root node of the exported procedure
40    pub node: MastNodeId,
41    /// The fully-qualified name of the exported procedure
42    pub name: QualifiedProcedureName,
43    /// The type signature of the exported procedure, if known
44    #[cfg_attr(feature = "serde", serde(default))]
45    pub signature: Option<FunctionType>,
46    #[cfg_attr(feature = "serde", serde(default))]
47    pub attributes: AttributeSet,
48}
49
50impl LibraryExport {
51    /// Create a new [LibraryExport] representing the export of `node` with `name`
52    pub fn new(node: MastNodeId, name: QualifiedProcedureName) -> Self {
53        Self {
54            node,
55            name,
56            signature: None,
57            attributes: Default::default(),
58        }
59    }
60
61    /// Specify the type signature and ABI of this export
62    pub fn with_signature(mut self, signature: FunctionType) -> Self {
63        self.signature = Some(signature);
64        self
65    }
66
67    /// Specify the set of attributes attached to this export
68    pub fn with_attributes(mut self, attrs: AttributeSet) -> Self {
69        self.attributes = attrs;
70        self
71    }
72}
73
74#[cfg(feature = "arbitrary")]
75impl Arbitrary for LibraryExport {
76    type Parameters = ();
77
78    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
79        use proptest::collection::vec as prop_vec;
80
81        // Generate a small set of simple types for params/results to keep strategies fast/stable
82        let simple_type = prop_oneof![Just(Type::Felt), Just(Type::U32), Just(Type::U64),];
83
84        // Small vectors of params/results
85        let params = prop_vec(simple_type.clone(), 0..=4);
86        let results = prop_vec(simple_type, 0..=2);
87
88        // Use Fast ABI for roundtrip coverage
89        let abi = Just(midenc_hir_type::CallConv::Fast);
90
91        // Option<FunctionType>
92        let signature =
93            prop::option::of((abi, params, results).prop_map(|(abi, params_vec, results_vec)| {
94                let params = SmallVec::<[Type; 4]>::from_vec(params_vec);
95                let results = SmallVec::<[Type; 1]>::from_vec(results_vec);
96                FunctionType { abi, params, results }
97            }));
98
99        let nid = any::<MastNodeId>();
100        let name = any::<QualifiedProcedureName>();
101        (nid, name, signature)
102            .prop_map(|(nodeid, procname, signature)| LibraryExport {
103                node: nodeid,
104                name: procname,
105                signature,
106                attributes: Default::default(),
107            })
108            .boxed()
109    }
110
111    type Strategy = BoxedStrategy<Self>;
112}
113
114// LIBRARY
115// ================================================================================================
116
117/// Represents a library where all modules were compiled into a [`MastForest`].
118///
119/// A library exports a set of one or more procedures. Currently, all exported procedures belong
120/// to the same top-level namespace.
121#[derive(Debug, Clone, PartialEq, Eq)]
122#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
123pub struct Library {
124    /// The content hash of this library, formed by hashing the roots of all exports in
125    /// lexicographical order (by digest, not procedure name)
126    digest: Word,
127    /// A map between procedure paths and the corresponding procedure metadata in the MAST forest.
128    /// Multiple paths can map to the same root, and also, some roots may not be associated with
129    /// any paths.
130    ///
131    /// Note that we use `MastNodeId` as an identifier for procedures instead of MAST root, since 2
132    /// different procedures with the same MAST root can be different due to the decorators they
133    /// contain. However, note that `MastNodeId` is also not a unique identifier for procedures; if
134    /// the procedures have the same MAST root and decorators, they will have the same
135    /// `MastNodeId`.
136    exports: BTreeMap<QualifiedProcedureName, LibraryExport>,
137    /// The MAST forest underlying this library.
138    mast_forest: Arc<MastForest>,
139}
140
141impl AsRef<Library> for Library {
142    #[inline(always)]
143    fn as_ref(&self) -> &Library {
144        self
145    }
146}
147
148// ------------------------------------------------------------------------------------------------
149/// Constructors
150impl Library {
151    /// Constructs a new [`Library`] from the provided MAST forest and a set of exports.
152    ///
153    /// # Errors
154    /// Returns an error if the set of exports is empty.
155    /// Returns an error if any of the specified exports do not have a corresponding procedure root
156    /// in the provided MAST forest.
157    pub fn new(
158        mast_forest: Arc<MastForest>,
159        exports: BTreeMap<QualifiedProcedureName, LibraryExport>,
160    ) -> Result<Self, LibraryError> {
161        if exports.is_empty() {
162            return Err(LibraryError::NoExport);
163        }
164        for LibraryExport { name, node, .. } in exports.values() {
165            if !mast_forest.is_procedure_root(*node) {
166                return Err(LibraryError::NoProcedureRootForExport {
167                    procedure_path: name.clone(),
168                });
169            }
170        }
171
172        let digest =
173            mast_forest.compute_nodes_commitment(exports.values().map(|export| &export.node));
174
175        Ok(Self { digest, exports, mast_forest })
176    }
177
178    /// Produces a new library with the existing [`MastForest`] and where all key/values in the
179    /// provided advice map are added to the internal advice map.
180    pub fn with_advice_map(self, advice_map: AdviceMap) -> Self {
181        let mut mast_forest = (*self.mast_forest).clone();
182        mast_forest.advice_map_mut().extend(advice_map);
183        Self {
184            mast_forest: Arc::new(mast_forest),
185            ..self
186        }
187    }
188}
189
190// ------------------------------------------------------------------------------------------------
191/// Public accessors
192impl Library {
193    /// Returns the [Word] representing the content hash of this library
194    pub fn digest(&self) -> &Word {
195        &self.digest
196    }
197
198    /// Returns the fully qualified name and metadata of all procedures exported by the library.
199    pub fn exports(&self) -> impl Iterator<Item = &LibraryExport> {
200        self.exports.values()
201    }
202
203    /// Returns the number of exports in this library.
204    pub fn num_exports(&self) -> usize {
205        self.exports.len()
206    }
207
208    /// Returns a MAST node ID associated with the specified exported procedure.
209    ///
210    /// # Panics
211    /// Panics if the specified procedure is not exported from this library.
212    pub fn get_export_node_id(&self, proc_name: &QualifiedProcedureName) -> MastNodeId {
213        self.exports
214            .get(proc_name)
215            .expect("procedure not exported from the library")
216            .node
217    }
218
219    /// Returns true if the specified exported procedure is re-exported from a dependency.
220    pub fn is_reexport(&self, proc_name: &QualifiedProcedureName) -> bool {
221        self.exports
222            .get(proc_name)
223            .map(|export| self.mast_forest[export.node].is_external())
224            .unwrap_or(false)
225    }
226
227    /// Returns a reference to the inner [`MastForest`].
228    pub fn mast_forest(&self) -> &Arc<MastForest> {
229        &self.mast_forest
230    }
231
232    /// Returns the digest of the procedure with the specified name, or `None` if it was not found
233    /// in the library or its library path is malformed.
234    pub fn get_procedure_root_by_name(
235        &self,
236        proc_name: impl TryInto<QualifiedProcedureName>,
237    ) -> Option<Word> {
238        if let Ok(qualified_proc_name) = proc_name.try_into() {
239            let export = self.exports.get(&qualified_proc_name);
240            export.map(|e| self.mast_forest()[e.node].digest())
241        } else {
242            None
243        }
244    }
245}
246
247/// Conversions
248impl Library {
249    /// Returns an iterator over the module infos of the library.
250    pub fn module_infos(&self) -> impl Iterator<Item = ModuleInfo> {
251        let mut modules_by_path: BTreeMap<LibraryPath, ModuleInfo> = BTreeMap::new();
252
253        for LibraryExport { node, name, signature, attributes } in self.exports.values() {
254            modules_by_path
255                .entry(name.module.clone())
256                .and_modify(|compiled_module| {
257                    let proc_digest = self.mast_forest[*node].digest();
258                    compiled_module.add_procedure(
259                        name.name.clone(),
260                        proc_digest,
261                        signature.clone().map(Arc::new),
262                        attributes.clone(),
263                    );
264                })
265                .or_insert_with(|| {
266                    let mut module_info = ModuleInfo::new(name.module.clone());
267
268                    let proc_digest = self.mast_forest[*node].digest();
269                    module_info.add_procedure(
270                        name.name.clone(),
271                        proc_digest,
272                        signature.clone().map(Arc::new),
273                        attributes.clone(),
274                    );
275
276                    module_info
277                });
278        }
279
280        modules_by_path.into_values()
281    }
282}
283
284#[cfg(feature = "std")]
285impl Library {
286    /// File extension for the Assembly Library.
287    pub const LIBRARY_EXTENSION: &'static str = "masl";
288
289    /// Write the library to a target file
290    ///
291    /// NOTE: It is up to the caller to use the correct file extension, but there is no
292    /// specific requirement that the extension be set, or the same as
293    /// [`Self::LIBRARY_EXTENSION`].
294    pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
295        let path = path.as_ref();
296
297        if let Some(dir) = path.parent() {
298            std::fs::create_dir_all(dir)?;
299        }
300
301        // NOTE: We catch panics due to i/o errors here due to the fact that the ByteWriter
302        // trait does not provide fallible APIs, so WriteAdapter will panic if the underlying
303        // writes fail. This needs to be addressed in winterfell at some point
304        std::panic::catch_unwind(|| {
305            let mut file = std::fs::File::create(path)?;
306            self.write_into(&mut file);
307            Ok(())
308        })
309        .map_err(|p| {
310            match p.downcast::<std::io::Error>() {
311                // SAFETY: It is guaranteed safe to read Box<std::io::Error>
312                Ok(err) => unsafe { core::ptr::read(&*err) },
313                Err(err) => std::panic::resume_unwind(err),
314            }
315        })?
316    }
317
318    pub fn deserialize_from_file(
319        path: impl AsRef<std::path::Path>,
320    ) -> Result<Self, DeserializationError> {
321        use miden_core::utils::ReadAdapter;
322
323        let path = path.as_ref();
324        let mut file = std::fs::File::open(path).map_err(|err| {
325            DeserializationError::InvalidValue(format!(
326                "failed to open file at {}: {err}",
327                path.to_string_lossy()
328            ))
329        })?;
330        let mut adapter = ReadAdapter::new(&mut file);
331
332        Self::read_from(&mut adapter)
333    }
334}
335
336// KERNEL LIBRARY
337// ================================================================================================
338
339/// Represents a library containing a Miden VM kernel.
340///
341/// This differs from the regular [Library] as follows:
342/// - All exported procedures must be exported directly from the kernel namespace (i.e., `$kernel`).
343/// - There must be at least one exported procedure.
344/// - The number of exported procedures cannot exceed [Kernel::MAX_NUM_PROCEDURES] (i.e., 256).
345#[derive(Debug, Clone, PartialEq, Eq)]
346#[cfg_attr(feature = "serde", derive(Deserialize))]
347#[cfg_attr(feature = "serde", serde(try_from = "Library"))]
348pub struct KernelLibrary {
349    #[cfg_attr(feature = "serde", serde(skip))]
350    kernel: Kernel,
351    #[cfg_attr(feature = "serde", serde(skip))]
352    kernel_info: ModuleInfo,
353    library: Library,
354}
355
356#[cfg(feature = "serde")]
357impl serde::Serialize for KernelLibrary {
358    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
359    where
360        S: serde::Serializer,
361    {
362        Library::serialize(&self.library, serializer)
363    }
364}
365
366impl AsRef<Library> for KernelLibrary {
367    #[inline(always)]
368    fn as_ref(&self) -> &Library {
369        &self.library
370    }
371}
372
373impl KernelLibrary {
374    /// Returns the [Kernel] for this kernel library.
375    pub fn kernel(&self) -> &Kernel {
376        &self.kernel
377    }
378
379    /// Returns a reference to the inner [`MastForest`].
380    pub fn mast_forest(&self) -> &Arc<MastForest> {
381        self.library.mast_forest()
382    }
383
384    /// Destructures this kernel library into individual parts.
385    pub fn into_parts(self) -> (Kernel, ModuleInfo, Arc<MastForest>) {
386        (self.kernel, self.kernel_info, self.library.mast_forest)
387    }
388}
389
390impl TryFrom<Library> for KernelLibrary {
391    type Error = LibraryError;
392
393    fn try_from(library: Library) -> Result<Self, Self::Error> {
394        let kernel_path = LibraryPath::from(LibraryNamespace::Kernel);
395        let mut proc_digests = Vec::with_capacity(library.exports.len());
396
397        let mut kernel_module = ModuleInfo::new(kernel_path.clone());
398
399        for export in library.exports.values() {
400            // make sure all procedures are exported only from the kernel root
401            if export.name.module != kernel_path {
402                return Err(LibraryError::InvalidKernelExport {
403                    procedure_path: export.name.clone(),
404                });
405            }
406
407            let proc_digest = library.mast_forest[export.node].digest();
408            proc_digests.push(proc_digest);
409            kernel_module.add_procedure(
410                export.name.name.clone(),
411                proc_digest,
412                export.signature.clone().map(Arc::new),
413                export.attributes.clone(),
414            );
415        }
416
417        let kernel = Kernel::new(&proc_digests).map_err(LibraryError::KernelConversion)?;
418
419        Ok(Self {
420            kernel,
421            kernel_info: kernel_module,
422            library,
423        })
424    }
425}
426
427#[cfg(feature = "std")]
428impl KernelLibrary {
429    /// Write the library to a target file
430    pub fn write_to_file(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
431        self.library.write_to_file(path)
432    }
433}
434
435// LIBRARY SERIALIZATION
436// ================================================================================================
437
438/// NOTE: Serialization of libraries is likely to be deprecated in a future release
439impl Serializable for Library {
440    fn write_into<W: ByteWriter>(&self, target: &mut W) {
441        let Self { digest: _, exports, mast_forest } = self;
442
443        mast_forest.write_into(target);
444
445        target.write_usize(exports.len());
446        for LibraryExport { node, name, signature, attributes: _ } in exports.values() {
447            name.module.write_into(target);
448            name.name.write_into(target);
449            target.write_u32(u32::from(*node));
450            if let Some(sig) = signature {
451                target.write_bool(true);
452                FunctionTypeSerializer(sig).write_into(target);
453            } else {
454                target.write_bool(false);
455            }
456        }
457    }
458}
459
460/// NOTE: Serialization of libraries is likely to be deprecated in a future release
461impl Deserializable for Library {
462    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
463        let mast_forest = Arc::new(MastForest::read_from(source)?);
464
465        let num_exports = source.read_usize()?;
466        if num_exports == 0 {
467            return Err(DeserializationError::InvalidValue(String::from("No exported procedures")));
468        };
469        let mut exports = BTreeMap::new();
470        for _ in 0..num_exports {
471            let proc_module = source.read()?;
472            let proc_name = source.read()?;
473            let proc_name = QualifiedProcedureName::new(proc_module, proc_name);
474            let node = MastNodeId::from_u32_safe(source.read_u32()?, &mast_forest)?;
475            let signature = if source.read_bool()? {
476                Some(FunctionTypeDeserializer::read_from(source)?.0)
477            } else {
478                None
479            };
480            let export = LibraryExport {
481                node,
482                name: proc_name.clone(),
483                signature,
484                attributes: Default::default(),
485            };
486
487            exports.insert(proc_name, export);
488        }
489
490        let digest = mast_forest.compute_nodes_commitment(exports.values().map(|e| &e.node));
491
492        Ok(Self { digest, exports, mast_forest })
493    }
494}
495
496#[cfg(feature = "serde")]
497impl serde::Serialize for Library {
498    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
499    where
500        S: serde::Serializer,
501    {
502        use serde::ser::SerializeStruct;
503
504        struct LibraryExports<'a>(&'a BTreeMap<QualifiedProcedureName, LibraryExport>);
505        impl serde::Serialize for LibraryExports<'_> {
506            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
507            where
508                S: serde::Serializer,
509            {
510                use serde::ser::SerializeSeq;
511                let mut serializer = serializer.serialize_seq(Some(self.0.len()))?;
512                for elem in self.0.values() {
513                    serializer.serialize_element(elem)?;
514                }
515                serializer.end()
516            }
517        }
518
519        let Self { digest: _, exports, mast_forest } = self;
520
521        let mut serializer = serializer.serialize_struct("Library", 2)?;
522        serializer.serialize_field("mast_forest", mast_forest)?;
523        serializer.serialize_field("exports", &LibraryExports(exports))?;
524        serializer.end()
525    }
526}
527
528#[cfg(feature = "serde")]
529impl<'de> serde::Deserialize<'de> for Library {
530    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
531    where
532        D: serde::Deserializer<'de>,
533    {
534        use serde::de::Visitor;
535
536        #[derive(Deserialize)]
537        #[serde(field_identifier, rename_all = "snake_case")]
538        enum Field {
539            MastForest,
540            Exports,
541        }
542
543        struct LibraryVisitor;
544
545        impl<'de> Visitor<'de> for LibraryVisitor {
546            type Value = Library;
547
548            fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
549                formatter.write_str("struct Library")
550            }
551
552            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
553            where
554                A: serde::de::SeqAccess<'de>,
555            {
556                let mast_forest = seq
557                    .next_element()?
558                    .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
559                let exports: Vec<LibraryExport> = seq
560                    .next_element()?
561                    .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
562                let exports =
563                    exports.into_iter().map(|export| (export.name.clone(), export)).collect();
564                Library::new(mast_forest, exports).map_err(serde::de::Error::custom)
565            }
566
567            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
568            where
569                A: serde::de::MapAccess<'de>,
570            {
571                let mut mast_forest = None;
572                let mut exports = None;
573                while let Some(key) = map.next_key()? {
574                    match key {
575                        Field::MastForest => {
576                            if mast_forest.is_some() {
577                                return Err(serde::de::Error::duplicate_field("mast_forest"));
578                            }
579                            mast_forest = Some(map.next_value()?);
580                        },
581                        Field::Exports => {
582                            if exports.is_some() {
583                                return Err(serde::de::Error::duplicate_field("exports"));
584                            }
585                            let items: Vec<LibraryExport> = map.next_value()?;
586                            exports = Some(
587                                items
588                                    .into_iter()
589                                    .map(|export| (export.name.clone(), export))
590                                    .collect(),
591                            );
592                        },
593                    }
594                }
595                let mast_forest =
596                    mast_forest.ok_or_else(|| serde::de::Error::missing_field("mast_forest"))?;
597                let exports = exports.ok_or_else(|| serde::de::Error::missing_field("exports"))?;
598                Library::new(mast_forest, exports).map_err(serde::de::Error::custom)
599            }
600        }
601
602        deserializer.deserialize_struct("Library", &["mast_forest", "exports"], LibraryVisitor)
603    }
604}
605
606/// NOTE: Serialization of libraries is likely to be deprecated in a future release
607impl Serializable for KernelLibrary {
608    fn write_into<W: ByteWriter>(&self, target: &mut W) {
609        let Self { kernel: _, kernel_info: _, library } = self;
610
611        library.write_into(target);
612    }
613}
614
615/// NOTE: Serialization of libraries is likely to be deprecated in a future release
616impl Deserializable for KernelLibrary {
617    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
618        let library = Library::read_from(source)?;
619
620        Self::try_from(library).map_err(|err| {
621            DeserializationError::InvalidValue(format!(
622                "Failed to deserialize kernel library: {err}"
623            ))
624        })
625    }
626}
627
628/// A wrapper type for [FunctionType] that provides serialization support via the winter-utils
629/// serializer.
630///
631/// This is a temporary implementation to allow type information to be serialized with libraries,
632/// but in a future release we'll either rely on the `serde` serialization for these types, or
633/// provide the serialization implementation in midenc-hir-type instead
634pub struct FunctionTypeSerializer<'a>(pub &'a FunctionType);
635
636impl Serializable for FunctionTypeSerializer<'_> {
637    fn write_into<W: ByteWriter>(&self, target: &mut W) {
638        target.write_u8(self.0.abi as u8);
639        target.write_usize(self.0.params().len());
640        target.write_many(self.0.params().iter().map(TypeSerializer));
641        target.write_usize(self.0.results().len());
642        target.write_many(self.0.results().iter().map(TypeSerializer));
643    }
644}
645
646/// A wrapper type for [FunctionType] that provides deserialization support via the winter-utils
647/// serializer.
648///
649/// This is a temporary implementation to allow type information to be serialized with libraries,
650/// but in a future release we'll either rely on the `serde` serialization for these types, or
651/// provide the serialization implementation in midenc-hir-type instead
652pub struct FunctionTypeDeserializer(pub FunctionType);
653
654impl Deserializable for FunctionTypeDeserializer {
655    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
656        use midenc_hir_type::CallConv;
657
658        let abi = match source.read_u8()? {
659            0 => CallConv::Fast,
660            1 => CallConv::SystemV,
661            2 => CallConv::Wasm,
662            3 => CallConv::CanonLift,
663            4 => CallConv::CanonLower,
664            5 => CallConv::Kernel,
665            invalid => {
666                return Err(DeserializationError::InvalidValue(format!(
667                    "invalid CallConv tag: {invalid}"
668                )));
669            },
670        };
671
672        let arity = source.read_usize()?;
673        let mut params = SmallVec::<[Type; 4]>::with_capacity(arity);
674        for _ in 0..arity {
675            let ty = TypeDeserializer::read_from(source)?.0;
676            params.push(ty);
677        }
678
679        let num_results = source.read_usize()?;
680        let mut results = SmallVec::<[Type; 1]>::with_capacity(num_results);
681        for _ in 0..num_results {
682            let ty = TypeDeserializer::read_from(source)?.0;
683            results.push(ty);
684        }
685
686        Ok(Self(FunctionType { abi, params, results }))
687    }
688}
689
690/// A wrapper type for [Type] that provides serialization support via the winter-utils serializer.
691///
692/// This is a temporary implementation to allow type information to be serialized with libraries,
693/// but in a future release we'll either rely on the `serde` serialization for these types, or
694/// provide the serialization implementation in midenc-hir-type instead
695pub struct TypeSerializer<'a>(pub &'a Type);
696
697impl Serializable for TypeSerializer<'_> {
698    fn write_into<W: ByteWriter>(&self, target: &mut W) {
699        use midenc_hir_type::{AddressSpace, TypeRepr};
700
701        match self.0 {
702            Type::Unknown => target.write_u8(0),
703            Type::Never => target.write_u8(1),
704            Type::I1 => target.write_u8(2),
705            Type::I8 => target.write_u8(3),
706            Type::U8 => target.write_u8(4),
707            Type::I16 => target.write_u8(5),
708            Type::U16 => target.write_u8(6),
709            Type::I32 => target.write_u8(7),
710            Type::U32 => target.write_u8(8),
711            Type::I64 => target.write_u8(9),
712            Type::U64 => target.write_u8(10),
713            Type::I128 => target.write_u8(11),
714            Type::U128 => target.write_u8(12),
715            Type::U256 => target.write_u8(13),
716            Type::F64 => target.write_u8(14),
717            Type::Felt => target.write_u8(15),
718            Type::Ptr(ty) => {
719                target.write_u8(16);
720                match ty.addrspace {
721                    AddressSpace::Byte => target.write_u8(0),
722                    AddressSpace::Element => target.write_u8(1),
723                }
724                TypeSerializer(&ty.pointee).write_into(target);
725            },
726            Type::Struct(ty) => {
727                target.write_u8(17);
728                match ty.repr() {
729                    TypeRepr::Default => target.write_u8(0),
730                    TypeRepr::Align(align) => {
731                        target.write_u8(1);
732                        target.write_u16(align.get());
733                    },
734                    TypeRepr::Packed(align) => {
735                        target.write_u8(2);
736                        target.write_u16(align.get());
737                    },
738                    TypeRepr::Transparent => target.write_u8(3),
739                    TypeRepr::BigEndian => target.write_u8(4),
740                }
741                target.write_u8(ty.len() as u8);
742                for field in ty.fields() {
743                    TypeSerializer(&field.ty).write_into(target);
744                }
745            },
746            Type::Array(ty) => {
747                target.write_u8(18);
748                target.write_usize(ty.len);
749                TypeSerializer(&ty.ty).write_into(target);
750            },
751            Type::List(ty) => {
752                target.write_u8(19);
753                TypeSerializer(ty).write_into(target);
754            },
755            Type::Function(ty) => {
756                target.write_u8(20);
757                FunctionTypeSerializer(ty).write_into(target);
758            },
759        }
760    }
761}
762
763/// A wrapper type for [Type] that provides deserialization support via the winter-utils serializer.
764///
765/// This is a temporary implementation to allow type information to be serialized with libraries,
766/// but in a future release we'll either rely on the `serde` serialization for these types, or
767/// provide the serialization implementation in midenc-hir-type instead
768pub struct TypeDeserializer(pub Type);
769
770impl Deserializable for TypeDeserializer {
771    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
772        use alloc::string::ToString;
773        use core::num::NonZeroU16;
774
775        use midenc_hir_type::{AddressSpace, ArrayType, PointerType, StructType, TypeRepr};
776
777        let ty = match source.read_u8()? {
778            0 => Type::Unknown,
779            1 => Type::Never,
780            2 => Type::I1,
781            3 => Type::I8,
782            4 => Type::U8,
783            5 => Type::I16,
784            6 => Type::U16,
785            7 => Type::I32,
786            8 => Type::U32,
787            9 => Type::I64,
788            10 => Type::U64,
789            11 => Type::I128,
790            12 => Type::U128,
791            13 => Type::U256,
792            14 => Type::F64,
793            15 => Type::Felt,
794            16 => {
795                let addrspace = match source.read_u8()? {
796                    0 => AddressSpace::Byte,
797                    1 => AddressSpace::Element,
798                    invalid => {
799                        return Err(DeserializationError::InvalidValue(format!(
800                            "invalid AddressSpace tag: {invalid}"
801                        )));
802                    },
803                };
804                let pointee = TypeDeserializer::read_from(source)?.0;
805                Type::Ptr(Arc::new(PointerType { addrspace, pointee }))
806            },
807            17 => {
808                let repr = match source.read_u8()? {
809                    0 => TypeRepr::Default,
810                    1 => {
811                        let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
812                            DeserializationError::InvalidValue(
813                                "invalid type repr: alignment must be a non-zero value".to_string(),
814                            )
815                        })?;
816                        TypeRepr::Align(align)
817                    },
818                    2 => {
819                        let align = NonZeroU16::new(source.read_u16()?).ok_or_else(|| {
820                            DeserializationError::InvalidValue(
821                                "invalid type repr: packed alignment must be a non-zero value"
822                                    .to_string(),
823                            )
824                        })?;
825                        TypeRepr::Packed(align)
826                    },
827                    3 => TypeRepr::Transparent,
828                    invalid => {
829                        return Err(DeserializationError::InvalidValue(format!(
830                            "invalid TypeRepr tag: {invalid}"
831                        )));
832                    },
833                };
834                let num_fields = source.read_u8()?;
835                let mut fields = SmallVec::<[Type; 4]>::with_capacity(num_fields as usize);
836                for _ in 0..num_fields {
837                    let ty = TypeDeserializer::read_from(source)?.0;
838                    fields.push(ty);
839                }
840                Type::Struct(Arc::new(StructType::new_with_repr(repr, fields)))
841            },
842            18 => {
843                let arity = source.read_usize()?;
844                let ty = TypeDeserializer::read_from(source)?.0;
845                Type::Array(Arc::new(ArrayType { ty, len: arity }))
846            },
847            19 => {
848                let ty = TypeDeserializer::read_from(source)?.0;
849                Type::List(Arc::new(ty))
850            },
851            20 => Type::Function(Arc::new(FunctionTypeDeserializer::read_from(source)?.0)),
852            invalid => {
853                return Err(DeserializationError::InvalidValue(format!(
854                    "invalid Type tag: {invalid}"
855                )));
856            },
857        };
858        Ok(Self(ty))
859    }
860}
861
862#[cfg(any(test, feature = "arbitrary"))]
863impl proptest::prelude::Arbitrary for Library {
864    type Parameters = ();
865
866    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
867        use proptest::prelude::*;
868        prop::collection::btree_map(any::<QualifiedProcedureName>(), any::<LibraryExport>(), 1..5)
869            .prop_flat_map(|mut exports| {
870                // Create a MastForest with actual nodes for the exports
871                let mut mast_forest = MastForest::new();
872                let mut nodes = Vec::new();
873
874                // Create a simple node for each export
875                for (export_name, export) in exports.iter_mut() {
876                    use miden_core::Operation;
877
878                    // Ensure the export name matches the exported procedure, as the Arbitrary
879                    // impl will generate different paths for each
880                    export.name = export_name.clone();
881
882                    let node_id = mast_forest
883                        .add_block(vec![Operation::Add, Operation::Mul], Vec::new())
884                        .unwrap();
885                    nodes.push((export.node, node_id));
886                }
887
888                (Just(mast_forest), Just(nodes), Just(exports))
889            })
890            .prop_map(|(mut mast_forest, nodes, exports)| {
891                // Replace the export node IDs with the actual node IDs we created
892                let mut adjusted_exports = BTreeMap::new();
893
894                for (proc_name, mut export) in exports {
895                    // Find the corresponding node we created
896                    if let Some(&(_, actual_node_id)) =
897                        nodes.iter().find(|(original_id, _)| *original_id == export.node)
898                    {
899                        export.node = actual_node_id;
900                    } else {
901                        // If we can't find the node (shouldn't happen), use the first node we
902                        // created
903                        if let Some(&(_, first_node_id)) = nodes.first() {
904                            export.node = first_node_id;
905                        } else {
906                            // This should never happen since we create nodes for each export
907                            panic!("No nodes created for exports");
908                        }
909                    }
910
911                    adjusted_exports.insert(proc_name, export);
912                }
913
914                let mut node_ids = Vec::with_capacity(adjusted_exports.len());
915                for export in adjusted_exports.values() {
916                    // Add the node to the forest roots if it's not already there
917                    mast_forest.make_root(export.node);
918                    // Collect the node id for recomputing the digest
919                    node_ids.push(export.node);
920                }
921
922                // Recompute the digest
923                let digest = mast_forest.compute_nodes_commitment(&node_ids);
924
925                let mast_forest = Arc::new(mast_forest);
926                Library {
927                    digest,
928                    exports: adjusted_exports,
929                    mast_forest,
930                }
931            })
932            .boxed()
933    }
934
935    type Strategy = proptest::prelude::BoxedStrategy<Self>;
936}