miden_assembly_syntax/sema/passes/
verify_invoke.rs1use alloc::{boxed::Box, collections::BTreeSet, sync::Arc};
2use core::ops::ControlFlow;
3
4use miden_debug_types::{SourceSpan, Span, Spanned};
5
6use crate::{
7 PathBuf,
8 ast::*,
9 sema::{AnalysisContext, SemanticAnalysisError},
10};
11
12pub struct VerifyInvokeTargets<'a> {
23 analyzer: &'a mut AnalysisContext,
24 module: &'a mut Module,
25 procedures: &'a BTreeSet<Ident>,
26 current_procedure: Option<ProcedureName>,
27 invoked: BTreeSet<Invoke>,
28}
29
30impl<'a> VerifyInvokeTargets<'a> {
31 pub fn new(
32 analyzer: &'a mut AnalysisContext,
33 module: &'a mut Module,
34 procedures: &'a BTreeSet<Ident>,
35 current_procedure: Option<ProcedureName>,
36 ) -> Self {
37 Self {
38 analyzer,
39 module,
40 procedures,
41 current_procedure,
42 invoked: Default::default(),
43 }
44 }
45}
46
47impl VerifyInvokeTargets<'_> {
48 fn resolve_local(&mut self, name: &Ident) -> ControlFlow<()> {
49 if !self.procedures.contains(name) {
50 self.analyzer.error(SemanticAnalysisError::SymbolResolutionError(Box::new(
51 SymbolResolutionError::undefined(name.span(), &self.analyzer.source_manager()),
52 )));
53 }
54 ControlFlow::Continue(())
55 }
56 fn resolve_external(&mut self, span: SourceSpan, path: &Path) -> Option<InvocationTarget> {
57 log::debug!(target: "verify-invoke", "resolving external symbol '{path}'");
58 let Some((module, rest)) = path.split_first() else {
59 self.analyzer.error(SemanticAnalysisError::InvalidInvokePath { span });
60 return None;
61 };
62 log::debug!(target: "verify-invoke", "attempting to resolve '{module}' to local import");
63 if let Some(import) = self.module.get_import_mut(module) {
64 log::debug!(target: "verify-invoke", "found import '{}'", import.target());
65 import.uses += 1;
66 match import.target() {
67 AliasTarget::MastRoot(_) => {
68 self.analyzer.error(SemanticAnalysisError::InvalidInvokeTargetViaImport {
69 span,
70 import: import.span(),
71 });
72 None
73 },
74 AliasTarget::Path(shadowed) if shadowed.as_deref() == path => {
79 Some(InvocationTarget::Path(
80 shadowed.as_deref().map(|p| p.to_absolute().join(rest).into()),
81 ))
82 },
83 AliasTarget::Path(path) => {
84 let path = path.clone();
85 let resolved = self.resolve_external(path.span(), path.inner())?;
86 match resolved {
87 InvocationTarget::MastRoot(digest) => {
88 self.analyzer.error(
89 SemanticAnalysisError::InvalidInvokeTargetViaImport {
90 span,
91 import: digest.span(),
92 },
93 );
94 None
95 },
96 InvocationTarget::Path(resolved) => Some(InvocationTarget::Path(
99 resolved.with_span(span).map(|p| p.to_absolute().join(rest).into()),
100 )),
101 InvocationTarget::Symbol(_) => {
102 panic!("unexpected local target resolution for alias")
103 },
104 }
105 },
106 }
107 } else {
108 Some(InvocationTarget::Path(Span::new(span, path.to_absolute().into_owned().into())))
110 }
111 }
112 fn track_used_alias(&mut self, name: &Ident) {
113 if let Some(alias) = self.module.aliases_mut().find(|a| a.name() == name) {
114 alias.uses += 1;
115 }
116 }
117}
118
119impl VisitMut for VerifyInvokeTargets<'_> {
120 fn visit_mut_alias(&mut self, alias: &mut Alias) -> ControlFlow<()> {
121 if alias.visibility().is_public() {
122 alias.uses += 1;
124 assert!(alias.is_used());
125 }
126 self.visit_mut_alias_target(alias.target_mut())
127 }
128 fn visit_mut_procedure(&mut self, procedure: &mut Procedure) -> ControlFlow<()> {
129 let result = visit::visit_mut_procedure(self, procedure);
130 procedure.extend_invoked(core::mem::take(&mut self.invoked));
131 result
132 }
133 fn visit_mut_syscall(&mut self, target: &mut InvocationTarget) -> ControlFlow<()> {
134 match target {
135 InvocationTarget::Symbol(name) => {
138 let span = name.span();
139 let path = Path::kernel_path().join(name).into();
140 *target = InvocationTarget::Path(Span::new(span, path));
141 },
142 InvocationTarget::Path(path) => {
144 let span = path.span();
145 if let Some(name) = path.as_ident() {
146 let new_path = Path::kernel_path().join(&name).into();
147 *path = Span::new(span, new_path);
148 } else {
149 self.analyzer.error(SemanticAnalysisError::InvalidSyscallTarget { span });
150 }
151 },
152 InvocationTarget::MastRoot(_) => (),
155 }
156 self.invoked.insert(Invoke::new(InvokeKind::SysCall, target.clone()));
157 ControlFlow::Continue(())
158 }
159 fn visit_mut_call(&mut self, target: &mut InvocationTarget) -> ControlFlow<()> {
160 self.visit_mut_invoke_target(target)?;
161 self.invoked.insert(Invoke::new(InvokeKind::Call, target.clone()));
162 ControlFlow::Continue(())
163 }
164 fn visit_mut_exec(&mut self, target: &mut InvocationTarget) -> ControlFlow<()> {
165 self.visit_mut_invoke_target(target)?;
166 self.invoked.insert(Invoke::new(InvokeKind::Exec, target.clone()));
167 ControlFlow::Continue(())
168 }
169 fn visit_mut_procref(&mut self, target: &mut InvocationTarget) -> ControlFlow<()> {
170 self.visit_mut_invoke_target(target)?;
171 self.invoked.insert(Invoke::new(InvokeKind::Exec, target.clone()));
172 ControlFlow::Continue(())
173 }
174 fn visit_mut_invoke_target(&mut self, target: &mut InvocationTarget) -> ControlFlow<()> {
175 let span = target.span();
176 let path = match &*target {
177 InvocationTarget::MastRoot(_) => return ControlFlow::Continue(()),
178 InvocationTarget::Path(path) => path.clone(),
179 InvocationTarget::Symbol(symbol) => {
180 Span::new(symbol.span(), PathBuf::from(symbol.clone()).into())
181 },
182 };
183 let current = self.current_procedure.as_ref().map(|p| p.as_ident());
184 if let Some(name) = path.as_ident() {
185 let name = name.with_span(span);
186 if current.is_some_and(|curr| curr == name) {
187 self.analyzer.error(SemanticAnalysisError::SelfRecursive { span });
188 } else {
189 return self.resolve_local(&name);
190 }
191 } else if path.parent().unwrap() == self.module.path()
192 && current.is_some_and(|curr| curr.as_str() == path.last().unwrap())
193 {
194 self.analyzer.error(SemanticAnalysisError::SelfRecursive { span });
195 } else if self.resolve_external(target.span(), &path).is_none() {
196 self.analyzer
197 .error(SemanticAnalysisError::MissingImport { span: target.span() });
198 }
199 ControlFlow::Continue(())
200 }
201 fn visit_mut_alias_target(&mut self, target: &mut AliasTarget) -> ControlFlow<()> {
202 match target {
203 AliasTarget::MastRoot(_) => ControlFlow::Continue(()),
204 AliasTarget::Path(path) => {
205 if path.is_absolute() {
206 return ControlFlow::Continue(());
207 }
208
209 let Some((ns, _)) = path.split_first() else {
210 return ControlFlow::Continue(());
211 };
212
213 if let Some(via) = self.module.get_import_mut(ns) {
214 via.uses += 1;
215 assert!(via.is_used());
216 }
217 ControlFlow::Continue(())
218 },
219 }
220 }
221 fn visit_mut_immediate_error_message(&mut self, code: &mut ErrorMsg) -> ControlFlow<()> {
222 if let Immediate::Constant(name) = code {
223 self.track_used_alias(name);
224 }
225 ControlFlow::Continue(())
226 }
227 fn visit_mut_immediate_felt(
228 &mut self,
229 imm: &mut Immediate<miden_core::Felt>,
230 ) -> ControlFlow<()> {
231 if let Immediate::Constant(name) = imm {
232 self.track_used_alias(name);
233 }
234 ControlFlow::Continue(())
235 }
236 fn visit_mut_immediate_u32(&mut self, imm: &mut Immediate<u32>) -> ControlFlow<()> {
237 if let Immediate::Constant(name) = imm {
238 self.track_used_alias(name);
239 }
240 ControlFlow::Continue(())
241 }
242 fn visit_mut_immediate_u16(&mut self, imm: &mut Immediate<u16>) -> ControlFlow<()> {
243 if let Immediate::Constant(name) = imm {
244 self.track_used_alias(name);
245 }
246 ControlFlow::Continue(())
247 }
248 fn visit_mut_immediate_u8(&mut self, imm: &mut Immediate<u8>) -> ControlFlow<()> {
249 if let Immediate::Constant(name) = imm {
250 self.track_used_alias(name);
251 }
252 ControlFlow::Continue(())
253 }
254 fn visit_mut_immediate_push_value(
255 &mut self,
256 imm: &mut Immediate<crate::parser::PushValue>,
257 ) -> ControlFlow<()> {
258 if let Immediate::Constant(name) = imm {
259 self.track_used_alias(name);
260 }
261 ControlFlow::Continue(())
262 }
263 fn visit_mut_immediate_word_value(
264 &mut self,
265 imm: &mut Immediate<crate::parser::WordValue>,
266 ) -> ControlFlow<()> {
267 if let Immediate::Constant(name) = imm {
268 self.track_used_alias(name);
269 }
270 ControlFlow::Continue(())
271 }
272 fn visit_mut_type_ref(&mut self, path: &mut Span<Arc<Path>>) -> ControlFlow<()> {
273 if let Some(name) = path.as_ident() {
274 self.track_used_alias(&name);
275 } else if let Some((module, _)) = path.split_first()
276 && let Some(alias) = self.module.aliases_mut().find(|a| a.name().as_str() == module)
277 {
278 alias.uses += 1;
279 }
280 ControlFlow::Continue(())
281 }
282 fn visit_mut_constant_ref(&mut self, path: &mut Span<Arc<Path>>) -> ControlFlow<()> {
283 if let Some(name) = path.as_ident() {
284 self.track_used_alias(&name);
285 } else if let Some((module, _)) = path.split_first()
286 && let Some(alias) = self.module.aliases_mut().find(|a| a.name().as_str() == module)
287 {
288 alias.uses += 1;
289 }
290 ControlFlow::Continue(())
291 }
292}