@@ -57,39 +57,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5757 // See issue #47412 for this hole being discovered in the wild.
5858 //
5959 // HACK(eddyb) Work around the above issue by adding a dummy inspection
60- // of `discriminant_place`, specifically by applying `Rvalue::Discriminant`
61- // (which will work regardless of type) and storing the result in a temp.
60+ // of `discriminant_place`, specifically by applying `ReadForMatch`.
6261 //
63- // NOTE: Under NLL, the above issue should no longer occur because it
64- // injects a borrow of the matched input, which should have the same effect
65- // as eddyb's hack. Once NLL is the default, we can remove the hack.
66-
67- let dummy_source_info = self . source_info ( discriminant_span) ;
68- let dummy_access = Rvalue :: Discriminant ( discriminant_place. clone ( ) ) ;
69- let dummy_ty = dummy_access. ty ( & self . local_decls , tcx) ;
70- let dummy_temp = self . temp ( dummy_ty, dummy_source_info. span ) ;
71- self . cfg
72- . push_assign ( block, dummy_source_info, & dummy_temp, dummy_access) ;
62+ // NOTE: ReadForMatch also checks that the discriminant is initialized.
63+ // This is currently needed to not allow matching on an uninitialized,
64+ // uninhabited value. If we get never patterns, those will check that
65+ // the place is initialized, and so this read would only be used to
66+ // check safety.
7367
7468 let source_info = self . source_info ( discriminant_span) ;
75- let borrowed_input_temp = if tcx. generate_borrow_of_any_match_input ( ) {
76- // The region is unknown at this point; we rely on NLL
77- // inference to find an appropriate one. Therefore you can
78- // only use this when NLL is turned on.
79- assert ! ( tcx. use_mir_borrowck( ) ) ;
80- let borrowed_input = Rvalue :: Ref (
81- tcx. types . re_empty ,
82- BorrowKind :: Shared ,
69+ self . cfg . push ( block, Statement {
70+ source_info,
71+ kind : StatementKind :: FakeRead (
72+ FakeReadCause :: ForMatchedPlace ,
8373 discriminant_place. clone ( ) ,
84- ) ;
85- let borrowed_input_ty = borrowed_input. ty ( & self . local_decls , tcx) ;
86- let borrowed_input_temp = self . temp ( borrowed_input_ty, span) ;
87- self . cfg
88- . push_assign ( block, source_info, & borrowed_input_temp, borrowed_input) ;
89- Some ( borrowed_input_temp)
90- } else {
91- None
92- } ;
74+ ) ,
75+ } ) ;
9376
9477 let mut arm_blocks = ArmBlocks {
9578 blocks : arms. iter ( ) . map ( |_| self . cfg . start_new_block ( ) ) . collect ( ) ,
@@ -118,6 +101,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
118101 . map ( |_| self . cfg . start_new_block ( ) )
119102 . collect ( ) ;
120103
104+ let mut has_guard = false ;
105+
121106 // assemble a list of candidates: there is one candidate per
122107 // pattern, which means there may be more than one candidate
123108 // *per arm*. These candidates are kept sorted such that the
@@ -140,24 +125,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
140125 . map (
141126 |(
142127 ( arm_index, pat_index, pattern, guard) ,
143- ( pre_binding_block, next_candidate_pre_binding_block) ,
128+ ( pre_binding_block, next_candidate_pre_binding_block)
144129 ) | {
145- if let ( true , Some ( borrow_temp) ) =
146- ( tcx. emit_read_for_match ( ) , borrowed_input_temp. clone ( ) )
147- {
148- // Inject a fake read, see comments on `FakeReadCause::ForMatch`.
149- let pattern_source_info = self . source_info ( pattern. span ) ;
150- self . cfg . push (
151- * pre_binding_block,
152- Statement {
153- source_info : pattern_source_info,
154- kind : StatementKind :: FakeRead (
155- FakeReadCause :: ForMatch ,
156- borrow_temp. clone ( ) ,
157- ) ,
158- } ,
159- ) ;
160- }
130+ has_guard |= guard. is_some ( ) ;
161131
162132 // One might ask: why not build up the match pair such that it
163133 // matches via `borrowed_input_temp.deref()` instead of
@@ -202,9 +172,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
202172 TerminatorKind :: Unreachable ,
203173 ) ;
204174
175+ // Maps a place to the kind of Fake borrow that we want to perform on
176+ // it: either Shallow or Shared, depending on whether the place is
177+ // bound in the match, or just switched on.
178+ // If there are no match guards then we don't need any fake borrows,
179+ // so don't track them.
180+ let mut fake_borrows = if has_guard && tcx. generate_borrow_of_any_match_input ( ) {
181+ Some ( FxHashMap ( ) )
182+ } else {
183+ None
184+ } ;
185+
186+ let pre_binding_blocks: Vec < _ > = candidates
187+ . iter ( )
188+ . map ( |cand| ( cand. pre_binding_block , cand. span ) )
189+ . collect ( ) ;
190+
205191 // this will generate code to test discriminant_place and
206192 // branch to the appropriate arm block
207- let otherwise = self . match_candidates ( span, & mut arm_blocks, candidates, block) ;
193+ let otherwise = self . match_candidates (
194+ discriminant_span,
195+ & mut arm_blocks,
196+ candidates,
197+ block,
198+ & mut fake_borrows,
199+ ) ;
208200
209201 if !otherwise. is_empty ( ) {
210202 // All matches are exhaustive. However, because some matches
@@ -224,6 +216,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
224216 }
225217 }
226218
219+ if let Some ( fake_borrows) = fake_borrows {
220+ self . add_fake_borrows ( & pre_binding_blocks, fake_borrows, source_info, block) ;
221+ }
222+
227223 // all the arm blocks will rejoin here
228224 let end_block = self . cfg . start_new_block ( ) ;
229225
@@ -714,12 +710,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
714710 /// up the list of candidates and recurse with a non-exhaustive
715711 /// list. This is important to keep the size of the generated code
716712 /// under control. See `test_candidates` for more details.
713+ ///
714+ /// If `add_fake_borrows` is true, then places which need fake borrows
715+ /// will be added to it.
717716 fn match_candidates < ' pat > (
718717 & mut self ,
719718 span : Span ,
720719 arm_blocks : & mut ArmBlocks ,
721720 mut candidates : Vec < Candidate < ' pat , ' tcx > > ,
722721 mut block : BasicBlock ,
722+ fake_borrows : & mut Option < FxHashMap < Place < ' tcx > , BorrowKind > > ,
723723 ) -> Vec < BasicBlock > {
724724 debug ! (
725725 "matched_candidate(span={:?}, block={:?}, candidates={:?})" ,
@@ -747,6 +747,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
747747 ) ;
748748 let mut unmatched_candidates = candidates. split_off ( fully_matched) ;
749749
750+ // Insert a *Shared* borrow of any places that are bound.
751+ if let Some ( fake_borrows) = fake_borrows {
752+ for Binding { source, .. }
753+ in candidates. iter ( ) . flat_map ( |candidate| & candidate. bindings )
754+ {
755+ fake_borrows. insert ( source. clone ( ) , BorrowKind :: Shared ) ;
756+ }
757+ }
758+
750759 let fully_matched_with_guard = candidates. iter ( ) . take_while ( |c| c. guard . is_some ( ) ) . count ( ) ;
751760
752761 let unreachable_candidates = if fully_matched_with_guard + 1 < candidates. len ( ) {
@@ -783,7 +792,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
783792 return vec ! [ ] ;
784793 } else {
785794 let target = self . cfg . start_new_block ( ) ;
786- return self . match_candidates ( span, arm_blocks, unmatched_candidates, target) ;
795+ return self . match_candidates (
796+ span,
797+ arm_blocks,
798+ unmatched_candidates,
799+ target,
800+ & mut None ,
801+ ) ;
787802 }
788803 }
789804 }
@@ -796,7 +811,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
796811
797812 // Test candidates where possible.
798813 let ( otherwise, tested_candidates) =
799- self . test_candidates ( span, arm_blocks, & unmatched_candidates, block) ;
814+ self . test_candidates ( span, arm_blocks, & unmatched_candidates, block, fake_borrows ) ;
800815
801816 // If the target candidates were exhaustive, then we are done.
802817 // But for borrowck continue build decision tree.
@@ -810,7 +825,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
810825
811826 // Otherwise, let's process those remaining candidates.
812827 let join_block = self . join_otherwise_blocks ( span, otherwise) ;
813- self . match_candidates ( span, arm_blocks, untested_candidates, join_block)
828+ self . match_candidates ( span, arm_blocks, untested_candidates, join_block, & mut None )
814829 }
815830
816831 fn join_otherwise_blocks ( & mut self , span : Span , mut otherwise : Vec < BasicBlock > ) -> BasicBlock {
@@ -950,6 +965,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
950965 arm_blocks : & mut ArmBlocks ,
951966 candidates : & [ Candidate < ' pat , ' tcx > ] ,
952967 block : BasicBlock ,
968+ fake_borrows : & mut Option < FxHashMap < Place < ' tcx > , BorrowKind > > ,
953969 ) -> ( Vec < BasicBlock > , usize ) {
954970 // extract the match-pair from the highest priority candidate
955971 let match_pair = & candidates. first ( ) . unwrap ( ) . match_pairs [ 0 ] ;
@@ -990,6 +1006,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
9901006 _ => { }
9911007 }
9921008
1009+ // Insert a Shallow borrow of any places that is switched on.
1010+ fake_borrows. as_mut ( ) . map ( |fb| {
1011+ fb. entry ( match_pair. place . clone ( ) ) . or_insert ( BorrowKind :: Shallow )
1012+ } ) ;
1013+
9931014 // perform the test, branching to one of N blocks. For each of
9941015 // those N possible outcomes, create a (initially empty)
9951016 // vector of candidates. Those are the candidates that still
@@ -1026,7 +1047,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
10261047 . into_iter ( )
10271048 . zip ( target_candidates)
10281049 . flat_map ( |( target_block, target_candidates) | {
1029- self . match_candidates ( span, arm_blocks, target_candidates, target_block)
1050+ self . match_candidates (
1051+ span,
1052+ arm_blocks,
1053+ target_candidates,
1054+ target_block,
1055+ fake_borrows,
1056+ )
10301057 } )
10311058 . collect ( ) ;
10321059
@@ -1504,4 +1531,86 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
15041531 debug ! ( "declare_binding: vars={:?}" , locals) ;
15051532 self . var_indices . insert ( var_id, locals) ;
15061533 }
1534+
1535+ // Determine the fake borrows that are needed to ensure that the place
1536+ // will evaluate to the same thing until an arm has been chosen.
1537+ fn add_fake_borrows < ' pat > (
1538+ & mut self ,
1539+ pre_binding_blocks : & [ ( BasicBlock , Span ) ] ,
1540+ fake_borrows : FxHashMap < Place < ' tcx > , BorrowKind > ,
1541+ source_info : SourceInfo ,
1542+ start_block : BasicBlock ,
1543+ ) {
1544+ let tcx = self . hir . tcx ( ) ;
1545+
1546+ debug ! ( "add_fake_borrows pre_binding_blocks = {:?}, fake_borrows = {:?}" ,
1547+ pre_binding_blocks, fake_borrows) ;
1548+
1549+ let mut all_fake_borrows = Vec :: with_capacity ( fake_borrows. len ( ) ) ;
1550+
1551+ // Insert a Shallow borrow of the prefixes of any fake borrows.
1552+ for ( place, borrow_kind) in fake_borrows
1553+ {
1554+ {
1555+ let mut prefix_cursor = & place;
1556+ while let Place :: Projection ( box Projection { base, elem } ) = prefix_cursor {
1557+ if let ProjectionElem :: Deref = elem {
1558+ // Insert a shallow borrow after a deref. For other
1559+ // projections the borrow of prefix_cursor will
1560+ // conflict with any mutation of base.
1561+ all_fake_borrows. push ( ( base. clone ( ) , BorrowKind :: Shallow ) ) ;
1562+ }
1563+ prefix_cursor = base;
1564+ }
1565+ }
1566+
1567+ all_fake_borrows. push ( ( place, borrow_kind) ) ;
1568+ }
1569+
1570+ // Deduplicate and ensure a deterministic order.
1571+ all_fake_borrows. sort ( ) ;
1572+ all_fake_borrows. dedup ( ) ;
1573+
1574+ debug ! ( "add_fake_borrows all_fake_borrows = {:?}" , all_fake_borrows) ;
1575+
1576+ // Add fake borrows to the start of the match and reads of them before
1577+ // the start of each arm.
1578+ let mut borrowed_input_temps = Vec :: with_capacity ( all_fake_borrows. len ( ) ) ;
1579+
1580+ for ( matched_place, borrow_kind) in all_fake_borrows {
1581+ let borrowed_input =
1582+ Rvalue :: Ref ( tcx. types . re_empty , borrow_kind, matched_place. clone ( ) ) ;
1583+ let borrowed_input_ty = borrowed_input. ty ( & self . local_decls , tcx) ;
1584+ let borrowed_input_temp = self . temp ( borrowed_input_ty, source_info. span ) ;
1585+ self . cfg . push_assign (
1586+ start_block,
1587+ source_info,
1588+ & borrowed_input_temp,
1589+ borrowed_input
1590+ ) ;
1591+ borrowed_input_temps. push ( borrowed_input_temp) ;
1592+ }
1593+
1594+ // FIXME: This could be a lot of reads (#fake borrows * #patterns).
1595+ // The false edges that we currently generate would allow us to only do
1596+ // this on the last Candidate, but it's possible that there might not be
1597+ // so many false edges in the future, so we read for all Candidates for
1598+ // now.
1599+ // Another option would be to make our own block and add our own false
1600+ // edges to it.
1601+ if tcx. emit_read_for_match ( ) {
1602+ for & ( pre_binding_block, span) in pre_binding_blocks {
1603+ let pattern_source_info = self . source_info ( span) ;
1604+ for temp in & borrowed_input_temps {
1605+ self . cfg . push ( pre_binding_block, Statement {
1606+ source_info : pattern_source_info,
1607+ kind : StatementKind :: FakeRead (
1608+ FakeReadCause :: ForMatchGuard ,
1609+ temp. clone ( ) ,
1610+ ) ,
1611+ } ) ;
1612+ }
1613+ }
1614+ }
1615+ }
15071616}
0 commit comments