@@ -24,7 +24,7 @@ use rustc_target::abi::{Integer, Size, VariantIdx};
24
24
25
25
use smallvec:: { smallvec, SmallVec } ;
26
26
use std:: cmp:: { self , max, min, Ordering } ;
27
- use std:: iter:: IntoIterator ;
27
+ use std:: iter:: { once , IntoIterator } ;
28
28
use std:: ops:: RangeInclusive ;
29
29
30
30
/// An inclusive interval, used for precise integer exhaustiveness checking.
@@ -183,77 +183,24 @@ impl IntRange {
183
183
Pat { ty, span : DUMMY_SP , kind : Box :: new ( kind) }
184
184
}
185
185
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.
218
187
fn split < ' p , ' tcx > (
219
188
& self ,
220
189
pcx : PatCtxt < ' _ , ' p , ' tcx > ,
221
190
hir_id : Option < HirId > ,
222
191
) -> 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)
245
194
let mut overlaps = vec ! [ ] ;
195
+ let mut split_range = SplitIntRange :: new ( self . clone ( ) ) ;
246
196
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
250
198
. matrix
251
199
. 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) {
257
204
// FIXME: for now, only check for overlapping ranges on simple range
258
205
// patterns. Otherwise with the current logic the following is detected
259
206
// as overlapping:
@@ -264,36 +211,15 @@ impl IntRange {
264
211
// _ => {}
265
212
// }
266
213
// ```
267
- overlaps. push ( ( range . clone ( ) , span) ) ;
214
+ overlaps. push ( ( intersection . clone ( ) , * span) ) ;
268
215
}
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 ( ) ) ;
275
219
276
220
self . lint_overlapping_range_endpoints ( pcx, hir_id, overlaps) ;
277
221
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 ( )
297
223
}
298
224
299
225
fn lint_overlapping_range_endpoints (
@@ -339,6 +265,101 @@ impl IntRange {
339
265
}
340
266
}
341
267
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
+
342
363
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
343
364
enum SliceKind {
344
365
/// Patterns of length `n` (`[x, y]`).
0 commit comments