miden_assembly/ast/procedure/
alias.rs

1use alloc::string::String;
2use core::fmt;
3
4use super::{ProcedureName, QualifiedProcedureName};
5use crate::{
6    RpoDigest,
7    ast::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<Span<String>>,
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;
43        self
44    }
45
46    /// Returns the documentation associated with this declaration.
47    pub fn docs(&self) -> Option<&Span<String>> {
48        self.docs.as_ref()
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 = Document::Empty;
101        if let Some(docs) = self.docs.as_deref() {
102            doc = docs
103                .lines()
104                .map(text)
105                .reduce(|acc, line| acc + nl() + text("#! ") + line)
106                .unwrap_or_default();
107        }
108
109        doc += const_text("export.");
110        doc += match &self.target {
111            target @ AliasTarget::MastRoot(_) => display(format_args!("{}->{}", target, self.name)),
112            target => {
113                let prefix = if self.is_absolute() { "::" } else { "" };
114                if self.is_renamed() {
115                    display(format_args!("{}{}->{}", prefix, target, &self.name))
116                } else {
117                    display(format_args!("{}{}", prefix, target))
118                }
119            },
120        };
121        doc
122    }
123}
124
125/// A fully-qualified external procedure that is the target of a procedure alias
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub enum AliasTarget {
128    /// An alias of the procedure whose root is the given digest
129    ///
130    /// Corresponds to [`InvocationTarget::MastRoot`]
131    MastRoot(Span<RpoDigest>),
132    /// An alias of `name`, imported from `module`
133    ///
134    /// Corresponds to [`InvocationTarget::ProcedurePath`]
135    ProcedurePath(QualifiedProcedureName),
136    /// An alias of a procedure with the given absolute, fully-qualified path
137    ///
138    /// Corresponds to [InvocationTarget::AbsoluteProcedurePath]
139    AbsoluteProcedurePath(QualifiedProcedureName),
140}
141
142impl Spanned for AliasTarget {
143    fn span(&self) -> SourceSpan {
144        match self {
145            Self::MastRoot(spanned) => spanned.span(),
146            Self::ProcedurePath(spanned) | Self::AbsoluteProcedurePath(spanned) => spanned.span(),
147        }
148    }
149}
150
151impl From<Span<RpoDigest>> for AliasTarget {
152    fn from(digest: Span<RpoDigest>) -> Self {
153        Self::MastRoot(digest)
154    }
155}
156
157impl TryFrom<InvocationTarget> for AliasTarget {
158    type Error = InvocationTarget;
159
160    fn try_from(target: InvocationTarget) -> Result<Self, Self::Error> {
161        let span = target.span();
162        match target {
163            InvocationTarget::MastRoot(digest) => Ok(Self::MastRoot(digest)),
164            InvocationTarget::ProcedurePath { name, module } => {
165                let ns = crate::LibraryNamespace::from_ident_unchecked(module);
166                let module = crate::LibraryPath::new_from_components(ns, []);
167                Ok(Self::ProcedurePath(QualifiedProcedureName { span, module, name }))
168            },
169            InvocationTarget::AbsoluteProcedurePath { name, path: module } => {
170                Ok(Self::AbsoluteProcedurePath(QualifiedProcedureName { span, module, name }))
171            },
172            target @ InvocationTarget::ProcedureName(_) => Err(target),
173        }
174    }
175}
176
177impl From<&AliasTarget> for InvocationTarget {
178    fn from(target: &AliasTarget) -> Self {
179        match target {
180            AliasTarget::MastRoot(digest) => Self::MastRoot(*digest),
181            AliasTarget::ProcedurePath(fqn) => {
182                let name = fqn.name.clone();
183                let module = fqn.module.last_component().to_ident();
184                Self::ProcedurePath { name, module }
185            },
186            AliasTarget::AbsoluteProcedurePath(fqn) => Self::AbsoluteProcedurePath {
187                name: fqn.name.clone(),
188                path: fqn.module.clone(),
189            },
190        }
191    }
192}
193impl From<AliasTarget> for InvocationTarget {
194    fn from(target: AliasTarget) -> Self {
195        match target {
196            AliasTarget::MastRoot(digest) => Self::MastRoot(digest),
197            AliasTarget::ProcedurePath(fqn) => {
198                let name = fqn.name;
199                let module = fqn.module.last_component().to_ident();
200                Self::ProcedurePath { name, module }
201            },
202            AliasTarget::AbsoluteProcedurePath(fqn) => {
203                Self::AbsoluteProcedurePath { name: fqn.name, path: fqn.module }
204            },
205        }
206    }
207}
208
209impl crate::prettier::PrettyPrint for AliasTarget {
210    fn render(&self) -> crate::prettier::Document {
211        use vm_core::utils::DisplayHex;
212
213        use crate::prettier::*;
214
215        match self {
216            Self::MastRoot(digest) => display(DisplayHex(digest.as_bytes().as_slice())),
217            Self::ProcedurePath(fqn) => display(fqn),
218            Self::AbsoluteProcedurePath(fqn) => display(format_args!("::{}", fqn)),
219        }
220    }
221}
222
223impl fmt::Display for AliasTarget {
224    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225        use crate::prettier::PrettyPrint;
226
227        self.pretty_print(f)
228    }
229}