@@ -85,7 +85,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
85
85
86
86
// this will generate code to test discriminant_lvalue and
87
87
// branch to the appropriate arm block
88
- self . match_candidates ( span, & mut arm_blocks, candidates, block) ;
88
+ let otherwise = self . match_candidates ( span, & mut arm_blocks, candidates, block) ;
89
+
90
+ // because all matches are exhaustive, in principle we expect
91
+ // an empty vector to be returned here, but the algorithm is
92
+ // not entirely precise
93
+ if !otherwise. is_empty ( ) {
94
+ let join_block = self . join_otherwise_blocks ( otherwise) ;
95
+ self . panic ( join_block) ;
96
+ }
89
97
90
98
// all the arm blocks will rejoin here
91
99
let end_block = self . cfg . start_new_block ( ) ;
@@ -279,11 +287,32 @@ struct Test<'tcx> {
279
287
// Main matching algorithm
280
288
281
289
impl < ' a , ' tcx > Builder < ' a , ' tcx > {
290
+ /// The main match algorithm. It begins with a set of candidates
291
+ /// `candidates` and has the job of generating code to determine
292
+ /// which of these candidates, if any, is the correct one. The
293
+ /// candidates are sorted in inverse priority -- so the last item
294
+ /// in the list has highest priority. When a candidate is found to
295
+ /// match the value, we will generate a branch to the appropriate
296
+ /// block found in `arm_blocks`.
297
+ ///
298
+ /// The return value is a list of "otherwise" blocks. These are
299
+ /// points in execution where we found that *NONE* of the
300
+ /// candidates apply. In principle, this means that the input
301
+ /// list was not exhaustive, though at present we sometimes are
302
+ /// not smart enough to recognize all exhaustive inputs.
303
+ ///
304
+ /// It might be surprising that the input can be inexhaustive.
305
+ /// Indeed, initially, it is not, because all matches are
306
+ /// exhaustive in Rust. But during processing we sometimes divide
307
+ /// up the list of candidates and recurse with a non-exhaustive
308
+ /// list. This is important to keep the size of the generated code
309
+ /// under control. See `test_candidates` for more details.
282
310
fn match_candidates < ' pat > ( & mut self ,
283
311
span : Span ,
284
312
arm_blocks : & mut ArmBlocks ,
285
313
mut candidates : Vec < Candidate < ' pat , ' tcx > > ,
286
314
mut block : BasicBlock )
315
+ -> Vec < BasicBlock >
287
316
{
288
317
debug ! ( "matched_candidate(span={:?}, block={:?}, candidates={:?})" ,
289
318
span, block, candidates) ;
@@ -311,17 +340,127 @@ impl<'a,'tcx> Builder<'a,'tcx> {
311
340
} else {
312
341
// if None is returned, then any remaining candidates
313
342
// are unreachable (at least not through this path).
314
- return ;
343
+ return vec ! [ ] ;
315
344
}
316
345
}
317
346
318
347
// If there are no candidates that still need testing, we're done.
319
348
// Since all matches are exhaustive, execution should never reach this point.
320
349
if candidates. is_empty ( ) {
321
- return self . panic ( block) ;
350
+ return vec ! [ block] ;
351
+ }
352
+
353
+ // Test candidates where possible.
354
+ let ( otherwise, tested_candidates) =
355
+ self . test_candidates ( span, arm_blocks, & candidates, block) ;
356
+
357
+ // If the target candidates were exhaustive, then we are done.
358
+ if otherwise. is_empty ( ) {
359
+ return vec ! [ ] ;
360
+ }
361
+
362
+ // If all candidates were sorted into `target_candidates` somewhere, then
363
+ // the initial set was inexhaustive.
364
+ let untested_candidates = candidates. len ( ) - tested_candidates;
365
+ if untested_candidates == 0 {
366
+ return otherwise;
322
367
}
323
368
324
- // otherwise, extract the next match pair and construct tests
369
+ // Otherwise, let's process those remaining candidates.
370
+ let join_block = self . join_otherwise_blocks ( otherwise) ;
371
+ candidates. truncate ( untested_candidates) ;
372
+ self . match_candidates ( span, arm_blocks, candidates, join_block)
373
+ }
374
+
375
+ fn join_otherwise_blocks ( & mut self ,
376
+ otherwise : Vec < BasicBlock > )
377
+ -> BasicBlock
378
+ {
379
+ if otherwise. len ( ) == 1 {
380
+ otherwise[ 0 ]
381
+ } else {
382
+ let join_block = self . cfg . start_new_block ( ) ;
383
+ for block in otherwise {
384
+ self . cfg . terminate ( block, Terminator :: Goto { target : join_block } ) ;
385
+ }
386
+ join_block
387
+ }
388
+ }
389
+
390
+ /// This is the most subtle part of the matching algorithm. At
391
+ /// this point, the input candidates have been fully simplified,
392
+ /// and so we know that all remaining match-pairs require some
393
+ /// sort of test. To decide what test to do, we take the highest
394
+ /// priority candidate (last one in the list) and extract the
395
+ /// first match-pair from the list. From this we decide what kind
396
+ /// of test is needed using `test`, defined in the `test` module.
397
+ ///
398
+ /// *Note:* taking the first match pair is somewhat arbitrary, and
399
+ /// we might do better here by choosing more carefully what to
400
+ /// test.
401
+ ///
402
+ /// For example, consider the following possible match-pairs:
403
+ ///
404
+ /// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has
405
+ /// 2. `x @ 22` -- we will do a `SwitchInt`
406
+ /// 3. `x @ 3..5` -- we will do a range test
407
+ /// 4. etc.
408
+ ///
409
+ /// Once we know what sort of test we are going to perform, this
410
+ /// test may also help us with other candidates. So we walk over
411
+ /// the candidates (from high to low priority) and check. This
412
+ /// gives us, for each outcome of the test, a transformed list of
413
+ /// candidates. For example, if we are testing the current
414
+ /// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1
415
+ /// @ 22}`, then we would have a resulting candidate of `{(x.0 as
416
+ /// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now
417
+ /// simpler (and, in fact, irrefutable).
418
+ ///
419
+ /// But there may also be candidates that the test just doesn't
420
+ /// apply to. For example, consider the case of #29740:
421
+ ///
422
+ /// ```rust
423
+ /// match x {
424
+ /// "foo" => ...,
425
+ /// "bar" => ...,
426
+ /// "baz" => ...,
427
+ /// _ => ...,
428
+ /// }
429
+ /// ```
430
+ ///
431
+ /// Here the match-pair we are testing will be `x @ "foo"`, and we
432
+ /// will generate an `Eq` test. Because `"bar"` and `"baz"` are different
433
+ /// constants, we will decide that these later candidates are just not
434
+ /// informed by the eq test. So we'll wind up with three candidate sets:
435
+ ///
436
+ /// - If outcome is that `x == "foo"` (one candidate, derived from `x @ "foo"`)
437
+ /// - If outcome is that `x != "foo"` (empty list of candidates)
438
+ /// - Otherwise (three candidates, `x @ "bar"`, `x @ "baz"`, `x @
439
+ /// _`). Here we have the invariant that everything in the
440
+ /// otherwise list is of **lower priority** than the stuff in the
441
+ /// other lists.
442
+ ///
443
+ /// So we'll compile the test. For each outcome of the test, we
444
+ /// recursively call `match_candidates` with the corresponding set
445
+ /// of candidates. But note that this set is now inexhaustive: for
446
+ /// example, in the case where the test returns false, there are
447
+ /// NO candidates, even though there is stll a value to be
448
+ /// matched. So we'll collect the return values from
449
+ /// `match_candidates`, which are the blocks where control-flow
450
+ /// goes if none of the candidates matched. At this point, we can
451
+ /// continue with the "otherwise" list.
452
+ ///
453
+ /// If you apply this to the above test, you basically wind up
454
+ /// with an if-else-if chain, testing each candidate in turn,
455
+ /// which is precisely what we want.
456
+ fn test_candidates < ' pat > ( & mut self ,
457
+ span : Span ,
458
+ arm_blocks : & mut ArmBlocks ,
459
+ candidates : & [ Candidate < ' pat , ' tcx > ] ,
460
+ block : BasicBlock )
461
+ -> ( Vec < BasicBlock > , usize )
462
+ {
463
+ // extract the match-pair from the highest priority candidate
325
464
let match_pair = & candidates. last ( ) . unwrap ( ) . match_pairs [ 0 ] ;
326
465
let mut test = self . test ( match_pair) ;
327
466
@@ -331,35 +470,57 @@ impl<'a,'tcx> Builder<'a,'tcx> {
331
470
// available
332
471
match test. kind {
333
472
TestKind :: SwitchInt { switch_ty, ref mut options, ref mut indices } => {
334
- for candidate in & candidates {
335
- self . add_cases_to_switch ( & match_pair. lvalue ,
336
- candidate,
337
- switch_ty,
338
- options,
339
- indices) ;
473
+ for candidate in candidates. iter ( ) . rev ( ) {
474
+ if !self . add_cases_to_switch ( & match_pair. lvalue ,
475
+ candidate,
476
+ switch_ty,
477
+ options,
478
+ indices) {
479
+ break ;
480
+ }
340
481
}
341
482
}
342
483
_ => { }
343
484
}
344
485
486
+ // perform the test, branching to one of N blocks. For each of
487
+ // those N possible outcomes, create a (initially empty)
488
+ // vector of candidates. Those are the candidates that still
489
+ // apply if the test has that particular outcome.
345
490
debug ! ( "match_candidates: test={:?} match_pair={:?}" , test, match_pair) ;
346
491
let target_blocks = self . perform_test ( block, & match_pair. lvalue , & test) ;
347
-
348
492
let mut target_candidates: Vec < _ > = ( 0 ..target_blocks. len ( ) ) . map ( |_| vec ! [ ] ) . collect ( ) ;
349
493
350
- for candidate in & candidates {
351
- self . sort_candidate ( & match_pair. lvalue ,
352
- & test,
353
- candidate,
354
- & mut target_candidates) ;
355
- }
356
-
357
- for ( target_block, target_candidates) in
494
+ // Sort the candidates into the appropriate vector in
495
+ // `target_candidates`. Note that at some point we may
496
+ // encounter a candidate where the test is not relevant; at
497
+ // that point, we stop sorting.
498
+ let tested_candidates =
499
+ candidates. iter ( )
500
+ . rev ( )
501
+ . take_while ( |c| self . sort_candidate ( & match_pair. lvalue ,
502
+ & test,
503
+ c,
504
+ & mut target_candidates) )
505
+ . count ( ) ;
506
+ assert ! ( tested_candidates > 0 ) ; // at least the last candidate ought to be tested
507
+
508
+ // For each outcome of test, process the candidates that still
509
+ // apply. Collect a list of blocks where control flow will
510
+ // branch if one of the `target_candidate` sets is not
511
+ // exhaustive.
512
+ let otherwise: Vec < _ > =
358
513
target_blocks. into_iter ( )
359
- . zip ( target_candidates. into_iter ( ) )
360
- {
361
- self . match_candidates ( span, arm_blocks, target_candidates, target_block) ;
362
- }
514
+ . zip ( target_candidates)
515
+ . flat_map ( |( target_block, target_candidates) | {
516
+ self . match_candidates ( span,
517
+ arm_blocks,
518
+ target_candidates,
519
+ target_block)
520
+ } )
521
+ . collect ( ) ;
522
+
523
+ ( otherwise, tested_candidates)
363
524
}
364
525
365
526
/// Initializes each of the bindings from the candidate by
0 commit comments