@@ -85,7 +85,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
8585
8686 // this will generate code to test discriminant_lvalue and
8787 // 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+ }
8997
9098 // all the arm blocks will rejoin here
9199 let end_block = self . cfg . start_new_block ( ) ;
@@ -279,11 +287,32 @@ struct Test<'tcx> {
279287// Main matching algorithm
280288
281289impl < ' 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.
282310 fn match_candidates < ' pat > ( & mut self ,
283311 span : Span ,
284312 arm_blocks : & mut ArmBlocks ,
285313 mut candidates : Vec < Candidate < ' pat , ' tcx > > ,
286314 mut block : BasicBlock )
315+ -> Vec < BasicBlock >
287316 {
288317 debug ! ( "matched_candidate(span={:?}, block={:?}, candidates={:?})" ,
289318 span, block, candidates) ;
@@ -311,17 +340,127 @@ impl<'a,'tcx> Builder<'a,'tcx> {
311340 } else {
312341 // if None is returned, then any remaining candidates
313342 // are unreachable (at least not through this path).
314- return ;
343+ return vec ! [ ] ;
315344 }
316345 }
317346
318347 // If there are no candidates that still need testing, we're done.
319348 // Since all matches are exhaustive, execution should never reach this point.
320349 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;
322367 }
323368
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
325464 let match_pair = & candidates. last ( ) . unwrap ( ) . match_pairs [ 0 ] ;
326465 let mut test = self . test ( match_pair) ;
327466
@@ -331,35 +470,57 @@ impl<'a,'tcx> Builder<'a,'tcx> {
331470 // available
332471 match test. kind {
333472 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+ }
340481 }
341482 }
342483 _ => { }
343484 }
344485
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.
345490 debug ! ( "match_candidates: test={:?} match_pair={:?}" , test, match_pair) ;
346491 let target_blocks = self . perform_test ( block, & match_pair. lvalue , & test) ;
347-
348492 let mut target_candidates: Vec < _ > = ( 0 ..target_blocks. len ( ) ) . map ( |_| vec ! [ ] ) . collect ( ) ;
349493
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 < _ > =
358513 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)
363524 }
364525
365526 /// Initializes each of the bindings from the candidate by
0 commit comments