miden_assembly/linker/resolver/
mod.rs1mod symbol_resolver;
2
3use alloc::{collections::BTreeMap, string::ToString, sync::Arc};
4
5use miden_assembly_syntax::{
6 Report,
7 ast::{
8 self, GlobalItemIndex, Ident, ItemIndex, ModuleIndex, Path, SymbolResolution,
9 SymbolResolutionError,
10 constants::{ConstEnvironment, ConstEvalError, eval::CachedConstantValue},
11 types,
12 },
13 debuginfo::{SourceFile, SourceManager, SourceSpan, Span, Spanned},
14 diagnostics::{LabeledSpan, RelatedError, Severity, diagnostic},
15 module::ItemInfo,
16};
17use smallvec::SmallVec;
18
19pub use self::symbol_resolver::{SymbolResolutionContext, SymbolResolver};
20use super::SymbolItem;
21use crate::LinkerError;
22
23pub struct Resolver<'a, 'b: 'a> {
28 pub resolver: &'a SymbolResolver<'b>,
29 pub cache: &'a mut ResolverCache,
30 pub current_module: ModuleIndex,
31}
32
33#[derive(Default)]
38pub struct ResolverCache {
39 pub types: BTreeMap<GlobalItemIndex, types::Type>,
40 pub constants: BTreeMap<GlobalItemIndex, ast::ConstantValue>,
41 pub evaluating_constants: BTreeMap<GlobalItemIndex, SourceSpan>,
42}
43
44impl<'a, 'b: 'a> Resolver<'a, 'b> {
45 fn invalid_constant_ref(&self, span: SourceSpan) -> LinkerError {
46 LinkerError::InvalidConstantRef {
47 span,
48 source_file: self.get_source_file_for(span),
49 }
50 }
51
52 pub(super) fn materialize_constant_by_gid(
53 &mut self,
54 gid: GlobalItemIndex,
55 span: SourceSpan,
56 ) -> Result<(), LinkerError> {
57 if self.cache.constants.contains_key(&gid) {
58 return Ok(());
59 }
60
61 match self.resolver.linker()[gid].item() {
62 SymbolItem::Compiled(ItemInfo::Constant(_)) => return Ok(()),
63 SymbolItem::Constant(item) => {
64 let expr = item.value.clone();
65 let eval_span = item.value.span();
66 if let Some(start) = self.cache.evaluating_constants.get(&gid).copied() {
67 return Err(ConstEvalError::eval_cycle(start, span, self).into());
68 }
69
70 self.cache.evaluating_constants.insert(gid, eval_span);
71 let value = self.resolver.linker().const_eval(gid, &expr, self.cache);
72 self.cache.evaluating_constants.remove(&gid);
73
74 let value = value?;
75 self.cache.constants.insert(gid, value);
76 return Ok(());
77 },
78 SymbolItem::Compiled(_) | SymbolItem::Procedure(_) | SymbolItem::Type(_) => (),
79 }
80
81 Err(self.invalid_constant_ref(span))
82 }
83
84 fn get_constant_by_gid(
85 &mut self,
86 gid: GlobalItemIndex,
87 span: SourceSpan,
88 ) -> Result<Option<CachedConstantValue<'_>>, LinkerError> {
89 self.materialize_constant_by_gid(gid, span)?;
90
91 if let Some(cached) = self.cache.constants.get(&gid) {
92 return Ok(Some(CachedConstantValue::Hit(cached)));
93 }
94
95 match self.resolver.linker()[gid].item() {
96 SymbolItem::Compiled(ItemInfo::Constant(info)) => {
97 Ok(Some(CachedConstantValue::Hit(&info.value)))
98 },
99 SymbolItem::Compiled(_)
100 | SymbolItem::Constant(_)
101 | SymbolItem::Procedure(_)
102 | SymbolItem::Type(_) => Err(self.invalid_constant_ref(span)),
103 }
104 }
105}
106
107impl<'a, 'b: 'a> ConstEnvironment for Resolver<'a, 'b> {
108 type Error = LinkerError;
109
110 fn get_source_file_for(&self, span: SourceSpan) -> Option<Arc<SourceFile>> {
111 self.resolver.source_manager().get(span.source_id()).ok()
112 }
113
114 fn get(&mut self, name: &Ident) -> Result<Option<CachedConstantValue<'_>>, Self::Error> {
115 let context = SymbolResolutionContext {
116 span: name.span(),
117 module: self.current_module,
118 kind: None,
119 };
120 let path = Path::from_ident(name);
121 let gid = self
122 .resolver
123 .resolve_constant_path(&context, Span::new(name.span(), path.as_ref()))?;
124
125 self.get_constant_by_gid(gid, name.span())
126 }
127
128 fn get_by_path(
129 &mut self,
130 path: Span<&Path>,
131 ) -> Result<Option<CachedConstantValue<'_>>, Self::Error> {
132 let context = SymbolResolutionContext {
133 span: path.span(),
134 module: self.current_module,
135 kind: None,
136 };
137 let gid = self.resolver.resolve_constant_path(&context, path)?;
138
139 self.get_constant_by_gid(gid, path.span())
140 }
141
142 fn on_eval_completed(&mut self, path: Span<&Path>, value: &ast::ConstantExpr) {
145 let Some(value) = value.as_value() else {
146 return;
147 };
148 let context = SymbolResolutionContext {
149 span: path.span(),
150 module: self.current_module,
151 kind: None,
152 };
153 let gid = match self.resolver.resolve_path(&context, path) {
154 Ok(SymbolResolution::Exact { gid, .. }) => gid,
155 _ => return,
156 };
157 self.cache.constants.insert(gid, value);
158 }
159}
160
161impl<'a, 'b: 'a> ast::TypeResolver<LinkerError> for Resolver<'a, 'b> {
162 #[inline]
163 fn source_manager(&self) -> Arc<dyn SourceManager> {
164 self.resolver.source_manager_arc()
165 }
166 #[inline]
167 fn resolve_local_failed(&self, err: SymbolResolutionError) -> LinkerError {
168 LinkerError::from(err)
169 }
170
171 fn get_type(
172 &mut self,
173 context: SourceSpan,
174 gid: GlobalItemIndex,
175 ) -> Result<types::Type, LinkerError> {
176 match self.resolver.linker()[gid].item() {
177 SymbolItem::Compiled(ItemInfo::Type(info)) => Ok(info.ty.clone()),
178 SymbolItem::Type(ast::TypeDecl::Enum(ty)) => {
179 let mut variants = SmallVec::<[types::Variant; 4]>::new_const();
186 for variant in ty.variants() {
187 let discriminant_value = match self.resolver.linker().const_eval(
188 gid,
189 &variant.discriminant,
190 self.cache,
191 )? {
192 ast::ConstantValue::Int(v) => Some(v.as_canonical_u64() as u128),
193 invalid => {
194 return Err(LinkerError::Related {
195 errors: vec![RelatedError::new(Report::from(diagnostic!(
196 severity = Severity::Error,
197 labels = vec![LabeledSpan::at(
198 invalid.span(),
199 "invalid enum discriminant: expected an integer"
200 )],
201 "invalid enum type"
202 )))]
203 .into_boxed_slice(),
204 });
205 },
206 };
207 variants.push(types::Variant {
208 name: variant.name.clone().into_inner(),
209 value: match variant.value_ty.as_ref() {
210 Some(t) => t.resolve_type(self)?,
211 None => None,
212 },
213 discriminant_value,
214 });
215 }
216 types::EnumType::new(ty.name().clone().into_inner(), ty.ty().clone(), variants)
217 .map(|t| types::Type::Enum(Arc::new(t)))
218 .map_err(|err| LinkerError::Related {
219 errors: vec![RelatedError::from(Report::from(diagnostic!(
220 severity = Severity::Error,
221 labels = vec![LabeledSpan::at(context, err.to_string())],
222 "invalid enum type"
223 )))]
224 .into_boxed_slice(),
225 })
226 },
227 SymbolItem::Type(ast::TypeDecl::Alias(ty)) => {
228 Ok(ty.ty.resolve_type(self)?.expect("unreachable"))
229 },
230 SymbolItem::Compiled(_) | SymbolItem::Constant(_) | SymbolItem::Procedure(_) => {
231 Err(LinkerError::InvalidTypeRef {
232 span: context,
233 source_file: self.get_source_file_for(context),
234 })
235 },
236 }
237 }
238
239 fn get_local_type(
240 &mut self,
241 context: SourceSpan,
242 id: ItemIndex,
243 ) -> Result<Option<types::Type>, LinkerError> {
244 self.get_type(context, self.current_module + id).map(Some)
245 }
246
247 fn resolve_type_ref(&mut self, ty: Span<&Path>) -> Result<SymbolResolution, LinkerError> {
248 let context = SymbolResolutionContext {
249 span: ty.span(),
250 module: self.current_module,
251 kind: None,
252 };
253 let gid = self.resolver.resolve_type_path(&context, ty)?;
254 Ok(SymbolResolution::Exact {
255 gid,
256 path: Span::new(ty.span(), self.resolver.item_path(gid)),
257 })
258 }
259}