@@ -21,7 +21,7 @@ use rustc::mir::*;
2121use rustc:: hir;
2222use hair:: * ;
2323use syntax:: ast:: { Name , NodeId } ;
24- use syntax_pos:: { DUMMY_SP , Span } ;
24+ use syntax_pos:: Span ;
2525
2626// helper functions, broken out by category:
2727mod simplify;
@@ -54,29 +54,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5454 ( body, scope. unwrap_or ( self . visibility_scope ) )
5555 } ) . collect ( ) ;
5656
57+ // create binding start block for link them by false edges
58+ let candidate_count = arms. iter ( ) . fold ( 0 , |ac, c| ac + c. patterns . len ( ) ) ;
59+ let pre_binding_blocks: Vec < _ > = ( 0 ..candidate_count + 1 )
60+ . map ( |_| self . cfg . start_new_block ( ) ) . collect ( ) ;
61+
5762 // assemble a list of candidates: there is one candidate per
5863 // pattern, which means there may be more than one candidate
5964 // *per arm*. These candidates are kept sorted such that the
6065 // highest priority candidate comes first in the list.
6166 // (i.e. same order as in source)
67+
6268 let candidates: Vec < _ > =
6369 arms. iter ( )
6470 . enumerate ( )
6571 . flat_map ( |( arm_index, arm) | {
6672 arm. patterns . iter ( )
6773 . map ( move |pat| ( arm_index, pat, arm. guard . clone ( ) ) )
6874 } )
69- . map ( |( arm_index, pattern, guard) | {
75+ . zip ( pre_binding_blocks. iter ( ) . zip ( pre_binding_blocks. iter ( ) . skip ( 1 ) ) )
76+ . map ( |( ( arm_index, pattern, guard) ,
77+ ( pre_binding_block, next_candidate_pre_binding_block) ) | {
7078 Candidate {
7179 span : pattern. span ,
7280 match_pairs : vec ! [ MatchPair :: new( discriminant_lvalue. clone( ) , pattern) ] ,
7381 bindings : vec ! [ ] ,
7482 guard,
7583 arm_index,
84+ pre_binding_block : * pre_binding_block,
85+ next_candidate_pre_binding_block : * next_candidate_pre_binding_block,
7686 }
7787 } )
7888 . collect ( ) ;
7989
90+ let outer_source_info = self . source_info ( span) ;
91+ self . cfg . terminate ( * pre_binding_blocks. last ( ) . unwrap ( ) ,
92+ outer_source_info, TerminatorKind :: Unreachable ) ;
93+
8094 // this will generate code to test discriminant_lvalue and
8195 // branch to the appropriate arm block
8296 let otherwise = self . match_candidates ( span, & mut arm_blocks, candidates, block) ;
@@ -148,7 +162,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
148162 match_pairs : vec ! [ MatchPair :: new( initializer. clone( ) , & irrefutable_pat) ] ,
149163 bindings : vec ! [ ] ,
150164 guard : None ,
151- arm_index : 0 , // since we don't call `match_candidates`, this field is unused
165+
166+ // since we don't call `match_candidates`, next fields is unused
167+ arm_index : 0 ,
168+ pre_binding_block : block,
169+ next_candidate_pre_binding_block : block
152170 } ;
153171
154172 // Simplify the candidate. Since the pattern is irrefutable, this should
@@ -278,6 +296,10 @@ pub struct Candidate<'pat, 'tcx:'pat> {
278296
279297 // ...and then we branch to arm with this index.
280298 arm_index : usize ,
299+
300+ // ...and the blocks for add false edges between candidates
301+ pre_binding_block : BasicBlock ,
302+ next_candidate_pre_binding_block : BasicBlock ,
281303}
282304
283305#[ derive( Clone , Debug ) ]
@@ -398,17 +420,43 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
398420 candidates. iter ( ) . take_while ( |c| c. match_pairs . is_empty ( ) ) . count ( ) ;
399421 debug ! ( "match_candidates: {:?} candidates fully matched" , fully_matched) ;
400422 let mut unmatched_candidates = candidates. split_off ( fully_matched) ;
401- for ( index, candidate) in candidates. into_iter ( ) . enumerate ( ) {
423+
424+ let fully_matched_with_guard =
425+ candidates. iter ( ) . take_while ( |c| c. guard . is_some ( ) ) . count ( ) ;
426+
427+ let unreachable_candidates = if fully_matched_with_guard + 1 < candidates. len ( ) {
428+ candidates. split_off ( fully_matched_with_guard + 1 )
429+ } else {
430+ vec ! [ ]
431+ } ;
432+
433+ for candidate in candidates {
402434 // If so, apply any bindings, test the guard (if any), and
403435 // branch to the arm.
404- let is_last = index == fully_matched - 1 ;
405- if let Some ( b) = self . bind_and_guard_matched_candidate ( block, arm_blocks,
406- candidate, is_last) {
436+ if let Some ( b) = self . bind_and_guard_matched_candidate ( block, arm_blocks, candidate) {
407437 block = b;
408438 } else {
409439 // if None is returned, then any remaining candidates
410440 // are unreachable (at least not through this path).
411- return vec ! [ ] ;
441+ // Link them with false edges.
442+ debug ! ( "match_candidates: add false edges for unreachable {:?} and unmatched {:?}" ,
443+ unreachable_candidates, unmatched_candidates) ;
444+ for candidate in unreachable_candidates {
445+ let source_info = self . source_info ( candidate. span ) ;
446+ let target = self . cfg . start_new_block ( ) ;
447+ if let Some ( otherwise) = self . bind_and_guard_matched_candidate ( target,
448+ arm_blocks,
449+ candidate) {
450+ self . cfg . terminate ( otherwise, source_info, TerminatorKind :: Unreachable ) ;
451+ }
452+ }
453+
454+ if unmatched_candidates. is_empty ( ) {
455+ return vec ! [ ]
456+ } else {
457+ let target = self . cfg . start_new_block ( ) ;
458+ return self . match_candidates ( span, arm_blocks, unmatched_candidates, target) ;
459+ }
412460 }
413461 }
414462
@@ -423,9 +471,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
423471 self . test_candidates ( span, arm_blocks, & unmatched_candidates, block) ;
424472
425473 // If the target candidates were exhaustive, then we are done.
426- if otherwise. is_empty ( ) {
427- return vec ! [ ] ;
428- }
474+ // But for borrowck continue build decision tree.
429475
430476 // If all candidates were sorted into `target_candidates` somewhere, then
431477 // the initial set was inexhaustive.
@@ -666,48 +712,50 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
666712 fn bind_and_guard_matched_candidate < ' pat > ( & mut self ,
667713 mut block : BasicBlock ,
668714 arm_blocks : & mut ArmBlocks ,
669- candidate : Candidate < ' pat , ' tcx > ,
670- is_last_arm : bool )
715+ candidate : Candidate < ' pat , ' tcx > )
671716 -> Option < BasicBlock > {
672717 debug ! ( "bind_and_guard_matched_candidate(block={:?}, candidate={:?})" ,
673718 block, candidate) ;
674719
675720 debug_assert ! ( candidate. match_pairs. is_empty( ) ) ;
676721
677- self . bind_matched_candidate ( block, candidate. bindings ) ;
678-
679722 let arm_block = arm_blocks. blocks [ candidate. arm_index ] ;
723+ let candidate_source_info = self . source_info ( candidate. span ) ;
724+
725+ self . cfg . terminate ( block, candidate_source_info,
726+ TerminatorKind :: Goto { target : candidate. pre_binding_block } ) ;
727+
728+ block = self . cfg . start_new_block ( ) ;
729+ self . cfg . terminate ( candidate. pre_binding_block , candidate_source_info,
730+ TerminatorKind :: FalseEdges {
731+ real_target : block,
732+ imaginary_targets :
733+ vec ! [ candidate. next_candidate_pre_binding_block] } ) ;
734+
735+ self . bind_matched_candidate ( block, candidate. bindings ) ;
680736
681737 if let Some ( guard) = candidate. guard {
682738 // the block to branch to if the guard fails; if there is no
683739 // guard, this block is simply unreachable
684740 let guard = self . hir . mirror ( guard) ;
685741 let source_info = self . source_info ( guard. span ) ;
686742 let cond = unpack ! ( block = self . as_local_operand( block, guard) ) ;
687- let otherwise = self . cfg . start_new_block ( ) ;
743+
744+ let false_edge_block = self . cfg . start_new_block ( ) ;
688745 self . cfg . terminate ( block, source_info,
689- TerminatorKind :: if_ ( self . hir . tcx ( ) , cond, arm_block, otherwise) ) ;
690- Some ( otherwise)
691- } else if !is_last_arm {
692- // Add always true guard in case of more than one arm
693- // it creates false edges and allow MIR borrowck detects errors
694- // FIXME(#45184) -- permit "false edges"
695- let source_info = self . source_info ( candidate. span ) ;
696- let true_expr = Expr {
697- temp_lifetime : None ,
698- ty : self . hir . tcx ( ) . types . bool ,
699- span : DUMMY_SP ,
700- kind : ExprKind :: Literal { literal : self . hir . true_literal ( ) } ,
701- } ;
702- let cond = unpack ! ( block = self . as_local_operand( block, true_expr) ) ;
746+ TerminatorKind :: if_ ( self . hir . tcx ( ) , cond, arm_block,
747+ false_edge_block) ) ;
748+
703749 let otherwise = self . cfg . start_new_block ( ) ;
704- self . cfg . terminate ( block, source_info,
705- TerminatorKind :: if_ ( self . hir . tcx ( ) , cond, arm_block, otherwise) ) ;
750+ self . cfg . terminate ( false_edge_block, source_info,
751+ TerminatorKind :: FalseEdges {
752+ real_target : otherwise,
753+ imaginary_targets :
754+ vec ! [ candidate. next_candidate_pre_binding_block] } ) ;
706755 Some ( otherwise)
707756 } else {
708- let source_info = self . source_info ( candidate. span ) ;
709- self . cfg . terminate ( block, source_info,
710- TerminatorKind :: Goto { target : arm_block } ) ;
757+ self . cfg . terminate ( block, candidate_source_info,
758+ TerminatorKind :: Goto { target : arm_block } ) ;
711759 None
712760 }
713761 }
0 commit comments