Skip to main content

rustc_abi/
wrapping_range.rs

1use std::ops::RangeFull;
2use std::{fmt, iter};
3
4use crate::Size;
5#[cfg(feature = "nightly")]
6use crate::StableHash;
7
8/// Inclusive wrap-around range of valid values, that is, if
9/// start > end, it represents `start..=MAX`, followed by `0..=end`.
10///
11/// That is, for an i8 primitive, a range of `254..=2` means following
12/// sequence:
13///
14///    254 (-2), 255 (-1), 0, 1, 2
15///
16/// This is intended specifically to mirror LLVM’s `!range` metadata semantics.
17#[derive(#[automatically_derived]
impl ::core::clone::Clone for WrappingRange {
    #[inline]
    fn clone(&self) -> WrappingRange {
        let _: ::core::clone::AssertParamIsClone<u128>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for WrappingRange { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for WrappingRange {
    #[inline]
    fn eq(&self, other: &WrappingRange) -> bool {
        self.start == other.start && self.end == other.end
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for WrappingRange {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<u128>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for WrappingRange {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.start, state);
        ::core::hash::Hash::hash(&self.end, state)
    }
}Hash)]
18#[cfg_attr(feature = "nightly", derive(const _: () =
    {
        impl ::rustc_data_structures::stable_hash::StableHash for
            WrappingRange {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __hasher:
                    &mut ::rustc_data_structures::stable_hash::StableHasher) {
                match *self {
                    WrappingRange { start: ref __binding_0, end: ref __binding_1
                        } => {
                        { __binding_0.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                    }
                }
            }
        }
    };StableHash))]
19pub struct WrappingRange {
20    pub start: u128,
21    pub end: u128,
22}
23
24impl WrappingRange {
25    pub(crate) fn debug_as(&self, size: Size, is_signed: bool) -> impl fmt::Debug {
26        let range = *self;
27        fmt::from_fn(move |f| {
28            if range == WrappingRange::full(size) {
29                // This is intentionally not using `is_full_for` so that we ensure
30                // different values always debug-print differently.
31                // We don't need the full details when it's the canonical full range,
32                // but if one is looking at the debug output it might be that seeing
33                // `u8 is (..=0) | (1..)` instead of `u8 is ..` is the information
34                // you needed because the problem is that despite being *a* full
35                // range it's not *the* canonical one you expected it was.
36                f.write_str("..")
37            } else if is_signed {
38                let start = size.sign_extend(range.start);
39                let end = size.sign_extend(range.end);
40                if start > end {
41                    f.write_fmt(format_args!("(..={0}) | ({1}..)", end, start))write!(f, "(..={}) | ({}..)", end, start)
42                } else {
43                    f.write_fmt(format_args!("{0}..={1}", start, end))write!(f, "{}..={}", start, end)
44                }
45            } else {
46                f.write_fmt(format_args!("{0:?}", range))write!(f, "{:?}", range)
47            }
48        })
49    }
50
51    pub fn full(size: Size) -> Self {
52        Self { start: 0, end: size.unsigned_int_max() }
53    }
54
55    /// Returns `true` if `v` is contained in the range.
56    #[inline(always)]
57    pub fn contains(&self, v: u128) -> bool {
58        if self.start <= self.end {
59            self.start <= v && v <= self.end
60        } else {
61            self.start <= v || v <= self.end
62        }
63    }
64
65    /// Returns `true` if all the values in `other` are contained in this range,
66    /// when the values are considered as having width `size`.
67    #[inline(always)]
68    pub fn contains_range(&self, other: Self, size: Size) -> bool {
69        if self.is_full_for(size) {
70            true
71        } else {
72            let trunc = |x| size.truncate(x);
73
74            let delta = self.start;
75            let max = trunc(self.end.wrapping_sub(delta));
76
77            let other_start = trunc(other.start.wrapping_sub(delta));
78            let other_end = trunc(other.end.wrapping_sub(delta));
79
80            // Having shifted both input ranges by `delta`, now we only need to check
81            // whether `0..=max` contains `other_start..=other_end`, which can only
82            // happen if the other doesn't wrap since `self` isn't everything.
83            (other_start <= other_end) && (other_end <= max)
84        }
85    }
86
87    /// Returns `self` with replaced `start`
88    #[inline(always)]
89    pub(crate) fn with_start(mut self, start: u128) -> Self {
90        self.start = start;
91        self
92    }
93
94    /// Returns `self` with replaced `end`
95    #[inline(always)]
96    pub(crate) fn with_end(mut self, end: u128) -> Self {
97        self.end = end;
98        self
99    }
100
101    /// The wrapping distance from `self.start` to `self.end`.
102    fn width(&self, size: Size) -> u128 {
103        size.truncate(u128::wrapping_sub(self.end, self.start))
104    }
105
106    /// Returns `true` if `size` completely fills the range.
107    ///
108    /// Note that this is *not* the same as `self == WrappingRange::full(size)`.
109    /// Niche calculations can produce full ranges which are not the canonical one;
110    /// for example `Option<NonZero<u16>>` gets `valid_range: (..=0) | (1..)`.
111    #[inline]
112    pub fn is_full_for(&self, size: Size) -> bool {
113        let max_value = size.unsigned_int_max();
114        if true {
    if !(self.start <= max_value && self.end <= max_value) {
        ::core::panicking::panic("assertion failed: self.start <= max_value && self.end <= max_value")
    };
};debug_assert!(self.start <= max_value && self.end <= max_value);
115        self.start == (self.end.wrapping_add(1) & max_value)
116    }
117
118    /// Checks whether this range is considered non-wrapping when the values are
119    /// interpreted as *unsigned* numbers of width `size`.
120    ///
121    /// Returns `Ok(true)` if there's no wrap-around, `Ok(false)` if there is,
122    /// and `Err(..)` if the range is full so it depends how you think about it.
123    #[inline]
124    pub fn no_unsigned_wraparound(&self, size: Size) -> Result<bool, RangeFull> {
125        if self.is_full_for(size) { Err(..) } else { Ok(self.start <= self.end) }
126    }
127
128    /// Checks whether this range is considered non-wrapping when the values are
129    /// interpreted as *signed* numbers of width `size`.
130    ///
131    /// This is heavily dependent on the `size`, as `100..=200` does wrap when
132    /// interpreted as `i8`, but doesn't when interpreted as `i16`.
133    ///
134    /// Returns `Ok(true)` if there's no wrap-around, `Ok(false)` if there is,
135    /// and `Err(..)` if the range is full so it depends how you think about it.
136    #[inline]
137    pub fn no_signed_wraparound(&self, size: Size) -> Result<bool, RangeFull> {
138        if self.is_full_for(size) {
139            Err(..)
140        } else {
141            let start: i128 = size.sign_extend(self.start);
142            let end: i128 = size.sign_extend(self.end);
143            Ok(start <= end)
144        }
145    }
146
147    /// Returns a `WrappingRange` that contains all of the values from the iterator,
148    /// when they're treated as values `size` wide.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use rustc_abi::{Size, WrappingRange};
154    ///
155    /// let chain = std::iter::chain(10..20, 30..40);
156    /// let range = WrappingRange::smallest_range_containing(chain, Size::from_bytes(2));
157    /// assert_eq!(range.unwrap(), WrappingRange { start: 10, end: 39 });
158    ///
159    /// // Values don't need to be sorted nor unique
160    /// let range = WrappingRange::smallest_range_containing([3, 5, 3, 1, 3], Size::from_bytes(2));
161    /// assert_eq!(range.unwrap(), WrappingRange { start: 1, end: 5 });
162    ///
163    /// // The size matters because it changes where the wrapping can happen:
164    /// let range = WrappingRange::smallest_range_containing([1, 254], Size::from_bytes(1));
165    /// assert_eq!(range.unwrap(), WrappingRange { start: 254, end: 1 });
166    /// let range = WrappingRange::smallest_range_containing([1, 254], Size::from_bytes(4));
167    /// assert_eq!(range.unwrap(), WrappingRange { start: 1, end: 254 });
168    /// ```
169    pub fn smallest_range_containing(
170        values: impl IntoIterator<Item = u128>,
171        size: Size,
172    ) -> Option<Self> {
173        let mut values: Vec<_> = values.into_iter().collect();
174
175        let (min, max) = values
176            .iter()
177            .copied()
178            .map(|x| (x, x))
179            .reduce(|a, b| (u128::min(a.0, b.0), u128::max(a.1, b.1)))?;
180
181        // Just checking the max is enough to ensure that everything is in-range.
182        if !(max <= size.unsigned_int_max()) {
    {
        ::core::panicking::panic_fmt(format_args!("Value {0:?} is too big for {1:?}",
                max, size));
    }
};assert!(max <= size.unsigned_int_max(), "Value {max:?} is too big for {size:?}");
183
184        // The simple answer is the non-wraparound range `min..=max`.
185        let obvious_range = WrappingRange { start: min, end: max };
186
187        // It's very common that the input was only small non-negative integers and the
188        // obvious range turns out to be the best we can do.
189        // Every possible wraparound range must include at least `(...=min) | (max..)`,
190        // so if what we found already is at least that good, just use it.
191        //     WR(min, max).width() ≤ WR(max, min).width()
192        //         (max - min) % 2ⁿ ≤ (min - max) % 2ⁿ
193        //                max - min ≤ min - max + 2ⁿ
194        //            2×(max - min) ≤ 2ⁿ
195        //                max - min ≤ 2ⁿ⁻¹
196        let half_range = 1 << (size.bits() - 1);
197        if max - min <= half_range {
198            return Some(obvious_range);
199        }
200
201        // But every `[.., end, start, ..]` is also a potential candidate for a wraparound
202        // range `(..=end) | (start..)`, so long as `start` and `end` aren't duplicates.
203        values.sort_unstable();
204        let wraparound_ranges = values
205            .array_windows::<2>()
206            .filter_map(|&[end, start]| (start != end).then_some(WrappingRange { start, end }));
207
208        // Pick whichever range is smallest. By putting the non-wraparound range first,
209        // it'll be preferred over a wraparound range with the same width.
210        iter::chain(iter::once(obvious_range), wraparound_ranges).min_by_key(|r| r.width(size))
211    }
212}
213
214impl fmt::Debug for WrappingRange {
215    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
216        if self.start > self.end {
217            fmt.write_fmt(format_args!("(..={0}) | ({1}..)", self.end, self.start))write!(fmt, "(..={}) | ({}..)", self.end, self.start)?;
218        } else {
219            fmt.write_fmt(format_args!("{0}..={1}", self.start, self.end))write!(fmt, "{}..={}", self.start, self.end)?;
220        }
221        Ok(())
222    }
223}