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