Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8f9cd3d

Browse files
committed
Rework range splitting api
1 parent b0889cb commit 8f9cd3d

File tree

1 file changed

+90
-112
lines changed

1 file changed

+90
-112
lines changed

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

Lines changed: 90 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
//!
4040
//! Splitting is implemented in the [`Constructor::split`] function. We don't do splitting for
4141
//! or-patterns; instead we just try the alternatives one-by-one. For details on splitting
42-
//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`SplitIntRange`]; for slices, see
42+
//! wildcards, see [`SplitWildcard`]; for integer ranges, see [`IntRange::split`]; for slices, see
4343
//! [`SplitVarLenSlice`].
4444
4545
use std::cell::Cell;
@@ -186,6 +186,93 @@ impl IntRange {
186186
(lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton()
187187
}
188188

189+
/// See `Constructor::is_covered_by`
190+
fn is_covered_by(&self, other: &Self) -> bool {
191+
if self.intersection(other).is_some() {
192+
// Constructor splitting should ensure that all intersections we encounter are actually
193+
// inclusions.
194+
assert!(self.is_subrange(other));
195+
true
196+
} else {
197+
false
198+
}
199+
}
200+
201+
/// Partition a range of integers into disjoint subranges. This does constructor splitting for
202+
/// integer ranges as explained at the top of the file.
203+
///
204+
/// This returns an output that covers `self`. The output is split so that the only
205+
/// intersections between an output range and a column range are inclusions. No output range
206+
/// straddles the boundary of one of the inputs.
207+
///
208+
/// The following input:
209+
/// ```text
210+
/// |-------------------------| // `self`
211+
/// |------| |----------| |----|
212+
/// |-------| |-------|
213+
/// ```
214+
/// would be iterated over as follows:
215+
/// ```text
216+
/// ||---|--||-|---|---|---|--|
217+
/// ```
218+
fn split(
219+
&self,
220+
column_ranges: impl Iterator<Item = IntRange>,
221+
) -> impl Iterator<Item = IntRange> {
222+
/// Represents a boundary between 2 integers. Because the intervals spanning boundaries must be
223+
/// able to cover every integer, we need to be able to represent 2^128 + 1 such boundaries.
224+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
225+
enum IntBoundary {
226+
JustBefore(u128),
227+
AfterMax,
228+
}
229+
230+
fn unpack_intrange(range: IntRange) -> [IntBoundary; 2] {
231+
use IntBoundary::*;
232+
let (lo, hi) = range.boundaries();
233+
let lo = JustBefore(lo);
234+
let hi = match hi.checked_add(1) {
235+
Some(m) => JustBefore(m),
236+
None => AfterMax,
237+
};
238+
[lo, hi]
239+
}
240+
241+
// The boundaries of ranges in `column_ranges` intersected with `self`.
242+
let mut boundaries: Vec<IntBoundary> = column_ranges
243+
.filter_map(|r| self.intersection(&r))
244+
.map(unpack_intrange)
245+
.flat_map(|[lo, hi]| [lo, hi])
246+
.collect();
247+
boundaries.sort_unstable();
248+
249+
let [self_start, self_end] = unpack_intrange(self.clone());
250+
// Gather pairs of adjacent boundaries.
251+
let mut prev_bdy = self_start;
252+
boundaries
253+
.into_iter()
254+
// End with the end of the range.
255+
.chain(once(self_end))
256+
// List pairs of adjacent boundaries.
257+
.map(move |bdy| {
258+
let ret = (prev_bdy, bdy);
259+
prev_bdy = bdy;
260+
ret
261+
})
262+
// Skip duplicates.
263+
.filter(|&(prev_bdy, bdy)| prev_bdy != bdy)
264+
// Convert back to ranges.
265+
.map(move |(prev_bdy, bdy)| {
266+
use IntBoundary::*;
267+
let range = match (prev_bdy, bdy) {
268+
(JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
269+
(JustBefore(n), AfterMax) => n..=u128::MAX,
270+
_ => unreachable!(), // Ruled out by the sorting and filtering we did
271+
};
272+
IntRange { range }
273+
})
274+
}
275+
189276
/// Only used for displaying the range properly.
190277
fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
191278
let (lo, hi) = self.boundaries();
@@ -254,18 +341,6 @@ impl IntRange {
254341
);
255342
}
256343
}
257-
258-
/// See `Constructor::is_covered_by`
259-
fn is_covered_by(&self, other: &Self) -> bool {
260-
if self.intersection(other).is_some() {
261-
// Constructor splitting should ensure that all intersections we encounter are actually
262-
// inclusions.
263-
assert!(self.is_subrange(other));
264-
true
265-
} else {
266-
false
267-
}
268-
}
269344
}
270345

271346
/// Note: this is often not what we want: e.g. `false` is converted into the range `0..=0` and
@@ -279,101 +354,6 @@ impl fmt::Debug for IntRange {
279354
}
280355
}
281356

282-
/// Represents a border between 2 integers. Because the intervals spanning borders must be able to
283-
/// cover every integer, we need to be able to represent 2^128 + 1 such borders.
284-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
285-
enum IntBorder {
286-
JustBefore(u128),
287-
AfterMax,
288-
}
289-
290-
/// A range of integers that is partitioned into disjoint subranges. This does constructor
291-
/// splitting for integer ranges as explained at the top of the file.
292-
///
293-
/// This is fed multiple ranges, and returns an output that covers the input, but is split so that
294-
/// the only intersections between an output range and a seen range are inclusions. No output range
295-
/// straddles the boundary of one of the inputs.
296-
///
297-
/// The following input:
298-
/// ```text
299-
/// |-------------------------| // `self`
300-
/// |------| |----------| |----|
301-
/// |-------| |-------|
302-
/// ```
303-
/// would be iterated over as follows:
304-
/// ```text
305-
/// ||---|--||-|---|---|---|--|
306-
/// ```
307-
#[derive(Debug, Clone)]
308-
struct SplitIntRange {
309-
/// The range we are splitting
310-
range: IntRange,
311-
/// The borders of ranges we have seen. They are all contained within `range`. This is kept
312-
/// sorted.
313-
borders: Vec<IntBorder>,
314-
}
315-
316-
impl SplitIntRange {
317-
fn new(range: IntRange) -> Self {
318-
SplitIntRange { range, borders: Vec::new() }
319-
}
320-
321-
/// Internal use
322-
fn to_borders(r: IntRange) -> [IntBorder; 2] {
323-
use IntBorder::*;
324-
let (lo, hi) = r.boundaries();
325-
let lo = JustBefore(lo);
326-
let hi = match hi.checked_add(1) {
327-
Some(m) => JustBefore(m),
328-
None => AfterMax,
329-
};
330-
[lo, hi]
331-
}
332-
333-
/// Add ranges relative to which we split.
334-
fn split(&mut self, ranges: impl Iterator<Item = IntRange>) {
335-
let this_range = &self.range;
336-
let included_ranges = ranges.filter_map(|r| this_range.intersection(&r));
337-
let included_borders = included_ranges.flat_map(|r| {
338-
let borders = Self::to_borders(r);
339-
once(borders[0]).chain(once(borders[1]))
340-
});
341-
self.borders.extend(included_borders);
342-
self.borders.sort_unstable();
343-
}
344-
345-
/// Iterate over the contained ranges.
346-
fn iter(&self) -> impl Iterator<Item = IntRange> + Captures<'_> {
347-
use IntBorder::*;
348-
349-
let self_range = Self::to_borders(self.range.clone());
350-
// Start with the start of the range.
351-
let mut prev_border = self_range[0];
352-
self.borders
353-
.iter()
354-
.copied()
355-
// End with the end of the range.
356-
.chain(once(self_range[1]))
357-
// List pairs of adjacent borders.
358-
.map(move |border| {
359-
let ret = (prev_border, border);
360-
prev_border = border;
361-
ret
362-
})
363-
// Skip duplicates.
364-
.filter(|(prev_border, border)| prev_border != border)
365-
// Finally, convert to ranges.
366-
.map(move |(prev_border, border)| {
367-
let range = match (prev_border, border) {
368-
(JustBefore(n), JustBefore(m)) if n < m => n..=(m - 1),
369-
(JustBefore(n), AfterMax) => n..=u128::MAX,
370-
_ => unreachable!(), // Ruled out by the sorting and filtering we did
371-
};
372-
IntRange { range }
373-
})
374-
}
375-
}
376-
377357
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
378358
enum SliceKind {
379359
/// Patterns of length `n` (`[x, y]`).
@@ -733,10 +713,8 @@ impl<'tcx> Constructor<'tcx> {
733713
// Fast-track if the range is trivial. In particular, we don't do the overlapping
734714
// ranges check.
735715
IntRange(ctor_range) if !ctor_range.is_singleton() => {
736-
let mut split_range = SplitIntRange::new(ctor_range.clone());
737-
let int_ranges = ctors.filter_map(|ctor| ctor.as_int_range());
738-
split_range.split(int_ranges.cloned());
739-
split_range.iter().map(IntRange).collect()
716+
let int_ranges = ctors.filter_map(|ctor| ctor.as_int_range()).cloned();
717+
ctor_range.split(int_ranges).map(IntRange).collect()
740718
}
741719
&Slice(Slice { kind: VarLen(self_prefix, self_suffix), array_len }) => {
742720
let mut split_self = SplitVarLenSlice::new(self_prefix, self_suffix, array_len);

0 commit comments

Comments
 (0)