1use itertools::{EitherOrBoth, Itertools};
46use ruff_index::newtype_index;
47use smallvec::{SmallVec, smallvec};
48
49use crate::ReachabilityConstraintsBuilder;
50use crate::narrowing_constraints::{NarrowingConstraintsBuilder, ScopedNarrowingConstraint};
51use crate::reachability_constraints::ScopedReachabilityConstraintId;
52
53#[newtype_index]
56#[derive(Ord, PartialOrd, get_size2::GetSize)]
57pub struct ScopedDefinitionId;
58
59impl ScopedDefinitionId {
60 pub(crate) const UNBOUND: ScopedDefinitionId = ScopedDefinitionId::from_u32(0);
66
67 pub(crate) fn is_unbound(self) -> bool {
68 self == Self::UNBOUND
69 }
70}
71
72#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, get_size2::GetSize)]
75pub(super) struct Declarations {
76 live_declarations: SmallVec<[LiveDeclaration; 2]>,
78}
79
80#[derive(Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
82pub(super) struct LiveDeclaration {
83 pub(super) declaration: ScopedDefinitionId,
84 pub(super) reachability_constraint: ScopedReachabilityConstraintId,
85}
86
87pub(super) type LiveDeclarationsIterator<'a> = std::slice::Iter<'a, LiveDeclaration>;
88
89#[derive(Clone, Copy, Debug)]
93pub(crate) enum PreviousDefinitions {
94 AreShadowed,
95 AreKept,
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
103pub(crate) enum FutureDefinitions {
104 ShadowThisOne,
105 DontShadowThisOne,
106}
107
108impl PreviousDefinitions {
109 fn are_shadowed(self) -> bool {
110 matches!(self, PreviousDefinitions::AreShadowed)
111 }
112}
113
114impl Declarations {
115 pub(super) fn undeclared_reachability_constraint(
116 &self,
117 ) -> Option<ScopedReachabilityConstraintId> {
118 let [
119 LiveDeclaration {
120 declaration: ScopedDefinitionId::UNBOUND,
121 reachability_constraint,
122 },
123 ] = self.live_declarations.as_slice()
124 else {
125 return None;
126 };
127 Some(*reachability_constraint)
128 }
129
130 pub(super) fn is_always_undeclared(&self) -> bool {
131 self.undeclared_reachability_constraint()
132 == Some(ScopedReachabilityConstraintId::ALWAYS_TRUE)
133 }
134
135 pub(super) fn undeclared(reachability_constraint: ScopedReachabilityConstraintId) -> Self {
136 let initial_declaration = LiveDeclaration {
137 declaration: ScopedDefinitionId::UNBOUND,
138 reachability_constraint,
139 };
140 Self {
141 live_declarations: smallvec![initial_declaration],
142 }
143 }
144
145 pub(super) fn record_declaration(
147 &mut self,
148 declaration: ScopedDefinitionId,
149 reachability_constraint: ScopedReachabilityConstraintId,
150 previous_definitions: PreviousDefinitions,
151 ) {
152 if previous_definitions.are_shadowed() {
153 self.live_declarations.clear();
155 }
156 self.live_declarations.push(LiveDeclaration {
157 declaration,
158 reachability_constraint,
159 });
160 }
161
162 fn record_reachability_constraint(
164 &mut self,
165 reachability_constraints: &mut ReachabilityConstraintsBuilder,
166 constraint: ScopedReachabilityConstraintId,
167 ) {
168 for declaration in &mut self.live_declarations {
169 declaration.reachability_constraint = reachability_constraints
170 .add_and_constraint(declaration.reachability_constraint, constraint);
171 }
172 }
173
174 pub(super) fn iter(&self) -> LiveDeclarationsIterator<'_> {
176 self.live_declarations.iter()
177 }
178
179 pub(super) fn as_slice(&self) -> &[LiveDeclaration] {
180 &self.live_declarations
181 }
182
183 fn merge(&mut self, b: Self, reachability_constraints: &mut ReachabilityConstraintsBuilder) {
184 let a = std::mem::take(self);
185
186 let a = a.live_declarations.into_iter();
191 let b = b.live_declarations.into_iter();
192 for zipped in a.merge_join_by(b, |a, b| a.declaration.cmp(&b.declaration)) {
193 match zipped {
194 EitherOrBoth::Both(a, b) => {
195 let reachability_constraint = reachability_constraints
196 .add_or_constraint(a.reachability_constraint, b.reachability_constraint);
197 self.live_declarations.push(LiveDeclaration {
198 declaration: a.declaration,
199 reachability_constraint,
200 });
201 }
202
203 EitherOrBoth::Left(declaration) | EitherOrBoth::Right(declaration) => {
204 self.live_declarations.push(declaration);
205 }
206 }
207 }
208 }
209}
210
211#[derive(Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
217pub(super) enum EnclosingSnapshot {
218 Constraint(ScopedNarrowingConstraint),
219 Bindings(Bindings),
220}
221
222#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, get_size2::GetSize)]
225pub(super) struct Bindings {
226 unbound_narrowing_constraint: Option<ScopedNarrowingConstraint>,
231 live_bindings: SmallVec<[LiveBinding; 2]>,
233}
234
235impl Bindings {
236 pub(super) fn is_always_unbound(&self) -> bool {
237 let [binding] = self.live_bindings.as_slice() else {
238 return false;
239 };
240 self.unbound_narrowing_constraint.is_none()
241 && binding.binding() == ScopedDefinitionId::UNBOUND
242 && binding.narrowing_constraint == ScopedNarrowingConstraint::ALWAYS_TRUE
243 && binding.reachability_constraint == ScopedReachabilityConstraintId::ALWAYS_TRUE
244 && binding.can_be_shadowed() == FutureDefinitions::ShadowThisOne
245 }
246
247 pub(super) fn unbound_narrowing_constraint(&self) -> ScopedNarrowingConstraint {
248 self.unbound_narrowing_constraint
249 .unwrap_or(self.live_bindings[0].narrowing_constraint)
250 }
251
252 pub(super) fn finish(
253 &mut self,
254 narrowing_constraints: &mut NarrowingConstraintsBuilder,
255 reachability_constraints: &mut ReachabilityConstraintsBuilder,
256 ) {
257 self.live_bindings.shrink_to_fit();
258 for binding in &self.live_bindings {
259 reachability_constraints.mark_used(binding.reachability_constraint);
260 narrowing_constraints.mark_used(binding.narrowing_constraint);
261 }
262 }
263}
264
265#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
267pub struct LiveBinding {
268 binding: PackedDefinitionId,
269 narrowing_constraint: ScopedNarrowingConstraint,
270 reachability_constraint: ScopedReachabilityConstraintId,
271}
272
273#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
274struct PackedDefinitionId(u32);
275
276impl PackedDefinitionId {
277 const DONT_SHADOW: u32 = 1 << 31;
280 const DEFINITION_MASK: u32 = !Self::DONT_SHADOW;
281
282 fn new(binding: ScopedDefinitionId, can_be_shadowed: FutureDefinitions) -> Self {
283 let binding = binding.as_u32();
284 assert_eq!(
285 binding & Self::DONT_SHADOW,
286 0,
287 "scopes cannot contain more than 2^31 definitions"
288 );
289 Self(
290 binding
291 | match can_be_shadowed {
292 FutureDefinitions::ShadowThisOne => 0,
293 FutureDefinitions::DontShadowThisOne => Self::DONT_SHADOW,
294 },
295 )
296 }
297
298 const fn definition(self) -> ScopedDefinitionId {
299 ScopedDefinitionId::from_u32(self.0 & Self::DEFINITION_MASK)
300 }
301
302 const fn can_be_shadowed(self) -> FutureDefinitions {
303 if self.0 & Self::DONT_SHADOW == 0 {
304 FutureDefinitions::ShadowThisOne
305 } else {
306 FutureDefinitions::DontShadowThisOne
307 }
308 }
309}
310
311impl LiveBinding {
312 fn new(
313 binding: ScopedDefinitionId,
314 narrowing_constraint: ScopedNarrowingConstraint,
315 reachability_constraint: ScopedReachabilityConstraintId,
316 can_be_shadowed: FutureDefinitions,
317 ) -> Self {
318 Self {
319 binding: PackedDefinitionId::new(binding, can_be_shadowed),
320 narrowing_constraint,
321 reachability_constraint,
322 }
323 }
324
325 pub const fn binding(&self) -> ScopedDefinitionId {
326 self.binding.definition()
327 }
328
329 pub const fn narrowing_constraint(&self) -> ScopedNarrowingConstraint {
330 self.narrowing_constraint
331 }
332
333 pub const fn reachability_constraint(&self) -> ScopedReachabilityConstraintId {
334 self.reachability_constraint
335 }
336
337 const fn can_be_shadowed(&self) -> FutureDefinitions {
338 self.binding.can_be_shadowed()
339 }
340}
341
342static_assertions::assert_eq_size!(LiveBinding, [u32; 3]);
343
344pub(super) type LiveBindingsIterator<'a> = std::slice::Iter<'a, LiveBinding>;
345
346impl Bindings {
347 pub(super) fn unbound(reachability_constraint: ScopedReachabilityConstraintId) -> Self {
348 let initial_binding = LiveBinding::new(
349 ScopedDefinitionId::UNBOUND,
350 ScopedNarrowingConstraint::ALWAYS_TRUE,
351 reachability_constraint,
352 FutureDefinitions::ShadowThisOne,
353 );
354 Self {
355 unbound_narrowing_constraint: None,
356 live_bindings: smallvec![initial_binding],
357 }
358 }
359
360 pub(super) fn record_binding(
362 &mut self,
363 binding: ScopedDefinitionId,
364 reachability_constraint: ScopedReachabilityConstraintId,
365 is_class_scope: bool,
366 is_place_name: bool,
367 previous_definitions: PreviousDefinitions,
368 can_be_shadowed: FutureDefinitions,
369 ) {
370 if is_class_scope && is_place_name && self.live_bindings[0].binding().is_unbound() {
373 self.unbound_narrowing_constraint = Some(self.live_bindings[0].narrowing_constraint);
374 }
375 if previous_definitions.are_shadowed() {
378 self.live_bindings
379 .retain(|b| b.can_be_shadowed() == FutureDefinitions::DontShadowThisOne);
380 }
381 self.live_bindings.push(LiveBinding::new(
382 binding,
383 ScopedNarrowingConstraint::ALWAYS_TRUE,
384 reachability_constraint,
385 can_be_shadowed,
386 ));
387 }
388
389 fn record_narrowing_constraint(
391 &mut self,
392 narrowing_constraints: &mut NarrowingConstraintsBuilder,
393 constraint: ScopedNarrowingConstraint,
394 ) {
395 for binding in &mut self.live_bindings {
396 binding.narrowing_constraint =
397 narrowing_constraints.add_and_constraint(binding.narrowing_constraint, constraint);
398 }
399 }
400
401 fn record_reachability_constraint(
403 &mut self,
404 reachability_constraints: &mut ReachabilityConstraintsBuilder,
405 constraint: ScopedReachabilityConstraintId,
406 ) {
407 for binding in &mut self.live_bindings {
408 binding.reachability_constraint = reachability_constraints
409 .add_and_constraint(binding.reachability_constraint, constraint);
410 }
411 }
412
413 pub(super) fn iter(&self) -> LiveBindingsIterator<'_> {
415 self.live_bindings.iter()
416 }
417
418 pub(super) fn as_slice(&self) -> &[LiveBinding] {
419 &self.live_bindings
420 }
421
422 pub(super) fn merge(
423 &mut self,
424 b: Self,
425 narrowing_constraints: &mut NarrowingConstraintsBuilder,
426 reachability_constraints: &mut ReachabilityConstraintsBuilder,
427 ) {
428 let a = std::mem::take(self);
429
430 if let Some((a, b)) = a
431 .unbound_narrowing_constraint
432 .zip(b.unbound_narrowing_constraint)
433 {
434 self.unbound_narrowing_constraint = Some(narrowing_constraints.add_or_constraint(a, b));
435 }
436
437 let a = a.live_bindings.into_iter();
442 let b = b.live_bindings.into_iter();
443 for zipped in a.merge_join_by(b, |a, b| a.binding().cmp(&b.binding())) {
444 match zipped {
445 EitherOrBoth::Both(a, b) => {
446 let narrowing_constraint = narrowing_constraints
449 .add_or_constraint(a.narrowing_constraint, b.narrowing_constraint);
450
451 let reachability_constraint = reachability_constraints
453 .add_or_constraint(a.reachability_constraint, b.reachability_constraint);
454
455 debug_assert_eq!(a.can_be_shadowed(), b.can_be_shadowed());
456 self.live_bindings.push(LiveBinding::new(
457 a.binding(),
458 narrowing_constraint,
459 reachability_constraint,
460 a.can_be_shadowed(),
461 ));
462 }
463
464 EitherOrBoth::Left(binding) | EitherOrBoth::Right(binding) => {
465 self.live_bindings.push(binding);
466 }
467 }
468 }
469 }
470}
471
472#[derive(Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)]
473pub(crate) struct PlaceState {
474 declarations: Declarations,
475 bindings: Bindings,
476}
477
478impl PlaceState {
479 pub(super) fn undefined(reachability: ScopedReachabilityConstraintId) -> Self {
481 Self {
482 declarations: Declarations::undeclared(reachability),
483 bindings: Bindings::unbound(reachability),
484 }
485 }
486
487 pub(super) fn record_binding(
489 &mut self,
490 binding_id: ScopedDefinitionId,
491 reachability_constraint: ScopedReachabilityConstraintId,
492 is_class_scope: bool,
493 is_place_name: bool,
494 previous_definitions: PreviousDefinitions,
495 can_be_shadowed: FutureDefinitions,
496 ) {
497 debug_assert_ne!(binding_id, ScopedDefinitionId::UNBOUND);
498 self.bindings.record_binding(
499 binding_id,
500 reachability_constraint,
501 is_class_scope,
502 is_place_name,
503 previous_definitions,
504 can_be_shadowed,
505 );
506 }
507
508 pub(super) fn record_narrowing_constraint(
510 &mut self,
511 narrowing_constraints: &mut NarrowingConstraintsBuilder,
512 constraint: ScopedNarrowingConstraint,
513 ) {
514 self.bindings
515 .record_narrowing_constraint(narrowing_constraints, constraint);
516 }
517
518 pub(super) fn record_narrowing_constraint_for_bindings_at_use(
520 &mut self,
521 narrowing_constraints: &mut NarrowingConstraintsBuilder,
522 constraint: ScopedNarrowingConstraint,
523 bindings_at_use: &Bindings,
524 ) {
525 for binding in &mut self.bindings.live_bindings {
526 if bindings_at_use
527 .iter()
528 .any(|binding_at_use| binding_at_use.binding() == binding.binding())
529 {
530 binding.narrowing_constraint = narrowing_constraints
531 .add_and_constraint(binding.narrowing_constraint, constraint);
532 }
533 }
534 }
535
536 pub(super) fn record_narrowing_constraint_for_bindings(
538 &mut self,
539 narrowing_constraints: &mut NarrowingConstraintsBuilder,
540 constraint: ScopedNarrowingConstraint,
541 bindings: &[ScopedDefinitionId],
542 ) {
543 for binding in &mut self.bindings.live_bindings {
544 if bindings.contains(&binding.binding()) {
545 binding.narrowing_constraint = narrowing_constraints
546 .add_and_constraint(binding.narrowing_constraint, constraint);
547 }
548 }
549 }
550
551 pub(super) fn record_reachability_constraint(
553 &mut self,
554 reachability_constraints: &mut ReachabilityConstraintsBuilder,
555 constraint: ScopedReachabilityConstraintId,
556 ) {
557 self.bindings
558 .record_reachability_constraint(reachability_constraints, constraint);
559 self.declarations
560 .record_reachability_constraint(reachability_constraints, constraint);
561 }
562
563 pub(super) fn record_declaration(
565 &mut self,
566 declaration_id: ScopedDefinitionId,
567 reachability_constraint: ScopedReachabilityConstraintId,
568 ) {
569 self.declarations.record_declaration(
570 declaration_id,
571 reachability_constraint,
572 PreviousDefinitions::AreShadowed,
573 );
574 }
575
576 pub(super) fn merge(
578 &mut self,
579 b: PlaceState,
580 narrowing_constraints: &mut NarrowingConstraintsBuilder,
581 reachability_constraints: &mut ReachabilityConstraintsBuilder,
582 ) {
583 self.bindings
584 .merge(b.bindings, narrowing_constraints, reachability_constraints);
585 self.declarations
586 .merge(b.declarations, reachability_constraints);
587 }
588
589 pub(super) fn bindings(&self) -> &Bindings {
590 &self.bindings
591 }
592
593 pub(super) fn declarations(&self) -> &Declarations {
594 &self.declarations
595 }
596
597 pub(super) fn into_parts(self) -> (Bindings, Declarations) {
598 (self.bindings, self.declarations)
599 }
600}
601
602#[cfg(test)]
603mod tests {
604 use super::*;
605 use ruff_index::Idx;
606
607 use crate::predicate::ScopedPredicateId;
608
609 #[track_caller]
610 fn assert_bindings(place: &PlaceState, expected: &[(u32, ScopedNarrowingConstraint)]) {
611 let actual: Vec<(u32, ScopedNarrowingConstraint)> = place
612 .bindings()
613 .iter()
614 .map(|live_binding| {
615 (
616 live_binding.binding().as_u32(),
617 live_binding.narrowing_constraint,
618 )
619 })
620 .collect();
621 assert_eq!(actual, expected);
622 }
623
624 #[track_caller]
625 fn assert_declarations(place: &PlaceState, expected: &[&str]) {
626 let actual = place
627 .declarations()
628 .iter()
629 .map(
630 |LiveDeclaration {
631 declaration,
632 reachability_constraint: _,
633 }| {
634 if *declaration == ScopedDefinitionId::UNBOUND {
635 "undeclared".into()
636 } else {
637 declaration.as_u32().to_string()
638 }
639 },
640 )
641 .collect::<Vec<_>>();
642 assert_eq!(actual, expected);
643 }
644
645 #[test]
646 fn unbound() {
647 let sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
648
649 assert_bindings(&sym, &[(0, ScopedNarrowingConstraint::ALWAYS_TRUE)]);
650 }
651
652 #[test]
653 fn with() {
654 let mut sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
655 sym.record_binding(
656 ScopedDefinitionId::from_u32(1),
657 ScopedReachabilityConstraintId::ALWAYS_TRUE,
658 false,
659 true,
660 PreviousDefinitions::AreShadowed,
661 FutureDefinitions::ShadowThisOne,
662 );
663
664 assert_bindings(&sym, &[(1, ScopedNarrowingConstraint::ALWAYS_TRUE)]);
665 }
666
667 #[test]
668 fn future_definitions_can_opt_out_of_shadowing() {
669 let mut sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
670 sym.record_binding(
671 ScopedDefinitionId::from_u32(1),
672 ScopedReachabilityConstraintId::ALWAYS_TRUE,
673 false,
674 true,
675 PreviousDefinitions::AreKept,
676 FutureDefinitions::DontShadowThisOne,
677 );
678 sym.record_binding(
679 ScopedDefinitionId::from_u32(2),
680 ScopedReachabilityConstraintId::ALWAYS_TRUE,
681 false,
682 true,
683 PreviousDefinitions::AreShadowed,
684 FutureDefinitions::ShadowThisOne,
685 );
686
687 assert_bindings(
688 &sym,
689 &[
690 (1, ScopedNarrowingConstraint::ALWAYS_TRUE),
691 (2, ScopedNarrowingConstraint::ALWAYS_TRUE),
692 ],
693 );
694
695 sym.record_binding(
696 ScopedDefinitionId::from_u32(3),
697 ScopedReachabilityConstraintId::ALWAYS_TRUE,
698 false,
699 true,
700 PreviousDefinitions::AreShadowed,
701 FutureDefinitions::ShadowThisOne,
702 );
703
704 assert_bindings(
705 &sym,
706 &[
707 (1, ScopedNarrowingConstraint::ALWAYS_TRUE),
708 (3, ScopedNarrowingConstraint::ALWAYS_TRUE),
709 ],
710 );
711 }
712
713 #[test]
714 fn record_constraint() {
715 let mut narrowing_constraints = NarrowingConstraintsBuilder::default();
716 let mut sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
717 sym.record_binding(
718 ScopedDefinitionId::from_u32(1),
719 ScopedReachabilityConstraintId::ALWAYS_TRUE,
720 false,
721 true,
722 PreviousDefinitions::AreShadowed,
723 FutureDefinitions::ShadowThisOne,
724 );
725 let atom = narrowing_constraints.add_atom(ScopedPredicateId::new(0));
726 sym.record_narrowing_constraint(&mut narrowing_constraints, atom);
727
728 assert_bindings(&sym, &[(1, atom)]);
729 }
730
731 #[test]
732 fn merge() {
733 let mut narrowing_constraints = NarrowingConstraintsBuilder::default();
734 let mut reachability_constraints = ReachabilityConstraintsBuilder::default();
735
736 let mut sym1a = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
738 sym1a.record_binding(
739 ScopedDefinitionId::from_u32(1),
740 ScopedReachabilityConstraintId::ALWAYS_TRUE,
741 false,
742 true,
743 PreviousDefinitions::AreShadowed,
744 FutureDefinitions::ShadowThisOne,
745 );
746 let atom0 = narrowing_constraints.add_atom(ScopedPredicateId::new(0));
747 sym1a.record_narrowing_constraint(&mut narrowing_constraints, atom0);
748
749 let mut sym1b = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
750 sym1b.record_binding(
751 ScopedDefinitionId::from_u32(1),
752 ScopedReachabilityConstraintId::ALWAYS_TRUE,
753 false,
754 true,
755 PreviousDefinitions::AreShadowed,
756 FutureDefinitions::ShadowThisOne,
757 );
758 sym1b.record_narrowing_constraint(&mut narrowing_constraints, atom0);
759
760 sym1a.merge(
761 sym1b,
762 &mut narrowing_constraints,
763 &mut reachability_constraints,
764 );
765 let mut sym1 = sym1a;
766 assert_bindings(&sym1, &[(1, atom0)]);
768
769 let mut sym2a = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
771 sym2a.record_binding(
772 ScopedDefinitionId::from_u32(2),
773 ScopedReachabilityConstraintId::ALWAYS_TRUE,
774 false,
775 true,
776 PreviousDefinitions::AreShadowed,
777 FutureDefinitions::ShadowThisOne,
778 );
779 let atom1 = narrowing_constraints.add_atom(ScopedPredicateId::new(1));
780 sym2a.record_narrowing_constraint(&mut narrowing_constraints, atom1);
781
782 let mut sym1b = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
783 sym1b.record_binding(
784 ScopedDefinitionId::from_u32(2),
785 ScopedReachabilityConstraintId::ALWAYS_TRUE,
786 false,
787 true,
788 PreviousDefinitions::AreShadowed,
789 FutureDefinitions::ShadowThisOne,
790 );
791 let atom2 = narrowing_constraints.add_atom(ScopedPredicateId::new(2));
792 sym1b.record_narrowing_constraint(&mut narrowing_constraints, atom2);
793
794 sym2a.merge(
795 sym1b,
796 &mut narrowing_constraints,
797 &mut reachability_constraints,
798 );
799 let sym2 = sym2a;
800 let merged_constraint = sym2.bindings().iter().next().unwrap().narrowing_constraint;
802 assert_ne!(merged_constraint, ScopedNarrowingConstraint::ALWAYS_TRUE);
803 assert_ne!(merged_constraint, ScopedNarrowingConstraint::ALWAYS_FALSE);
804 assert_ne!(merged_constraint, atom1);
805 assert_ne!(merged_constraint, atom2);
806
807 let mut sym3a = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
809 sym3a.record_binding(
810 ScopedDefinitionId::from_u32(3),
811 ScopedReachabilityConstraintId::ALWAYS_TRUE,
812 false,
813 true,
814 PreviousDefinitions::AreShadowed,
815 FutureDefinitions::ShadowThisOne,
816 );
817 let atom3 = narrowing_constraints.add_atom(ScopedPredicateId::new(3));
818 sym3a.record_narrowing_constraint(&mut narrowing_constraints, atom3);
819
820 let sym2b = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
821
822 sym3a.merge(
823 sym2b,
824 &mut narrowing_constraints,
825 &mut reachability_constraints,
826 );
827 let sym3 = sym3a;
828 let bindings: Vec<_> = sym3
829 .bindings()
830 .iter()
831 .map(|b| (b.binding().as_u32(), b.narrowing_constraint))
832 .collect();
833 assert_eq!(bindings.len(), 2);
834 assert_eq!(bindings[0].0, 0); assert_eq!(bindings[1].0, 3);
836 assert_eq!(bindings[1].1, atom3);
837
838 sym1.merge(
840 sym3,
841 &mut narrowing_constraints,
842 &mut reachability_constraints,
843 );
844 let sym = sym1;
845 let bindings: Vec<_> = sym
846 .bindings()
847 .iter()
848 .map(|b| (b.binding().as_u32(), b.narrowing_constraint))
849 .collect();
850 assert_eq!(bindings.len(), 3);
851 assert_eq!(bindings[0].0, 0); assert_eq!(bindings[1].0, 1);
853 assert_eq!(bindings[1].1, atom0);
854 assert_eq!(bindings[2].0, 3);
855 assert_eq!(bindings[2].1, atom3);
856 }
857
858 #[test]
859 fn no_declaration() {
860 let sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
861
862 assert_declarations(&sym, &["undeclared"]);
863 }
864
865 #[test]
866 fn record_declaration() {
867 let mut sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
868 sym.record_declaration(
869 ScopedDefinitionId::from_u32(1),
870 ScopedReachabilityConstraintId::ALWAYS_TRUE,
871 );
872
873 assert_declarations(&sym, &["1"]);
874 }
875
876 #[test]
877 fn record_declaration_override() {
878 let mut sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
879 sym.record_declaration(
880 ScopedDefinitionId::from_u32(1),
881 ScopedReachabilityConstraintId::ALWAYS_TRUE,
882 );
883 sym.record_declaration(
884 ScopedDefinitionId::from_u32(2),
885 ScopedReachabilityConstraintId::ALWAYS_TRUE,
886 );
887
888 assert_declarations(&sym, &["2"]);
889 }
890
891 #[test]
892 fn record_declaration_merge() {
893 let mut narrowing_constraints = NarrowingConstraintsBuilder::default();
894 let mut reachability_constraints = ReachabilityConstraintsBuilder::default();
895 let mut sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
896 sym.record_declaration(
897 ScopedDefinitionId::from_u32(1),
898 ScopedReachabilityConstraintId::ALWAYS_TRUE,
899 );
900
901 let mut sym2 = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
902 sym2.record_declaration(
903 ScopedDefinitionId::from_u32(2),
904 ScopedReachabilityConstraintId::ALWAYS_TRUE,
905 );
906
907 sym.merge(
908 sym2,
909 &mut narrowing_constraints,
910 &mut reachability_constraints,
911 );
912
913 assert_declarations(&sym, &["1", "2"]);
914 }
915
916 #[test]
917 fn record_declaration_merge_partial_undeclared() {
918 let mut narrowing_constraints = NarrowingConstraintsBuilder::default();
919 let mut reachability_constraints = ReachabilityConstraintsBuilder::default();
920 let mut sym = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
921 sym.record_declaration(
922 ScopedDefinitionId::from_u32(1),
923 ScopedReachabilityConstraintId::ALWAYS_TRUE,
924 );
925
926 let sym2 = PlaceState::undefined(ScopedReachabilityConstraintId::ALWAYS_TRUE);
927
928 sym.merge(
929 sym2,
930 &mut narrowing_constraints,
931 &mut reachability_constraints,
932 );
933
934 assert_declarations(&sym, &["undeclared", "1"]);
935 }
936}