@@ -57,39 +57,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
57
57
// See issue #47412 for this hole being discovered in the wild.
58
58
//
59
59
// 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`.
62
61
//
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.
73
67
74
68
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 ,
83
73
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
+ } ) ;
93
76
94
77
let mut arm_blocks = ArmBlocks {
95
78
blocks : arms. iter ( ) . map ( |_| self . cfg . start_new_block ( ) ) . collect ( ) ,
@@ -118,6 +101,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
118
101
. map ( |_| self . cfg . start_new_block ( ) )
119
102
. collect ( ) ;
120
103
104
+ let mut has_guard = false ;
105
+
121
106
// assemble a list of candidates: there is one candidate per
122
107
// pattern, which means there may be more than one candidate
123
108
// *per arm*. These candidates are kept sorted such that the
@@ -140,24 +125,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
140
125
. map (
141
126
|(
142
127
( arm_index, pat_index, pattern, guard) ,
143
- ( pre_binding_block, next_candidate_pre_binding_block) ,
128
+ ( pre_binding_block, next_candidate_pre_binding_block)
144
129
) | {
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 ( ) ;
161
131
162
132
// One might ask: why not build up the match pair such that it
163
133
// matches via `borrowed_input_temp.deref()` instead of
@@ -202,9 +172,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
202
172
TerminatorKind :: Unreachable ,
203
173
) ;
204
174
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
+
205
191
// this will generate code to test discriminant_place and
206
192
// 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
+ ) ;
208
200
209
201
if !otherwise. is_empty ( ) {
210
202
// All matches are exhaustive. However, because some matches
@@ -224,6 +216,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
224
216
}
225
217
}
226
218
219
+ if let Some ( fake_borrows) = fake_borrows {
220
+ self . add_fake_borrows ( & pre_binding_blocks, fake_borrows, source_info, block) ;
221
+ }
222
+
227
223
// all the arm blocks will rejoin here
228
224
let end_block = self . cfg . start_new_block ( ) ;
229
225
@@ -714,12 +710,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
714
710
/// up the list of candidates and recurse with a non-exhaustive
715
711
/// list. This is important to keep the size of the generated code
716
712
/// 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.
717
716
fn match_candidates < ' pat > (
718
717
& mut self ,
719
718
span : Span ,
720
719
arm_blocks : & mut ArmBlocks ,
721
720
mut candidates : Vec < Candidate < ' pat , ' tcx > > ,
722
721
mut block : BasicBlock ,
722
+ fake_borrows : & mut Option < FxHashMap < Place < ' tcx > , BorrowKind > > ,
723
723
) -> Vec < BasicBlock > {
724
724
debug ! (
725
725
"matched_candidate(span={:?}, block={:?}, candidates={:?})" ,
@@ -747,6 +747,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
747
747
) ;
748
748
let mut unmatched_candidates = candidates. split_off ( fully_matched) ;
749
749
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
+
750
759
let fully_matched_with_guard = candidates. iter ( ) . take_while ( |c| c. guard . is_some ( ) ) . count ( ) ;
751
760
752
761
let unreachable_candidates = if fully_matched_with_guard + 1 < candidates. len ( ) {
@@ -783,7 +792,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
783
792
return vec ! [ ] ;
784
793
} else {
785
794
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
+ ) ;
787
802
}
788
803
}
789
804
}
@@ -796,7 +811,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
796
811
797
812
// Test candidates where possible.
798
813
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 ) ;
800
815
801
816
// If the target candidates were exhaustive, then we are done.
802
817
// But for borrowck continue build decision tree.
@@ -810,7 +825,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
810
825
811
826
// Otherwise, let's process those remaining candidates.
812
827
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 )
814
829
}
815
830
816
831
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> {
950
965
arm_blocks : & mut ArmBlocks ,
951
966
candidates : & [ Candidate < ' pat , ' tcx > ] ,
952
967
block : BasicBlock ,
968
+ fake_borrows : & mut Option < FxHashMap < Place < ' tcx > , BorrowKind > > ,
953
969
) -> ( Vec < BasicBlock > , usize ) {
954
970
// extract the match-pair from the highest priority candidate
955
971
let match_pair = & candidates. first ( ) . unwrap ( ) . match_pairs [ 0 ] ;
@@ -990,6 +1006,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
990
1006
_ => { }
991
1007
}
992
1008
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
+
993
1014
// perform the test, branching to one of N blocks. For each of
994
1015
// those N possible outcomes, create a (initially empty)
995
1016
// vector of candidates. Those are the candidates that still
@@ -1026,7 +1047,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1026
1047
. into_iter ( )
1027
1048
. zip ( target_candidates)
1028
1049
. 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
+ )
1030
1057
} )
1031
1058
. collect ( ) ;
1032
1059
@@ -1504,4 +1531,86 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1504
1531
debug ! ( "declare_binding: vars={:?}" , locals) ;
1505
1532
self . var_indices . insert ( var_id, locals) ;
1506
1533
}
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
+ }
1507
1616
}
0 commit comments