1use cairo_lang_defs::ids::MemberId;
5use cairo_lang_proc_macros::DebugWithDb;
6use cairo_lang_utils::extract_matches;
7use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
8use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
9
10use crate::expr::fmt::ExprFormatter;
11use crate::expr::objects::Arenas;
12use crate::{
13 ConcreteStructId, Condition, Expr, ExprClosure, ExprFor, ExprFunctionCall, ExprFunctionCallArg,
14 ExprId, ExprLoop, ExprVarMemberPath, ExprWhile, FixedSizeArrayItems, FunctionBody, Parameter,
15 Pattern, PatternArena, PatternId, Statement, StatementBreak, StatementExpr, StatementLet,
16 StatementReturn, VarId,
17};
18
19#[cfg(test)]
20mod test;
21
22#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb)]
25#[debug_db(ExprFormatter<'db>)]
26pub enum MemberPath<'db> {
27 Var(VarId<'db>),
28 Member {
29 parent: Box<MemberPath<'db>>,
30 member_id: MemberId<'db>,
31 concrete_struct_id: ConcreteStructId<'db>,
32 },
33}
34impl<'db> MemberPath<'db> {
35 pub fn base_var(&self) -> VarId<'db> {
36 match self {
37 MemberPath::Var(var) => *var,
38 MemberPath::Member { parent, .. } => parent.base_var(),
39 }
40 }
41}
42impl<'db> From<&ExprVarMemberPath<'db>> for MemberPath<'db> {
43 fn from(value: &ExprVarMemberPath<'db>) -> Self {
44 match value {
45 ExprVarMemberPath::Var(expr) => MemberPath::Var(expr.var),
46 ExprVarMemberPath::Member { parent, member_id, concrete_struct_id, .. } => {
47 MemberPath::Member {
48 parent: Box::new(parent.as_ref().into()),
49 member_id: *member_id,
50 concrete_struct_id: *concrete_struct_id,
51 }
52 }
53 }
54 }
55}
56
57#[derive(Clone, Debug, Default, DebugWithDb)]
59#[debug_db(ExprFormatter<'db>)]
60pub struct Usage<'db> {
61 pub usage: OrderedHashMap<MemberPath<'db>, ExprVarMemberPath<'db>>,
63 pub changes: OrderedHashMap<MemberPath<'db>, ExprVarMemberPath<'db>>,
65 pub snap_usage: OrderedHashMap<MemberPath<'db>, ExprVarMemberPath<'db>>,
67 pub introductions: OrderedHashSet<VarId<'db>>,
69 pub has_early_return: bool,
71}
72
73impl<'db> Usage<'db> {
74 pub fn add_usage_and_changes(&mut self, usage: &Usage<'db>) {
76 for (path, expr) in usage.usage.iter() {
77 self.usage.insert(path.clone(), expr.clone());
78 }
79 for (path, expr) in usage.changes.iter() {
80 self.changes.insert(path.clone(), expr.clone());
81 }
82 for (path, expr) in usage.snap_usage.iter() {
83 self.snap_usage.insert(path.clone(), expr.clone());
84 }
85 self.has_early_return |= usage.has_early_return;
86 }
87
88 pub fn finalize_as_scope(&mut self) {
91 for (member_path, _) in self.usage.clone() {
92 if self.introductions.contains(&member_path.base_var()) {
94 self.usage.swap_remove(&member_path);
95 continue;
96 }
97
98 let mut current_path = member_path.clone();
100 while let MemberPath::Member { parent, .. } = current_path {
101 current_path = *parent.clone();
102 if self.usage.contains_key(¤t_path) {
103 self.usage.swap_remove(&member_path);
104 break;
105 }
106 }
107 }
108 for (member_path, _) in self.snap_usage.clone() {
109 if self.usage.contains_key(&member_path) {
111 self.snap_usage.swap_remove(&member_path);
112 continue;
113 }
114
115 if self.introductions.contains(&member_path.base_var()) {
117 self.snap_usage.swap_remove(&member_path);
118 }
119
120 let mut current_path = member_path.clone();
122 while let MemberPath::Member { parent, .. } = current_path {
123 current_path = *parent.clone();
124 if self.snap_usage.contains_key(¤t_path)
125 | self.usage.contains_key(¤t_path)
126 {
127 self.snap_usage.swap_remove(&member_path);
128 break;
129 }
130 }
131 }
132 for (member_path, _) in self.changes.clone() {
133 if self.introductions.contains(&member_path.base_var()) {
135 self.changes.swap_remove(&member_path);
136 }
137
138 let mut current_path = member_path.clone();
142 while let MemberPath::Member { parent, .. } = current_path {
143 current_path = *parent.clone();
144 if self.snap_usage.contains_key(¤t_path) {
145 if let Some(value) = self.snap_usage.swap_remove(¤t_path) {
148 self.usage.insert(current_path.clone(), value.clone());
149 self.changes.insert(current_path.clone(), value);
150 };
151 }
152 if self.changes.contains_key(¤t_path) {
153 self.changes.swap_remove(&member_path);
154 break;
155 }
156 }
157 }
158 }
159}
160
161#[derive(Debug, DebugWithDb)]
163#[debug_db(ExprFormatter<'db>)]
164pub struct Usages<'db> {
165 pub usages: OrderedHashMap<ExprId, Usage<'db>>,
167}
168impl<'db> Usages<'db> {
169 pub fn from_function_body(function_body: &FunctionBody<'db>) -> Self {
170 let mut current = Usage::default();
171 let mut usages = Self { usages: Default::default() };
172 usages.handle_expr(&function_body.arenas, function_body.body_expr, &mut current);
173 usages
174 }
175
176 pub fn handle_closure(
177 &mut self,
178 arenas: &Arenas<'db>,
179 param_ids: &[Parameter<'db>],
180 body: ExprId,
181 ) -> Usage<'db> {
182 let mut usage: Usage<'_> = Default::default();
183
184 usage.introductions.extend(param_ids.iter().map(|param| VarId::Param(param.id)));
185 self.handle_expr(arenas, body, &mut usage);
186 usage.finalize_as_scope();
187 usage
188 }
189
190 fn handle_expr(
191 &mut self,
192 arenas: &Arenas<'db>,
193 curr_expr_id: ExprId,
194 current: &mut Usage<'db>,
195 ) {
196 match &arenas.exprs[curr_expr_id] {
197 Expr::Tuple(expr) => {
198 for expr_id in &expr.items {
199 self.handle_expr(arenas, *expr_id, current);
200 }
201 }
202 Expr::FixedSizeArray(expr) => match &expr.items {
203 FixedSizeArrayItems::Items(items) => {
204 for expr_id in items {
205 self.handle_expr(arenas, *expr_id, current);
206 }
207 }
208 FixedSizeArrayItems::ValueAndSize(value, _) => {
209 self.handle_expr(arenas, *value, current);
210 }
211 },
212 Expr::Snapshot(expr) => {
213 let expr_id = expr.inner;
214
215 match &arenas.exprs[expr_id] {
216 Expr::Var(expr_var) => {
217 current.snap_usage.insert(
218 MemberPath::Var(expr_var.var),
219 ExprVarMemberPath::Var(expr_var.clone()),
220 );
221 }
222 Expr::MemberAccess(expr) => {
223 if let Some(member_path) = &expr.member_path {
224 current.snap_usage.insert(member_path.into(), member_path.clone());
225 } else {
226 self.handle_expr(arenas, expr.expr, current);
227 }
228 }
229 _ => self.handle_expr(arenas, expr_id, current),
230 }
231 }
232 Expr::Desnap(expr) => self.handle_expr(arenas, expr.inner, current),
233 Expr::Assignment(expr) => {
234 self.handle_expr(arenas, expr.rhs, current);
235 current.usage.insert((&expr.ref_arg).into(), expr.ref_arg.clone());
236 current.changes.insert((&expr.ref_arg).into(), expr.ref_arg.clone());
237 }
238 Expr::LogicalOperator(expr) => {
239 self.handle_expr(arenas, expr.lhs, current);
240 self.handle_expr(arenas, expr.rhs, current);
241 }
242 Expr::Block(expr) => {
243 let mut usage = Default::default();
244 for stmt in &expr.statements {
245 match &arenas.statements[*stmt] {
246 Statement::Let(StatementLet {
247 pattern,
248 expr,
249 else_clause,
250 stable_ptr: _,
251 }) => {
252 self.handle_expr(arenas, *expr, &mut usage);
253 Self::handle_pattern(&arenas.patterns, *pattern, &mut usage);
254
255 if let Some(else_clause) = else_clause {
256 self.handle_expr(arenas, *else_clause, &mut usage);
257 }
258 }
259 Statement::Expr(StatementExpr { expr, stable_ptr: _ }) => {
260 self.handle_expr(arenas, *expr, &mut usage)
261 }
262 Statement::Continue(_) => (),
263 Statement::Return(StatementReturn { expr_option, stable_ptr: _ }) => {
264 usage.has_early_return = true;
265 if let Some(expr) = expr_option {
266 self.handle_expr(arenas, *expr, &mut usage)
267 };
268 }
269 Statement::Break(StatementBreak { expr_option, stable_ptr: _ }) => {
270 if let Some(expr) = expr_option {
271 self.handle_expr(arenas, *expr, &mut usage)
272 };
273 }
274 Statement::Item(_) => {}
275 };
276 }
277 if let Some(expr_id) = expr.tail {
278 self.handle_expr(arenas, expr_id, &mut usage)
279 }
280 usage.finalize_as_scope();
281 current.add_usage_and_changes(&usage);
282 }
283 Expr::Loop(ExprLoop { body, ty: _, stable_ptr: _ }) => {
284 let mut usage = Default::default();
285 self.handle_expr(arenas, *body, &mut usage);
286 current.add_usage_and_changes(&usage);
287 self.usages.insert(curr_expr_id, usage);
288 }
289 Expr::While(ExprWhile { condition, body, stable_ptr: _, ty: _ }) => {
290 let mut usage = Default::default();
291 match condition {
292 Condition::BoolExpr(expr) => {
293 self.handle_expr(arenas, *expr, &mut usage);
294 }
295 Condition::Let(expr, patterns) => {
296 self.handle_expr(arenas, *expr, &mut usage);
297 for pattern in patterns {
298 Self::handle_pattern(&arenas.patterns, *pattern, &mut usage);
299 }
300 }
301 }
302 self.handle_expr(arenas, *body, &mut usage);
303 usage.finalize_as_scope();
304 current.add_usage_and_changes(&usage);
305
306 self.usages.insert(curr_expr_id, usage);
307 }
308 Expr::For(ExprFor {
309 expr_id,
310 into_iter_member_path,
311 pattern,
312 body,
313 stable_ptr: _,
314 into_iter: _,
315 next_function_id: _,
316 ty: _,
317 }) => {
318 self.handle_expr(arenas, *expr_id, current);
319 current
320 .introductions
321 .insert(extract_matches!(into_iter_member_path, ExprVarMemberPath::Var).var);
322 let mut usage: Usage<'_> = Default::default();
323 usage.usage.insert(into_iter_member_path.into(), into_iter_member_path.clone());
324 usage.changes.insert(into_iter_member_path.into(), into_iter_member_path.clone());
325 Self::handle_pattern(&arenas.patterns, *pattern, &mut usage);
326 self.handle_expr(arenas, *body, &mut usage);
327 usage.finalize_as_scope();
328 current.add_usage_and_changes(&usage);
329 self.usages.insert(curr_expr_id, usage);
330 }
331 Expr::ExprClosure(ExprClosure { body, params, stable_ptr: _, ty: _ }) => {
332 let usage = self.handle_closure(arenas, params, *body);
333
334 current.add_usage_and_changes(&usage);
335 self.usages.insert(curr_expr_id, usage);
336 }
337 Expr::FunctionCall(ExprFunctionCall {
338 args,
339 function: _,
340 coupon_arg: _,
341 stable_ptr: _,
342 ty: _,
343 }) => {
344 for arg in args {
345 match arg {
346 ExprFunctionCallArg::Reference(member_path) => {
347 current.usage.insert(member_path.into(), member_path.clone());
348 current.changes.insert(member_path.into(), member_path.clone());
349 }
350 ExprFunctionCallArg::Value(expr) => {
351 self.handle_expr(arenas, *expr, current)
352 }
353 }
354 }
355 }
356 Expr::Match(expr) => {
357 self.handle_expr(arenas, expr.matched_expr, current);
358 for arm in &expr.arms {
359 for pattern in &arm.patterns {
360 Self::handle_pattern(&arenas.patterns, *pattern, current);
361 }
362 self.handle_expr(arenas, arm.expression, current);
363 }
364 }
365 Expr::If(expr) => {
366 for condition in &expr.conditions {
367 match condition {
368 Condition::BoolExpr(expr) => {
369 self.handle_expr(arenas, *expr, current);
370 }
371 Condition::Let(expr, patterns) => {
372 self.handle_expr(arenas, *expr, current);
373 for pattern in patterns {
374 Self::handle_pattern(&arenas.patterns, *pattern, current);
375 }
376 }
377 }
378 }
379
380 self.handle_expr(arenas, expr.if_block, current);
381 if let Some(expr) = expr.else_block {
382 self.handle_expr(arenas, expr, current);
383 }
384 }
385 Expr::Var(expr) => {
386 current
387 .usage
388 .insert(MemberPath::Var(expr.var), ExprVarMemberPath::Var(expr.clone()));
389 }
390 Expr::Literal(_) | Expr::StringLiteral(_) => {}
391 Expr::MemberAccess(expr) => {
392 if let Some(member_path) = &expr.member_path {
393 current.usage.insert(member_path.into(), member_path.clone());
394 } else {
395 self.handle_expr(arenas, expr.expr, current);
396 }
397 }
398 Expr::StructCtor(expr) => {
399 for (expr_id, _) in &expr.members {
400 self.handle_expr(arenas, *expr_id, current);
401 }
402 if let Some(base) = &expr.base_struct {
403 self.handle_expr(arenas, *base, current);
404 }
405 }
406 Expr::EnumVariantCtor(expr) => self.handle_expr(arenas, expr.value_expr, current),
407 Expr::PropagateError(expr) => {
408 current.has_early_return = true;
409 self.handle_expr(arenas, expr.inner, current)
410 }
411 Expr::Constant(_) => {}
412 Expr::Missing(_) => {}
413 }
414 }
415
416 fn handle_pattern(arena: &PatternArena<'db>, pattern: PatternId, current: &mut Usage<'db>) {
417 let pattern = &arena[pattern];
418 match pattern {
419 Pattern::Literal(_) | Pattern::StringLiteral(_) => {}
420 Pattern::Variable(pattern) => {
421 current.introductions.insert(VarId::Local(pattern.var.id));
422 }
423 Pattern::Struct(pattern) => {
424 for (pattern, _) in &pattern.field_patterns {
425 Self::handle_pattern(arena, *pattern, current);
426 }
427 }
428 Pattern::Tuple(pattern) => {
429 for pattern in &pattern.field_patterns {
430 Self::handle_pattern(arena, *pattern, current);
431 }
432 }
433 Pattern::FixedSizeArray(pattern) => {
434 for pattern in &pattern.elements_patterns {
435 Self::handle_pattern(arena, *pattern, current);
436 }
437 }
438 Pattern::EnumVariant(pattern) => {
439 if let Some(inner_pattern) = &pattern.inner_pattern {
440 Self::handle_pattern(arena, *inner_pattern, current);
441 }
442 }
443 Pattern::Otherwise(_) => {}
444 Pattern::Missing(_) => {}
445 }
446 }
447}