1use cairo_lang_debug::DebugWithDb;
11use cairo_lang_defs::ids::{ExternFunctionId, NamedLanguageElementId};
12use cairo_lang_semantic::corelib::option_some_variant;
13use cairo_lang_semantic::helper::ModuleHelper;
14use cairo_lang_semantic::{ConcreteVariant, GenericArgumentId, MatchArmSelector, TypeId};
15use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
16use itertools::{Itertools, zip_eq};
17use salsa::Database;
18
19use crate::analysis::core::Edge;
20use crate::analysis::{DataflowAnalyzer, Direction, ForwardDataflowAnalysis};
21use crate::{
22 BlockEnd, BlockId, Lowered, MatchArm, MatchExternInfo, MatchInfo, Statement, VariableId,
23};
24
25#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
29struct Placeholder(usize);
30
31fn fresh_placeholder(next_placeholder: &mut usize) -> Placeholder {
34 *next_placeholder += 1;
35 Placeholder(*next_placeholder - 1)
36}
37
38#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
41enum FieldVar {
42 Var(VariableId),
43 Placeholder(Placeholder),
45}
46
47impl FieldVar {
48 fn as_var(self) -> Option<VariableId> {
50 match self {
51 FieldVar::Var(v) => Some(v),
52 FieldVar::Placeholder(_) => None,
53 }
54 }
55
56 fn find_rep(self, info: &mut EqualityState<'_>) -> Self {
58 match self {
59 FieldVar::Var(v) => FieldVar::Var(info.find(v)),
60 p @ FieldVar::Placeholder(_) => p,
61 }
62 }
63}
64
65const MAX_ARRAY_TRACKED_ITEMS: usize = 5;
71
72#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
75struct ArrayItems {
76 prefix_placeholder: Option<Placeholder>,
80 suffix: Vec<FieldVar>,
82}
83
84impl ArrayItems {
85 fn is_empty(&self) -> bool {
87 self.prefix_placeholder.is_none() && self.suffix.is_empty()
88 }
89
90 fn referenced_vars(&self) -> impl Iterator<Item = VariableId> + '_ {
92 self.suffix.iter().filter_map(|f| f.as_var())
93 }
94
95 fn find_rep(self, info: &mut EqualityState<'_>) -> Self {
97 Self { suffix: self.suffix.into_iter().map(|f| f.find_rep(info)).collect(), ..self }
98 }
99
100 fn append(mut self, elem: FieldVar, next_placeholder: &mut usize) -> Self {
103 if self.suffix.len() == MAX_ARRAY_TRACKED_ITEMS {
104 self.prefix_placeholder = Some(fresh_placeholder(next_placeholder));
105 self.suffix.remove(0);
106 }
107 self.suffix.push(elem);
108 self
109 }
110
111 fn pop(&self, from_front: bool, next_placeholder: &mut usize) -> PopResult {
113 if self.is_empty() {
115 return PopResult::KnownEmpty;
116 }
117 if self.prefix_placeholder.is_some() {
118 if from_front {
119 let [_, rest @ ..] = self.suffix.as_slice() else {
124 return PopResult::Unknown;
125 };
126 return PopResult::ForgottenElement {
127 remaining: ArrayItems {
128 prefix_placeholder: Some(fresh_placeholder(next_placeholder)),
129 suffix: rest.to_vec(),
130 },
131 };
132 }
133 if self.suffix.is_empty() {
134 return PopResult::Unknown;
137 }
138 }
139 let (&element, rest) =
140 if from_front { self.suffix.split_first() } else { self.suffix.split_last() }
141 .expect("suffix is non-empty");
142 PopResult::Element {
143 element,
144 remaining: ArrayItems {
145 prefix_placeholder: self.prefix_placeholder,
146 suffix: rest.to_vec(),
147 },
148 }
149 }
150}
151
152enum PopResult {
154 Element { element: FieldVar, remaining: ArrayItems },
156 ForgottenElement { remaining: ArrayItems },
158 KnownEmpty,
160 Unknown,
163}
164
165#[derive(Clone, Debug, Hash, PartialEq, Eq)]
168enum Relation<'db> {
169 Box(VariableId),
170 Snapshot(VariableId),
171 EnumConstruct(ConcreteVariant<'db>, VariableId),
172 StructConstruct(TypeId<'db>, Vec<FieldVar>),
173 Array(TypeId<'db>, ArrayItems),
174}
175
176impl<'db> Relation<'db> {
177 fn referenced_vars(&self) -> Box<dyn Iterator<Item = VariableId> + '_> {
179 match self {
180 Relation::Box(v) | Relation::Snapshot(v) | Relation::EnumConstruct(_, v) => {
181 Box::new(std::iter::once(*v))
182 }
183 Relation::StructConstruct(_, fields) => {
184 Box::new(fields.iter().filter_map(|f| f.as_var()))
185 }
186 Relation::Array(_, items) => Box::new(items.referenced_vars()),
187 }
188 }
189
190 fn with_fresh_reps(self, state: &mut EqualityState<'_>) -> Self {
192 match self {
193 Relation::Box(v) => Relation::Box(state.find(v)),
194 Relation::Snapshot(v) => Relation::Snapshot(state.find(v)),
195 Relation::EnumConstruct(variant, v) => Relation::EnumConstruct(variant, state.find(v)),
196 Relation::StructConstruct(ty, fields) => Relation::StructConstruct(
197 ty,
198 fields.into_iter().map(|f| f.find_rep(state)).collect(),
199 ),
200 Relation::Array(ty, items) => Relation::Array(ty, items.find_rep(state)),
201 }
202 }
203}
204
205#[derive(Clone, Debug, Default)]
213pub struct EqualityState<'db> {
214 union_find: OrderedHashMap<VariableId, VariableId>,
216
217 forward: OrderedHashMap<Relation<'db>, VariableId>,
225
226 reverse: OrderedHashMap<VariableId, Relation<'db>>,
229}
230
231impl<'db> EqualityState<'db> {
232 fn get_parent(&self, var: VariableId) -> VariableId {
234 self.union_find.get(&var).copied().unwrap_or(var)
235 }
236
237 fn find(&mut self, mut var: VariableId) -> VariableId {
240 let mut parent = self.get_parent(var);
241 while parent != var {
242 let grandparent = self.get_parent(parent);
243 self.union_find.insert(var, grandparent);
244 var = parent;
245 parent = grandparent;
246 }
247 var
248 }
249
250 pub(crate) fn find_immut(&self, mut var: VariableId) -> VariableId {
252 let mut parent = self.get_parent(var);
253 while parent != var {
254 var = parent;
255 parent = self.get_parent(var);
256 }
257 var
258 }
259
260 fn add_equality(&mut self, old_var: VariableId, union_var: VariableId) -> VariableId {
265 let old_var = self.find(old_var);
266 let new_var = self.find(union_var);
267 if new_var == old_var {
268 return old_var;
269 }
270 assert!(union_var == new_var, "Expected variables to always be 'new' or equal to old");
271 self.union_find.insert(new_var, old_var);
272 old_var
273 }
274
275 fn set_relation(&mut self, relation: Relation<'db>, output: VariableId) {
278 let relation = relation.with_fresh_reps(self);
280
281 let existing_output = if let Some(&existing_output) = self.forward.get(&relation) {
283 self.add_equality(existing_output, output)
284 } else {
285 output
286 };
287
288 self.forward.insert(relation.clone(), existing_output);
290 self.reverse.insert(existing_output, relation);
291 }
292
293 fn get_struct_construct_immut(&self, rep: VariableId) -> Option<(TypeId<'db>, Vec<FieldVar>)> {
295 match self.reverse.get(&rep)? {
296 Relation::StructConstruct(ty, fields) => Some((*ty, fields.clone())),
297 _ => None,
298 }
299 }
300
301 fn get_struct_construct(&mut self, var: VariableId) -> Option<(TypeId<'db>, Vec<FieldVar>)> {
303 let rep = self.find(var);
304 self.get_struct_construct_immut(rep)
305 }
306
307 fn get_array_construct_immut(&self, rep: VariableId) -> Option<(TypeId<'db>, ArrayItems)> {
309 match self.reverse.get(&rep)? {
310 Relation::Array(ty, items) => Some((*ty, items.clone())),
311 _ => None,
312 }
313 }
314
315 fn get_array_construct(&mut self, var: VariableId) -> Option<(TypeId<'db>, ArrayItems)> {
317 let rep = self.find(var);
318 self.get_array_construct_immut(rep)
319 }
320
321 pub(crate) fn get_enum_construct(
323 &self,
324 var: VariableId,
325 ) -> Option<(ConcreteVariant<'db>, VariableId)> {
326 let rep = self.find_immut(var);
327 self.get_enum_construct_immut(rep)
328 }
329
330 fn get_enum_construct_immut(
332 &self,
333 rep: VariableId,
334 ) -> Option<(ConcreteVariant<'db>, VariableId)> {
335 match self.reverse.get(&rep)? {
336 Relation::EnumConstruct(variant, input) => Some((*variant, *input)),
337 _ => None,
338 }
339 }
340}
341
342impl<'db> DebugWithDb<'db> for EqualityState<'db> {
343 type Db = dyn Database;
344
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
346 let v = |id: VariableId| format!("v{}", self.find_immut(id).index());
347 let mut lines = Vec::<String>::new();
348 for (relation, &output) in self.forward.iter() {
349 match relation {
350 Relation::Snapshot(source) => {
351 lines.push(format!("@{} = {}", v(*source), v(output)));
352 }
353 Relation::Box(source) => {
354 lines.push(format!("Box({}) = {}", v(*source), v(output)));
355 }
356 Relation::EnumConstruct(variant, input) => {
357 let name = variant.id.name(db).to_string(db);
358 lines.push(format!("{name}({}) = {}", v(*input), v(output)));
359 }
360 Relation::StructConstruct(ty, inputs) => {
361 let type_name = ty.format(db);
362 let fields = inputs
363 .iter()
364 .map(|f| match f {
365 FieldVar::Var(id) => v(*id),
366 FieldVar::Placeholder(_) => "?".to_string(),
367 })
368 .collect::<Vec<_>>()
369 .join(", ");
370 lines.push(format!("{type_name}({fields}) = {}", v(output)));
371 }
372 Relation::Array(ty, items) => {
375 let type_name = ty.format(db);
376 let elem = |f: &FieldVar| match f {
377 FieldVar::Var(id) => v(*id),
378 FieldVar::Placeholder(_) => "?".to_string(),
379 };
380 let prefix = items.prefix_placeholder.iter().map(|_| "?(*)".to_string());
381 let rendered =
382 prefix.chain(items.suffix.iter().map(elem)).collect::<Vec<_>>().join(", ");
383 lines.push(format!("{type_name}[{rendered}] = {}", v(output)));
384 }
385 }
386 }
387 for &var in self.union_find.keys() {
388 let rep = self.find_immut(var);
389 if var != rep {
390 lines.push(format!("v{} = v{}", rep.index(), var.index()));
391 }
392 }
393 lines.sort();
394 if lines.is_empty() {
395 write!(f, "(empty)")
396 } else {
397 write!(f, "{}", lines.iter().format(", "))
398 }
399 }
400}
401
402pub struct EqualityAnalysis<'a, 'db> {
411 db: &'db dyn Database,
412 lowered: &'a Lowered<'db>,
413 next_placeholder: usize,
416 array_new: ExternFunctionId<'db>,
418 array_append: ExternFunctionId<'db>,
420 array_pop_front: ExternFunctionId<'db>,
422 array_pop_front_consume: ExternFunctionId<'db>,
424 array_snapshot_pop_front: ExternFunctionId<'db>,
426 array_snapshot_pop_back: ExternFunctionId<'db>,
428}
429
430impl<'a, 'db> EqualityAnalysis<'a, 'db> {
431 pub fn new(db: &'db dyn Database, lowered: &'a Lowered<'db>) -> Self {
433 let array_module = ModuleHelper::core(db).submodule("array");
434 Self {
435 db,
436 lowered,
437 next_placeholder: 0,
438 array_new: array_module.extern_function_id("array_new"),
439 array_append: array_module.extern_function_id("array_append"),
440 array_pop_front: array_module.extern_function_id("array_pop_front"),
441 array_pop_front_consume: array_module.extern_function_id("array_pop_front_consume"),
442 array_snapshot_pop_front: array_module.extern_function_id("array_snapshot_pop_front"),
443 array_snapshot_pop_back: array_module.extern_function_id("array_snapshot_pop_back"),
444 }
445 }
446
447 pub fn analyze(
450 db: &'db dyn Database,
451 lowered: &'a Lowered<'db>,
452 ) -> Vec<Option<EqualityState<'db>>> {
453 ForwardDataflowAnalysis::new(lowered, EqualityAnalysis::new(db, lowered)).run()
454 }
455
456 fn transfer_extern_match_arm(
469 &mut self,
470 info: &mut EqualityState<'db>,
471 extern_info: &MatchExternInfo<'db>,
472 arm: &MatchArm<'db>,
473 ) {
474 let Some((id, _)) = extern_info.function.get_extern(self.db) else { return };
475 let MatchArmSelector::VariantId(variant) = arm.arm_selector else { return };
477 if id == self.array_pop_front
478 || id == self.array_pop_front_consume
479 || id == self.array_snapshot_pop_front
480 || id == self.array_snapshot_pop_back
481 {
482 let [GenericArgumentId::Type(option_ty)] =
483 variant.concrete_enum_id.long(self.db).generic_args[..]
484 else {
485 panic!("Expected Option<T> with a single type argument");
486 };
487 let some_variant = option_some_variant(self.db, option_ty);
488 assert_eq!(
489 variant.concrete_enum_id.enum_id(self.db),
490 some_variant.concrete_enum_id.enum_id(self.db),
491 "Expected match to be on an Option<T>"
492 );
493 self.transfer_array_pop_arm(info, extern_info, arm, id, variant == some_variant);
494 }
495 }
496
497 fn transfer_array_pop_arm(
504 &mut self,
505 info: &mut EqualityState<'db>,
506 extern_info: &MatchExternInfo<'db>,
507 arm: &MatchArm<'db>,
508 id: ExternFunctionId<'db>,
509 is_some: bool,
510 ) {
511 let is_snapshot = id == self.array_snapshot_pop_front || id == self.array_snapshot_pop_back;
512 let is_owned = id == self.array_pop_front || id == self.array_pop_front_consume;
513 if !is_some || !(is_snapshot || is_owned) {
514 return;
515 }
516 let input_arr = extern_info.inputs[0].var_id;
518 let remaining_arr = arm.var_ids[0];
519 let boxed_elem = arm.var_ids[1];
520
521 let input_rep = info.find(input_arr);
524 let original_rep = match info.reverse.get(&input_rep) {
525 Some(Relation::Snapshot(v)) if is_snapshot => Some(info.find_immut(*v)),
526 _ => None,
527 };
528 let Some((_, items)) = original_rep
529 .and_then(|orig| info.get_array_construct_immut(orig))
530 .or_else(|| info.get_array_construct_immut(input_rep))
531 else {
532 return;
533 };
534
535 let from_front = id != self.array_snapshot_pop_back;
536 let remaining = match items.pop(from_front, &mut self.next_placeholder) {
537 PopResult::Element { element, remaining } => {
538 if let FieldVar::Var(elem_var) = element {
541 let box_target = if is_snapshot {
544 let elem_rep = info.find(elem_var);
545 info.forward.get(&Relation::Snapshot(elem_rep)).copied()
546 } else {
547 Some(elem_var)
548 };
549 if let Some(target) = box_target {
550 info.set_relation(Relation::Box(target), boxed_elem);
551 }
552 }
553 remaining
554 }
555 PopResult::ForgottenElement { remaining } => remaining,
556 PopResult::KnownEmpty | PopResult::Unknown => return,
559 };
560 let ty = self.lowered.variables[remaining_arr].ty;
561 info.set_relation(Relation::Array(ty, remaining), remaining_arr);
562 }
563}
564
565fn merge_referenced_vars<'db, 'a>(
568 info1: &'a EqualityState<'db>,
569 info2: &'a EqualityState<'db>,
570) -> impl Iterator<Item = VariableId> + 'a {
571 let union_find_vars = info1.union_find.keys().chain(info2.union_find.keys()).copied();
572
573 let forward_vars =
574 info1.forward.iter().chain(info2.forward.iter()).flat_map(|(relation, &output)| {
575 relation.referenced_vars().chain(std::iter::once(output))
576 });
577
578 let reverse_vars = info1
579 .reverse
580 .iter()
581 .chain(info2.reverse.iter())
582 .flat_map(|(&rep, relation)| std::iter::once(rep).chain(relation.referenced_vars()));
583
584 union_find_vars.chain(forward_vars).chain(reverse_vars)
585}
586
587fn merge_field(
590 f1: FieldVar,
591 f2: FieldVar,
592 result: &mut EqualityState<'_>,
593 next_placeholder: &mut usize,
594 any_data: &mut bool,
595) -> FieldVar {
596 match (f1, f2) {
597 (FieldVar::Var(v), FieldVar::Var(w)) if result.find(v) == result.find(w) => {
598 *any_data = true;
599 FieldVar::Var(result.find(v))
600 }
601 (_, _) => FieldVar::Placeholder(fresh_placeholder(next_placeholder)),
602 }
603}
604
605fn merge_array_items(
610 items1: &ArrayItems,
611 items2: &ArrayItems,
612 result: &mut EqualityState<'_>,
613 next_placeholder: &mut usize,
614) -> (ArrayItems, bool) {
615 let mut any_data = false;
616 let kept = items1.suffix.len().min(items2.suffix.len());
618 let same_shape = items1.suffix.len() == items2.suffix.len();
619 let prefix_placeholder = match (items1.prefix_placeholder, items2.prefix_placeholder) {
620 (None, None) if same_shape => None,
621 (Some(p1), Some(p2)) if p1 == p2 && same_shape => {
622 any_data = true;
623 Some(p1)
624 }
625 _ => Some(fresh_placeholder(next_placeholder)),
626 };
627 let suffix = zip_eq(
628 &items1.suffix[items1.suffix.len() - kept..],
629 &items2.suffix[items2.suffix.len() - kept..],
630 )
631 .map(|(f1, f2)| merge_field(*f1, *f2, result, next_placeholder, &mut any_data))
632 .collect();
633 (ArrayItems { prefix_placeholder, suffix }, any_data)
634}
635
636fn merge_relations<'db>(
641 info1: &EqualityState<'db>,
642 info2: &EqualityState<'db>,
643 intersections: &OrderedHashMap<VariableId, Vec<(VariableId, VariableId)>>,
644 result: &mut EqualityState<'db>,
645 next_placeholder: &mut usize,
646) {
647 let mut info2_structs_by_output: OrderedHashMap<VariableId, (TypeId<'db>, Vec<FieldVar>)> =
651 OrderedHashMap::default();
652 let mut info2_arrays_by_output: OrderedHashMap<VariableId, (TypeId<'db>, ArrayItems)> =
653 OrderedHashMap::default();
654 for (relation, &output2) in info2.forward.iter() {
655 match relation {
656 Relation::StructConstruct(ty, fields) => {
657 info2_structs_by_output.insert(info2.find_immut(output2), (*ty, fields.clone()));
658 }
659 Relation::Array(ty, items) => {
660 info2_arrays_by_output.insert(info2.find_immut(output2), (*ty, items.clone()));
661 }
662 _ => {}
663 }
664 }
665
666 for (relation, &output1) in info1.forward.iter() {
668 match relation {
669 Relation::Box(source1) | Relation::Snapshot(source1) => {
670 for &(source2, intersection_var) in intersections.get(source1).into_iter().flatten()
671 {
672 let relation2 = match relation {
673 Relation::Box(_) => Relation::Box(source2),
674 Relation::Snapshot(_) => Relation::Snapshot(source2),
675 _ => unreachable!(),
676 };
677 let Some(&output2) = info2.forward.get(&relation2) else { continue };
678 let output_rep = result.find(output1);
683 if output_rep != result.find_immut(output2) {
684 continue;
685 }
686 let result_relation = match relation {
687 Relation::Box(_) => Relation::Box(result.find(intersection_var)),
688 Relation::Snapshot(_) => Relation::Snapshot(result.find(intersection_var)),
689 _ => unreachable!(),
690 };
691 result.set_relation(result_relation, output_rep);
692 }
693 }
694 Relation::EnumConstruct(variant, input1) => {
695 for &(input2, input_intersection) in
696 intersections.get(&info1.find_immut(*input1)).into_iter().flatten()
697 {
698 let relation2 = Relation::EnumConstruct(*variant, input2);
699 let Some(&output2) = info2.forward.get(&relation2) else { continue };
700 let output_rep = result.find(output1);
701 if output_rep != result.find_immut(output2) {
702 continue;
703 }
704 result.set_relation(
705 Relation::EnumConstruct(*variant, input_intersection),
706 output_rep,
707 );
708 }
709 }
710 Relation::StructConstruct(ty, fields1) => {
711 let output1_rep = info1.find_immut(output1);
712 for &(output2_rep, output_intersection) in
713 intersections.get(&output1_rep).into_iter().flatten()
714 {
715 let Some((ty2, fields2)) = info2_structs_by_output.get(&output2_rep) else {
716 continue;
717 };
718 if ty2 != ty || fields2.len() != fields1.len() {
719 continue;
720 }
721 let mut any_data = false;
722 let result_fields: Vec<FieldVar> = fields1
723 .iter()
724 .zip(fields2)
725 .map(|(f1, f2)| {
726 merge_field(*f1, *f2, result, next_placeholder, &mut any_data)
727 })
728 .collect();
729 if any_data || result_fields.is_empty() {
730 result.set_relation(
731 Relation::StructConstruct(*ty, result_fields),
732 output_intersection,
733 );
734 }
735 }
736 }
737 Relation::Array(ty, items1) => {
738 let output1_rep = info1.find_immut(output1);
739 for &(output2_rep, output_intersection) in
740 intersections.get(&output1_rep).into_iter().flatten()
741 {
742 let Some((ty2, items2)) = info2_arrays_by_output.get(&output2_rep) else {
743 continue;
744 };
745 if ty2 != ty {
746 continue;
747 }
748 let (result_items, any_data) =
749 merge_array_items(items1, items2, result, next_placeholder);
750 if any_data || result_items.is_empty() {
751 result
752 .set_relation(Relation::Array(*ty, result_items), output_intersection);
753 }
754 }
755 }
756 }
757 }
758}
759
760impl<'db, 'a> DataflowAnalyzer<'db, 'a> for EqualityAnalysis<'a, 'db> {
761 type Info = EqualityState<'db>;
762
763 const DIRECTION: Direction = Direction::Forward;
764
765 fn initial_info(&mut self, _block_id: BlockId, _block_end: &'a BlockEnd<'db>) -> Self::Info {
766 EqualityState::default()
767 }
768
769 fn merge(
770 &mut self,
771 _lowered: &Lowered<'db>,
772 _statement_location: super::StatementLocation,
773 info1: Self::Info,
774 info2: Self::Info,
775 ) -> Self::Info {
776 let mut result = EqualityState::default();
780
781 let mut groups: OrderedHashMap<(VariableId, VariableId), Vec<VariableId>> =
783 OrderedHashMap::default();
784
785 for var in merge_referenced_vars(&info1, &info2) {
787 let key = (info1.find_immut(var), info2.find_immut(var));
788 groups.entry(key).or_default().push(var);
789 }
790
791 for members in groups.values() {
795 let Some(&keep) = members.iter().min_by_key(|v| v.index()) else { continue };
796 for &var in members {
797 if var != keep {
798 result.add_equality(keep, var);
799 }
800 }
801 }
802
803 let mut intersections: OrderedHashMap<VariableId, Vec<(VariableId, VariableId)>> =
805 OrderedHashMap::default();
806 for (&(rep1, rep2), vars) in groups.iter() {
807 intersections.entry(rep1).or_default().push((rep2, result.find(vars[0])));
808 }
809
810 merge_relations(&info1, &info2, &intersections, &mut result, &mut self.next_placeholder);
811
812 result
813 }
814
815 fn transfer_stmt(
816 &mut self,
817 info: &mut Self::Info,
818 _statement_location: super::StatementLocation,
819 stmt: &'a Statement<'db>,
820 ) {
821 match stmt {
822 Statement::Snapshot(snapshot_stmt) => {
823 info.add_equality(snapshot_stmt.input.var_id, snapshot_stmt.original());
824 info.set_relation(
825 Relation::Snapshot(snapshot_stmt.input.var_id),
826 snapshot_stmt.snapshot(),
827 );
828 }
829
830 Statement::Desnap(desnap_stmt) => {
831 let input_rep = info.find(desnap_stmt.input.var_id);
832 if let Some(Relation::Snapshot(old_inner)) = info.reverse.get(&input_rep) {
833 info.add_equality(*old_inner, desnap_stmt.output);
834 } else {
835 info.set_relation(
836 Relation::Snapshot(desnap_stmt.output),
837 desnap_stmt.input.var_id,
838 );
839 }
840 }
841
842 Statement::IntoBox(into_box_stmt) => {
843 info.set_relation(Relation::Box(into_box_stmt.input.var_id), into_box_stmt.output);
844 }
845
846 Statement::Unbox(unbox_stmt) => {
847 let input_rep = info.find(unbox_stmt.input.var_id);
848 if let Some(Relation::Box(old_inner)) = info.reverse.get(&input_rep) {
849 info.add_equality(*old_inner, unbox_stmt.output);
850 } else {
851 info.set_relation(Relation::Box(unbox_stmt.output), unbox_stmt.input.var_id);
852 }
853 }
854
855 Statement::EnumConstruct(enum_stmt) => {
856 info.set_relation(
860 Relation::EnumConstruct(enum_stmt.variant, enum_stmt.input.var_id),
861 enum_stmt.output,
862 );
863 }
864
865 Statement::StructConstruct(struct_stmt) => {
866 let ty = self.lowered.variables[struct_stmt.output].ty;
870 let fields =
871 struct_stmt.inputs.iter().map(|i| FieldVar::Var(info.find(i.var_id))).collect();
872 info.set_relation(Relation::StructConstruct(ty, fields), struct_stmt.output);
873 }
874
875 Statement::StructDestructure(struct_stmt) => {
876 let struct_var = info.find(struct_stmt.input.var_id);
878 if let Some((_, field_reps)) = info.get_struct_construct(struct_var) {
881 for (&output, field) in struct_stmt.outputs.iter().zip(field_reps.iter()) {
882 if let FieldVar::Var(field_rep) = field {
883 info.add_equality(*field_rep, output);
884 }
885 }
886 }
887 let ty = self.lowered.variables[struct_var].ty;
889 let fields =
890 struct_stmt.outputs.iter().map(|&v| FieldVar::Var(info.find(v))).collect();
891 info.set_relation(Relation::StructConstruct(ty, fields), struct_var);
892 }
893
894 Statement::Call(call_stmt) => {
895 let Some((id, _)) = call_stmt.function.get_extern(self.db) else { return };
896 if id == self.array_new {
897 let ty = self.lowered.variables[call_stmt.outputs[0]].ty;
898 info.set_relation(
899 Relation::Array(ty, ArrayItems::default()),
900 call_stmt.outputs[0],
901 );
902 } else if id == self.array_append
903 && let Some((ty, items)) = info.get_array_construct(call_stmt.inputs[0].var_id)
904 {
905 let elem = FieldVar::Var(info.find(call_stmt.inputs[1].var_id));
908 let new_items = items.append(elem, &mut self.next_placeholder);
909 info.set_relation(Relation::Array(ty, new_items), call_stmt.outputs[0]);
910 }
911 }
912
913 Statement::Const(_) => {}
914 }
915 }
916
917 fn transfer_edge(&mut self, info: &Self::Info, edge: &Edge<'db, 'a>) -> Self::Info {
918 let mut new_info = info.clone();
919 match edge {
920 Edge::Goto { remapping, .. } => {
921 for (dst, src_usage) in remapping.iter() {
923 new_info.add_equality(src_usage.var_id, *dst);
924 }
925 }
926 Edge::MatchArm { arm, match_info } => {
927 if let MatchInfo::Enum(enum_info) = match_info
929 && let MatchArmSelector::VariantId(variant) = arm.arm_selector
930 && let [arm_var] = arm.var_ids[..]
931 {
932 let matched_var = enum_info.input.var_id;
933
934 let matched_rep = new_info.find(matched_var);
938 if let Some((old_variant, input)) =
939 new_info.get_enum_construct_immut(matched_rep)
940 && variant == old_variant
941 {
942 new_info.add_equality(input, arm_var);
943 }
944
945 new_info.set_relation(Relation::EnumConstruct(variant, arm_var), matched_var);
947 }
948
949 if let MatchInfo::Extern(extern_info) = match_info {
951 self.transfer_extern_match_arm(&mut new_info, extern_info, arm);
952 }
953 }
954 Edge::Return { .. } | Edge::Panic { .. } => {}
955 }
956 new_info
957 }
958}