@@ -24,7 +24,7 @@ use rustc_target::abi::{Integer, Size, VariantIdx};
2424
2525use smallvec:: { smallvec, SmallVec } ;
2626use std:: cmp:: { self , max, min, Ordering } ;
27- use std:: iter:: IntoIterator ;
27+ use std:: iter:: { once , IntoIterator } ;
2828use std:: ops:: RangeInclusive ;
2929
3030/// An inclusive interval, used for precise integer exhaustiveness checking.
@@ -183,77 +183,24 @@ impl IntRange {
183183 Pat { ty, span : DUMMY_SP , kind : Box :: new ( kind) }
184184 }
185185
186- /// For exhaustive integer matching, some constructors are grouped within other constructors
187- /// (namely integer typed values are grouped within ranges). However, when specialising these
188- /// constructors, we want to be specialising for the underlying constructors (the integers), not
189- /// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would
190- /// mean creating a separate constructor for every single value in the range, which is clearly
191- /// impractical. However, observe that for some ranges of integers, the specialisation will be
192- /// identical across all values in that range (i.e., there are equivalence classes of ranges of
193- /// constructors based on their `U(S(c, P), S(c, p))` outcome). These classes are grouped by
194- /// the patterns that apply to them (in the matrix `P`). We can split the range whenever the
195- /// patterns that apply to that range (specifically: the patterns that *intersect* with that range)
196- /// change.
197- /// Our solution, therefore, is to split the range constructor into subranges at every single point
198- /// the group of intersecting patterns changes (using the method described below).
199- /// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching
200- /// on actual integers. The nice thing about this is that the number of subranges is linear in the
201- /// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't
202- /// need to be worried about matching over gargantuan ranges.
203- ///
204- /// Essentially, given the first column of a matrix representing ranges, looking like the following:
205- ///
206- /// |------| |----------| |-------| ||
207- /// |-------| |-------| |----| ||
208- /// |---------|
209- ///
210- /// We split the ranges up into equivalence classes so the ranges are no longer overlapping:
211- ///
212- /// |--|--|||-||||--||---|||-------| |-|||| ||
213- ///
214- /// The logic for determining how to split the ranges is fairly straightforward: we calculate
215- /// boundaries for each interval range, sort them, then create constructors for each new interval
216- /// between every pair of boundary points. (This essentially sums up to performing the intuitive
217- /// merging operation depicted above.)
186+ /// Split this range, as described at the top of the file.
218187 fn split < ' p , ' tcx > (
219188 & self ,
220189 pcx : PatCtxt < ' _ , ' p , ' tcx > ,
221190 hir_id : Option < HirId > ,
222191 ) -> SmallVec < [ Constructor < ' tcx > ; 1 ] > {
223- /// Represents a border between 2 integers. Because the intervals spanning borders
224- /// must be able to cover every integer, we need to be able to represent
225- /// 2^128 + 1 such borders.
226- #[ derive( Clone , Copy , PartialEq , Eq , PartialOrd , Ord , Debug ) ]
227- enum Border {
228- JustBefore ( u128 ) ,
229- AfterMax ,
230- }
231-
232- // A function for extracting the borders of an integer interval.
233- fn range_borders ( r : IntRange ) -> impl Iterator < Item = Border > {
234- let ( lo, hi) = r. range . into_inner ( ) ;
235- let from = Border :: JustBefore ( lo) ;
236- let to = match hi. checked_add ( 1 ) {
237- Some ( m) => Border :: JustBefore ( m) ,
238- None => Border :: AfterMax ,
239- } ;
240- vec ! [ from, to] . into_iter ( )
241- }
242-
243- // Collect the span and range of all the intersecting ranges to lint on likely
244- // incorrect range patterns. (#63987)
192+ // We collect the span and range of all the intersecting ranges to lint on likely incorrect
193+ // range patterns. (#63987)
245194 let mut overlaps = vec ! [ ] ;
195+ let mut split_range = SplitIntRange :: new ( self . clone ( ) ) ;
246196 let row_len = pcx. matrix . column_count ( ) . unwrap_or ( 0 ) ;
247- // `borders` is the set of borders between equivalence classes: each equivalence
248- // class lies between 2 borders.
249- let row_borders = pcx
197+ let intranges = pcx
250198 . matrix
251199 . head_ctors_and_spans ( pcx. cx )
252- . filter_map ( |( ctor, span) | Some ( ( ctor. as_int_range ( ) ?, span) ) )
253- . filter_map ( |( range, span) | {
254- let intersection = self . intersection ( & range) ;
255- let should_lint = self . suspicious_intersection ( & range) ;
256- if let ( Some ( range) , 1 , true ) = ( & intersection, row_len, should_lint) {
200+ . filter_map ( |( ctor, span) | Some ( ( ctor. as_int_range ( ) ?, span) ) ) ;
201+ let intranges = intranges. inspect ( |( range, span) | {
202+ if let Some ( intersection) = self . intersection ( & range) {
203+ if row_len == 1 && self . suspicious_intersection ( & range) {
257204 // FIXME: for now, only check for overlapping ranges on simple range
258205 // patterns. Otherwise with the current logic the following is detected
259206 // as overlapping:
@@ -264,36 +211,15 @@ impl IntRange {
264211 // _ => {}
265212 // }
266213 // ```
267- overlaps. push ( ( range . clone ( ) , span) ) ;
214+ overlaps. push ( ( intersection . clone ( ) , * span) ) ;
268215 }
269- intersection
270- } )
271- . flat_map ( range_borders) ;
272- let self_borders = range_borders ( self . clone ( ) ) ;
273- let mut borders: Vec < _ > = row_borders. chain ( self_borders) . collect ( ) ;
274- borders. sort_unstable ( ) ;
216+ }
217+ } ) ;
218+ split_range. split ( intranges. map ( |( range, _) | range) . cloned ( ) ) ;
275219
276220 self . lint_overlapping_range_endpoints ( pcx, hir_id, overlaps) ;
277221
278- // We're going to iterate through every adjacent pair of borders, making sure that
279- // each represents an interval of nonnegative length, and convert each such
280- // interval into a constructor.
281- borders
282- . array_windows ( )
283- . filter_map ( |& pair| match pair {
284- [ Border :: JustBefore ( n) , Border :: JustBefore ( m) ] => {
285- if n < m {
286- Some ( n..=( m - 1 ) )
287- } else {
288- None
289- }
290- }
291- [ Border :: JustBefore ( n) , Border :: AfterMax ] => Some ( n..=u128:: MAX ) ,
292- [ Border :: AfterMax , _] => None ,
293- } )
294- . map ( |range| IntRange { range } )
295- . map ( IntRange )
296- . collect ( )
222+ split_range. iter ( ) . map ( IntRange ) . collect ( )
297223 }
298224
299225 fn lint_overlapping_range_endpoints (
@@ -339,6 +265,101 @@ impl IntRange {
339265 }
340266}
341267
268+ /// Represents a border between 2 integers. Because the intervals spanning borders must be able to
269+ /// cover every integer, we need to be able to represent 2^128 + 1 such borders.
270+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
271+ enum IntBorder {
272+ JustBefore ( u128 ) ,
273+ AfterMax ,
274+ }
275+
276+ /// A range of integers that is partitioned into disjoint subranges.
277+ ///
278+ /// This is fed an input of multiple ranges, and returns an output that covers the union of the
279+ /// inputs but is split so that an output range only intersects an input range by being a subrange
280+ /// of it. No output range straddles the boundary of one of the inputs. This does constructor
281+ /// splitting for integer ranges as explained at the top of the file.
282+ ///
283+ /// The following input:
284+ /// ```
285+ /// |-------------------------| // `self`
286+ /// |------| |----------| |----|
287+ /// |-------| |-------|
288+ /// ```
289+ /// would be iterated over as follows:
290+ /// ```
291+ /// ||---|--||-|---|---|---|--|
292+ /// ```
293+ #[ derive( Debug , Clone ) ]
294+ struct SplitIntRange {
295+ /// The range we are splitting
296+ range : IntRange ,
297+ /// The borders of ranges we have seen. They are all contained within `range`. This is kept
298+ /// sorted.
299+ borders : Vec < IntBorder > ,
300+ }
301+
302+ impl SplitIntRange {
303+ fn new ( r : IntRange ) -> Self {
304+ SplitIntRange { range : r. clone ( ) , borders : Vec :: new ( ) }
305+ }
306+
307+ /// Internal use
308+ fn to_borders ( r : IntRange ) -> [ IntBorder ; 2 ] {
309+ use IntBorder :: * ;
310+ let ( lo, hi) = r. boundaries ( ) ;
311+ let lo = JustBefore ( lo) ;
312+ let hi = match hi. checked_add ( 1 ) {
313+ Some ( m) => JustBefore ( m) ,
314+ None => AfterMax ,
315+ } ;
316+ [ lo, hi]
317+ }
318+
319+ /// Add ranges relative to which we split.
320+ fn split ( & mut self , ranges : impl Iterator < Item = IntRange > ) {
321+ let this_range = & self . range ;
322+ let included_ranges = ranges. filter_map ( |r| this_range. intersection ( & r) ) ;
323+ let included_borders = included_ranges. flat_map ( |r| {
324+ let borders = Self :: to_borders ( r) ;
325+ once ( borders[ 0 ] ) . chain ( once ( borders[ 1 ] ) )
326+ } ) ;
327+ self . borders . extend ( included_borders) ;
328+ self . borders . sort_unstable ( ) ;
329+ }
330+
331+ /// Iterate over the contained ranges.
332+ fn iter < ' a > ( & ' a self ) -> impl Iterator < Item = IntRange > + Captures < ' a > {
333+ use IntBorder :: * ;
334+
335+ let self_range = Self :: to_borders ( self . range . clone ( ) ) ;
336+ // Start with the start of the range.
337+ let mut prev_border = self_range[ 0 ] ;
338+ self . borders
339+ . iter ( )
340+ . copied ( )
341+ // End with the end of the range.
342+ . chain ( once ( self_range[ 1 ] ) )
343+ // List pairs of adjacent borders.
344+ . map ( move |border| {
345+ let ret = ( prev_border, border) ;
346+ prev_border = border;
347+ ret
348+ } )
349+ // Skip duplicates.
350+ . filter ( |( prev_border, border) | prev_border != border)
351+ // Finally, convert to ranges.
352+ . map ( |( prev_border, border) | {
353+ let range = match ( prev_border, border) {
354+ ( JustBefore ( n) , JustBefore ( m) ) if n < m => n..=( m - 1 ) ,
355+ ( JustBefore ( n) , AfterMax ) => n..=u128:: MAX ,
356+ _ => unreachable ! ( ) , // Ruled out by the sorting and filtering we did
357+ } ;
358+ IntRange { range }
359+ } )
360+ }
361+ }
362+
342363#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
343364enum SliceKind {
344365 /// Patterns of length `n` (`[x, y]`).
0 commit comments