miden_assembly/ast/procedure/
alias.rs

1use alloc::string::String;
2use core::fmt;
3
4use super::{ProcedureName, QualifiedProcedureName};
5use crate::{
6    RpoDigest,
7    ast::{DocString, InvocationTarget},
8    diagnostics::{SourceSpan, Span, Spanned},
9};
10
11// PROCEDURE ALIAS
12// ================================================================================================
13
14/// Represents a procedure that acts like it is locally-defined, but delegates to an externally-
15/// defined procedure.
16///
17/// These procedure "aliases" do not have a concrete representation in the module, but are instead
18/// resolved during compilation to refer directly to the aliased procedure, regardless of whether
19/// the caller is in the current module, or in another module.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct ProcedureAlias {
22    /// The documentation attached to this procedure
23    docs: Option<DocString>,
24    /// The name of this procedure
25    name: ProcedureName,
26    /// The underlying procedure being aliased.
27    ///
28    /// Alias targets are context-sensitive, depending on how they were defined and what stage of
29    /// compilation we're in. See [AliasTarget] for semantics of each target type, but they closely
30    /// correspond to [InvocationTarget].
31    target: AliasTarget,
32}
33
34impl ProcedureAlias {
35    /// Creates a new procedure alias called `name`, which resolves to `target`.
36    pub fn new(name: ProcedureName, target: AliasTarget) -> Self {
37        Self { docs: None, name, target }
38    }
39
40    /// Adds documentation to this procedure alias.
41    pub fn with_docs(mut self, docs: Option<Span<String>>) -> Self {
42        self.docs = docs.map(DocString::new);
43        self
44    }
45
46    /// Returns the documentation associated with this declaration.
47    pub fn docs(&self) -> Option<Span<&str>> {
48        self.docs.as_ref().map(|docstring| docstring.as_spanned_str())
49    }
50
51    /// Returns the name of this alias within its containing module.
52    ///
53    /// If the procedure is simply re-exported with the same name, this will be equivalent to
54    /// `self.target().name`
55    #[inline]
56    pub fn name(&self) -> &ProcedureName {
57        &self.name
58    }
59
60    /// Returns the target of this procedure alias
61    #[inline]
62    pub fn target(&self) -> &AliasTarget {
63        &self.target
64    }
65
66    /// Returns a mutable reference to the target of this procedure alias
67    #[inline]
68    pub fn target_mut(&mut self) -> &mut AliasTarget {
69        &mut self.target
70    }
71
72    /// Returns true if this procedure uses an absolute target path
73    #[inline]
74    pub fn is_absolute(&self) -> bool {
75        matches!(self.target, AliasTarget::MastRoot(_) | AliasTarget::AbsoluteProcedurePath(_))
76    }
77
78    /// Returns true if this alias uses a different name than the target procedure
79    #[inline]
80    pub fn is_renamed(&self) -> bool {
81        match self.target() {
82            AliasTarget::MastRoot(_) => true,
83            AliasTarget::ProcedurePath(fqn) | AliasTarget::AbsoluteProcedurePath(fqn) => {
84                fqn.name != self.name
85            },
86        }
87    }
88}
89
90impl Spanned for ProcedureAlias {
91    fn span(&self) -> SourceSpan {
92        self.target.span()
93    }
94}
95
96impl crate::prettier::PrettyPrint for ProcedureAlias {
97    fn render(&self) -> crate::prettier::Document {
98        use crate::prettier::*;
99
100        let mut doc = self
101            .docs
102            .as_ref()
103            .map(|docstring| docstring.render())
104            .unwrap_or(Document::Empty);
105
106        doc += const_text("export.");
107        doc += match &self.target {
108            target @ AliasTarget::MastRoot(_) => display(format_args!("{}->{}", target, self.name)),
109            target => {
110                let prefix = if self.is_absolute() { "::" } else { "" };
111                if self.is_renamed() {
112                    display(format_args!("{}{}->{}", prefix, target, &self.name))
113                } else {
114                    display(format_args!("{prefix}{target}"))
115                }
116            },
117        };
118        doc
119    }
120}
121
122/// A fully-qualified external procedure that is the target of a procedure alias
123#[derive(Debug, Clone, PartialEq, Eq)]
124pub enum AliasTarget {
125    /// An alias of the procedure whose root is the given digest
126    ///
127    /// Corresponds to [`InvocationTarget::MastRoot`]
128    MastRoot(Span<RpoDigest>),
129    /// An alias of `name`, imported from `module`
130    ///
131    /// Corresponds to [`InvocationTarget::ProcedurePath`]
132    ProcedurePath(QualifiedProcedureName),
133    /// An alias of a procedure with the given absolute, fully-qualified path
134    ///
135    /// Corresponds to [InvocationTarget::AbsoluteProcedurePath]
136    AbsoluteProcedurePath(QualifiedProcedureName),
137}
138
139impl Spanned for AliasTarget {
140    fn span(&self) -> SourceSpan {
141        match self {
142            Self::MastRoot(spanned) => spanned.span(),
143            Self::ProcedurePath(spanned) | Self::AbsoluteProcedurePath(spanned) => spanned.span(),
144        }
145    }
146}
147
148impl From<Span<RpoDigest>> for AliasTarget {
149    fn from(digest: Span<RpoDigest>) -> Self {
150        Self::MastRoot(digest)
151    }
152}
153
154impl TryFrom<InvocationTarget> for AliasTarget {
155    type Error = InvocationTarget;
156
157    fn try_from(target: InvocationTarget) -> Result<Self, Self::Error> {
158        let span = target.span();
159        match target {
160            InvocationTarget::MastRoot(digest) => Ok(Self::MastRoot(digest)),
161            InvocationTarget::ProcedurePath { name, module } => {
162                let ns = crate::LibraryNamespace::from_ident_unchecked(module);
163                let module = crate::LibraryPath::new_from_components(ns, []);
164                Ok(Self::ProcedurePath(QualifiedProcedureName { span, module, name }))
165            },
166            InvocationTarget::AbsoluteProcedurePath { name, path: module } => {
167                Ok(Self::AbsoluteProcedurePath(QualifiedProcedureName { span, module, name }))
168            },
169            target @ InvocationTarget::ProcedureName(_) => Err(target),
170        }
171    }
172}
173
174impl From<&AliasTarget> for InvocationTarget {
175    fn from(target: &AliasTarget) -> Self {
176        match target {
177            AliasTarget::MastRoot(digest) => Self::MastRoot(*digest),
178            AliasTarget::ProcedurePath(fqn) => {
179                let name = fqn.name.clone();
180                let module = fqn.module.last_component().to_ident();
181                Self::ProcedurePath { name, module }
182            },
183            AliasTarget::AbsoluteProcedurePath(fqn) => Self::AbsoluteProcedurePath {
184                name: fqn.name.clone(),
185                path: fqn.module.clone(),
186            },
187        }
188    }
189}
190impl From<AliasTarget> for InvocationTarget {
191    fn from(target: AliasTarget) -> Self {
192        match target {
193            AliasTarget::MastRoot(digest) => Self::MastRoot(digest),
194            AliasTarget::ProcedurePath(fqn) => {
195                let name = fqn.name;
196                let module = fqn.module.last_component().to_ident();
197                Self::ProcedurePath { name, module }
198            },
199            AliasTarget::AbsoluteProcedurePath(fqn) => {
200                Self::AbsoluteProcedurePath { name: fqn.name, path: fqn.module }
201            },
202        }
203    }
204}
205
206impl crate::prettier::PrettyPrint for AliasTarget {
207    fn render(&self) -> crate::prettier::Document {
208        use vm_core::utils::DisplayHex;
209
210        use crate::prettier::*;
211
212        match self {
213            Self::MastRoot(digest) => display(DisplayHex(digest.as_bytes().as_slice())),
214            Self::ProcedurePath(fqn) => display(fqn),
215            Self::AbsoluteProcedurePath(fqn) => display(format_args!("::{fqn}")),
216        }
217    }
218}
219
220impl fmt::Display for AliasTarget {
221    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222        use crate::prettier::PrettyPrint;
223
224        self.pretty_print(f)
225    }
226}