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