miden_assembly_syntax/ast/procedure/
alias.rs

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