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