miden_assembly/linker/resolver/
mod.rs1mod 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
20pub 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#[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 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}