diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index 347c885602256..3592fb33077d9 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -1,7 +1,7 @@ use std::iter::Step; use std::marker::PhantomData; -use std::ops::Bound; use std::ops::RangeBounds; +use std::ops::{Bound, Range}; use crate::vec::Idx; use crate::vec::IndexVec; @@ -11,6 +11,10 @@ use smallvec::SmallVec; mod tests; /// Stores a set of intervals on the indices. +/// +/// The elements in `map` are sorted and non-adjacent, which means +/// the second value of the previous element is *greater* than the +/// first value of the following element. #[derive(Debug, Clone)] pub struct IntervalSet { // Start, end @@ -84,7 +88,7 @@ impl IntervalSet { // continue to the next range. We're looking here for the first // range which starts *non-adjacently* to our end. let next = self.map.partition_point(|r| r.0 <= end + 1); - if let Some(right) = next.checked_sub(1) { + let result = if let Some(right) = next.checked_sub(1) { let (prev_start, prev_end) = self.map[right]; if prev_end + 1 >= start { // If the start for the inserted range is adjacent to the @@ -99,7 +103,7 @@ impl IntervalSet { if left != right { self.map.drain(left..right); } - return true; + true } else { // We overlap with the previous range, increase it to // include us. @@ -107,17 +111,17 @@ impl IntervalSet { // Make sure we're actually going to *increase* it though -- // it may be that end is just inside the previously existing // set. - return if end > prev_end { + if end > prev_end { self.map[right].1 = end; true } else { false - }; + } } } else { // Otherwise, we don't overlap, so just insert self.map.insert(right + 1, (start, end)); - return true; + true } } else { if self.map.is_empty() { @@ -127,8 +131,16 @@ impl IntervalSet { } else { self.map.insert(next, (start, end)); } - return true; - } + true + }; + debug_assert!( + self.check_invariants(), + "wrong intervals after insert {:?}..={:?} to {:?}", + start, + end, + self + ); + result } pub fn contains(&self, needle: I) -> bool { @@ -145,9 +157,26 @@ impl IntervalSet { where I: Step, { - // FIXME: Performance here is probably not great. We will be doing a lot - // of pointless tree traversals. - other.iter().all(|elem| self.contains(elem)) + let mut sup_iter = self.iter_intervals(); + let mut current = None; + let contains = |sup: Range, sub: Range, current: &mut Option>| { + if sup.end < sub.start { + // if `sup.end == sub.start`, the next sup doesn't contain `sub.start` + None // continue to the next sup + } else if sup.end >= sub.end && sup.start <= sub.start { + *current = Some(sup); // save the current sup + Some(true) + } else { + Some(false) + } + }; + other.iter_intervals().all(|sub| { + current + .take() + .and_then(|sup| contains(sup, sub.clone(), &mut current)) + .or_else(|| sup_iter.find_map(|sup| contains(sup, sub.clone(), &mut current))) + .unwrap_or(false) + }) } pub fn is_empty(&self) -> bool { @@ -174,7 +203,10 @@ impl IntervalSet { pub fn insert_all(&mut self) { self.clear(); - self.map.push((0, self.domain.try_into().unwrap())); + if let Some(end) = self.domain.checked_sub(1) { + self.map.push((0, end.try_into().unwrap())); + } + debug_assert!(self.check_invariants()); } pub fn union(&mut self, other: &IntervalSet) -> bool @@ -186,8 +218,21 @@ impl IntervalSet { for range in other.iter_intervals() { did_insert |= self.insert_range(range); } + debug_assert!(self.check_invariants()); did_insert } + + // Check the intervals are valid, sorted and non-adjacent + fn check_invariants(&self) -> bool { + let mut current: Option = None; + for (start, end) in &self.map { + if start > end || current.map_or(false, |x| x + 1 >= *start) { + return false; + } + current = Some(*end); + } + current.map_or(true, |x| x < self.domain as u32) + } } /// This data structure optimizes for cases where the stored bits in each row diff --git a/compiler/rustc_index/src/interval/tests.rs b/compiler/rustc_index/src/interval/tests.rs index d90b449f32609..375af60f66207 100644 --- a/compiler/rustc_index/src/interval/tests.rs +++ b/compiler/rustc_index/src/interval/tests.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn insert_collapses() { - let mut set = IntervalSet::::new(3000); + let mut set = IntervalSet::::new(10000); set.insert_range(9831..=9837); set.insert_range(43..=9830); assert_eq!(set.iter_intervals().collect::>(), [43..9838]);