miden_assembly/linker/resolver/
mod.rs

1mod symbol_resolver;
2
3use alloc::{collections::BTreeMap, sync::Arc};
4
5use miden_assembly_syntax::{
6    ast::{
7        self, GlobalItemIndex, Ident, ItemIndex, ModuleIndex, Path, SymbolResolution,
8        SymbolResolutionError,
9        constants::{ConstEnvironment, eval::CachedConstantValue},
10        types,
11    },
12    debuginfo::{SourceFile, SourceManager, SourceSpan, Span, Spanned},
13    library::ItemInfo,
14};
15
16pub use self::symbol_resolver::{SymbolResolutionContext, SymbolResolver};
17use super::SymbolItem;
18use crate::LinkerError;
19
20/// A [Resolver] is used to perform symbol resolution in the context of a specific module.
21///
22/// It is instantiated along with a [ResolverCache] to cache frequently-referenced symbols, and a
23/// [SymbolResolver] for resolving externally-defined symbols.
24pub struct Resolver<'a, 'b: 'a> {
25    pub resolver: &'a SymbolResolver<'b>,
26    pub cache: &'a mut ResolverCache,
27    pub current_module: ModuleIndex,
28}
29
30/// A [ResolverCache] is used to cache resolutions of type and constant expressions to concrete
31/// values that contain no references to other symbols. Since these resolutions can be expensive
32/// to compute, and often represent items which are referenced multiple times, we cache them to
33/// avoid recomputing the same information over and over again.
34#[derive(Default)]
35pub struct ResolverCache {
36    pub types: BTreeMap<GlobalItemIndex, ast::types::Type>,
37    pub constants: BTreeMap<GlobalItemIndex, ast::ConstantValue>,
38}
39
40impl<'a, 'b: 'a> ConstEnvironment for Resolver<'a, 'b> {
41    type Error = LinkerError;
42
43    fn get_source_file_for(&self, span: SourceSpan) -> Option<Arc<SourceFile>> {
44        self.resolver.source_manager().get(span.source_id()).ok()
45    }
46
47    fn get(&self, name: &Ident) -> Result<Option<CachedConstantValue<'_>>, Self::Error> {
48        let context = SymbolResolutionContext {
49            span: name.span(),
50            module: self.current_module,
51            kind: None,
52        };
53        let gid = match self.resolver.resolve_local(&context, name)? {
54            SymbolResolution::Exact { gid, .. } => gid,
55            SymbolResolution::Local(index) => self.current_module + index.into_inner(),
56            SymbolResolution::MastRoot(_) | SymbolResolution::Module { .. } => {
57                return Err(LinkerError::InvalidConstantRef {
58                    span: context.span,
59                    source_file: self.get_source_file_for(context.span),
60                });
61            },
62            SymbolResolution::External(path) => {
63                return Err(LinkerError::UndefinedSymbol {
64                    span: context.span,
65                    source_file: self.get_source_file_for(context.span),
66                    path: path.into_inner(),
67                });
68            },
69        };
70
71        match self.cache.constants.get(&gid).map(CachedConstantValue::Hit) {
72            some @ Some(_) => Ok(some),
73            None => match self.resolver.linker()[gid].item() {
74                SymbolItem::Compiled(ItemInfo::Constant(info)) => {
75                    Ok(Some(CachedConstantValue::Hit(&info.value)))
76                },
77                SymbolItem::Constant(item) => Ok(Some(CachedConstantValue::Miss(&item.value))),
78                _ => Err(LinkerError::InvalidConstantRef {
79                    span: name.span(),
80                    source_file: self.get_source_file_for(name.span()),
81                }),
82            },
83        }
84    }
85
86    fn get_by_path(
87        &self,
88        path: Span<&Path>,
89    ) -> Result<Option<CachedConstantValue<'_>>, Self::Error> {
90        let context = SymbolResolutionContext {
91            span: path.span(),
92            module: self.current_module,
93            kind: None,
94        };
95        let gid = match self.resolver.resolve_path(&context, path)? {
96            SymbolResolution::Exact { gid, .. } => gid,
97            SymbolResolution::Local(index) => self.current_module + index.into_inner(),
98            SymbolResolution::MastRoot(_) | SymbolResolution::Module { .. } => {
99                return Err(LinkerError::InvalidConstantRef {
100                    span: context.span,
101                    source_file: self.get_source_file_for(context.span),
102                });
103            },
104            SymbolResolution::External(path) => {
105                return Err(LinkerError::UndefinedSymbol {
106                    span: context.span,
107                    source_file: self.get_source_file_for(context.span),
108                    path: path.into_inner(),
109                });
110            },
111        };
112        if let Some(cached) = self.cache.constants.get(&gid) {
113            return Ok(Some(CachedConstantValue::Hit(cached)));
114        }
115        match self.resolver.linker()[gid].item() {
116            SymbolItem::Compiled(ItemInfo::Constant(info)) => {
117                Ok(Some(CachedConstantValue::Hit(&info.value)))
118            },
119            SymbolItem::Constant(item) => Ok(Some(CachedConstantValue::Miss(&item.value))),
120            SymbolItem::Compiled(_) | SymbolItem::Procedure(_) | SymbolItem::Type(_) => {
121                Err(LinkerError::InvalidConstantRef {
122                    span: context.span,
123                    source_file: self.get_source_file_for(context.span),
124                })
125            },
126            SymbolItem::Alias { .. } => {
127                unreachable!("the resolver should have expanded all aliases")
128            },
129        }
130    }
131
132    /// Cache evaluated constants so long as they evaluated to a ConstantValue, and we can resolve
133    /// the path to a known GlobalItemIndex
134    fn on_eval_completed(&mut self, path: Span<&Path>, value: &ast::ConstantExpr) {
135        let Some(value) = value.as_value() else {
136            return;
137        };
138        let context = SymbolResolutionContext {
139            span: path.span(),
140            module: self.current_module,
141            kind: None,
142        };
143        let gid = match self.resolver.resolve_path(&context, path) {
144            Ok(SymbolResolution::Exact { gid, .. }) => gid,
145            Ok(SymbolResolution::Local(index)) => self.current_module + index.into_inner(),
146            _ => return,
147        };
148        self.cache.constants.insert(gid, value);
149    }
150}
151
152impl<'a, 'b: 'a> ast::TypeResolver<LinkerError> for Resolver<'a, 'b> {
153    #[inline]
154    fn source_manager(&self) -> Arc<dyn SourceManager> {
155        self.resolver.source_manager_arc()
156    }
157    #[inline]
158    fn resolve_local_failed(&self, err: SymbolResolutionError) -> LinkerError {
159        LinkerError::from(err)
160    }
161
162    fn get_type(
163        &self,
164        context: SourceSpan,
165        gid: GlobalItemIndex,
166    ) -> Result<types::Type, LinkerError> {
167        match self.resolver.linker()[gid].item() {
168            SymbolItem::Compiled(ItemInfo::Type(info)) => Ok(info.ty.clone()),
169            SymbolItem::Type(ast::TypeDecl::Enum(ty)) => Ok(ty.ty().clone()),
170            SymbolItem::Type(ast::TypeDecl::Alias(ty)) => {
171                Ok(ty.ty.resolve_type(self)?.expect("unreachable"))
172            },
173            SymbolItem::Compiled(_) | SymbolItem::Constant(_) | SymbolItem::Procedure(_) => {
174                Err(LinkerError::InvalidTypeRef {
175                    span: context,
176                    source_file: self.get_source_file_for(context),
177                })
178            },
179            SymbolItem::Alias { .. } => unreachable!("resolver should have expanded all aliases"),
180        }
181    }
182
183    fn get_local_type(
184        &self,
185        context: SourceSpan,
186        id: ItemIndex,
187    ) -> Result<Option<types::Type>, LinkerError> {
188        self.get_type(context, self.current_module + id).map(Some)
189    }
190
191    fn resolve_type_ref(&self, ty: Span<&Path>) -> Result<SymbolResolution, LinkerError> {
192        let context = SymbolResolutionContext {
193            span: ty.span(),
194            module: self.current_module,
195            kind: None,
196        };
197        match self.resolver.resolve_path(&context, ty)? {
198            exact @ SymbolResolution::Exact { .. } => Ok(exact),
199            SymbolResolution::Local(index) => {
200                let (span, index) = index.into_parts();
201                let current_module = &self.resolver.linker()[self.current_module];
202                let item = current_module[index].name();
203                let path = Span::new(span, current_module.path().join(item).into());
204                Ok(SymbolResolution::Exact { gid: self.current_module + index, path })
205            },
206            SymbolResolution::MastRoot(_) | SymbolResolution::Module { .. } => {
207                Err(LinkerError::InvalidTypeRef {
208                    span: ty.span(),
209                    source_file: self.get_source_file_for(ty.span()),
210                })
211            },
212            SymbolResolution::External(path) => Err(LinkerError::UndefinedSymbol {
213                span: ty.span(),
214                source_file: self.get_source_file_for(ty.span()),
215                path: path.into_inner(),
216            }),
217        }
218    }
219}