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