@@ -127,28 +127,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
127
127
continue ;
128
128
}
129
129
130
- // _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref)
131
- // In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
132
- // block.
130
+ // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
133
131
let ( cloned, cannot_move_out) = unwrap_or_continue ! ( find_stmt_assigns_to( cx, mir, arg, from_borrow, bb) ) ;
134
132
135
133
let loc = mir:: Location {
136
134
block : bb,
137
135
statement_index : bbdata. statements . len ( ) ,
138
136
} ;
139
137
140
- if from_borrow && ( cannot_move_out || !possible_borrower. only_borrowers ( & [ arg] , cloned, loc) ) {
141
- continue ;
142
- }
138
+ // Cloned local
139
+ let local = if from_borrow {
140
+ // `res = clone(arg)` can be turned into `res = move arg;`
141
+ // if `arg` is the only borrow of `cloned` at this point.
142
+
143
+ if cannot_move_out || !possible_borrower. only_borrowers ( & [ arg] , cloned, loc) {
144
+ continue ;
145
+ }
146
+
147
+ cloned
148
+ } else {
149
+ // `arg` is a reference as it is `.deref()`ed in the previous block.
150
+ // Look into the predecessor block and find out the source of deref.
143
151
144
- // _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
145
- let referent = if from_deref {
146
152
let ps = mir. predecessors_for ( bb) ;
147
153
if ps. len ( ) != 1 {
148
154
continue ;
149
155
}
150
156
let pred_terminator = mir[ ps[ 0 ] ] . terminator ( ) ;
151
157
158
+ // receiver of the `deref()` call
152
159
let pred_arg = if_chain ! {
153
160
if let Some ( ( pred_fn_def_id, pred_arg, pred_arg_ty, Some ( res) ) ) =
154
161
is_call_with_ref_arg( cx, mir, & pred_terminator. kind) ;
@@ -169,22 +176,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
169
176
block : bb,
170
177
statement_index : mir. basic_blocks ( ) [ bb] . statements . len ( ) ,
171
178
} ;
179
+
180
+ // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
181
+ // at the last statement:
182
+ //
183
+ // ```
184
+ // pred_arg = &local;
185
+ // cloned = deref(pred_arg);
186
+ // arg = &cloned;
187
+ // StorageDead(pred_arg);
188
+ // res = to_path_buf(cloned);
189
+ // ```
172
190
if cannot_move_out || !possible_borrower. only_borrowers ( & [ arg, cloned] , local, loc) {
173
191
continue ;
174
192
}
193
+
175
194
local
176
- } else {
177
- cloned
178
195
} ;
179
196
197
+ // `local` cannot be moved out if it is used later
180
198
let used_later = traversal:: ReversePostorder :: new ( & mir, bb) . skip ( 1 ) . any ( |( tbb, tdata) | {
181
199
// Give up on loops
182
200
if tdata. terminator ( ) . successors ( ) . any ( |s| * s == bb) {
183
201
return true ;
184
202
}
185
203
186
204
let mut vis = LocalUseVisitor {
187
- local : referent ,
205
+ local,
188
206
used_other_than_drop : false ,
189
207
} ;
190
208
vis. visit_basic_block_data ( tbb, tdata) ;
@@ -207,7 +225,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
207
225
span. lo( ) + BytePos ( u32 :: try_from( dot) . unwrap( ) )
208
226
) ;
209
227
let mut app = Applicability :: MaybeIncorrect ;
228
+
210
229
let mut call_snip = & snip[ dot + 1 ..] ;
230
+ // Machine applicable when `call_snip` looks like `foobar()`
211
231
if call_snip. ends_with( "()" ) {
212
232
call_snip = call_snip[ ..call_snip. len( ) -2 ] . trim( ) ;
213
233
if call_snip. as_bytes( ) . iter( ) . all( |b| b. is_ascii_alphabetic( ) || * b == b'_' ) {
@@ -366,6 +386,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor {
366
386
}
367
387
}
368
388
389
+ /// Determines liveness of each local purely based on `StorageLive`/`Dead`.
369
390
#[ derive( Copy , Clone ) ]
370
391
struct MaybeStorageLive < ' a , ' tcx > {
371
392
body : & ' a mir:: Body < ' tcx > ,
@@ -420,6 +441,9 @@ impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
420
441
const BOTTOM_VALUE : bool = false ;
421
442
}
422
443
444
+ /// Collects the possible borrowers of each local.
445
+ /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
446
+ /// possible borrowers of `a`.
423
447
struct PossibleBorrowerVisitor < ' a , ' tcx > {
424
448
possible_borrower : TransitiveRelation < mir:: Local > ,
425
449
body : & ' a mir:: Body < ' tcx > ,
@@ -507,10 +531,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
507
531
..
508
532
} = & terminator. kind
509
533
{
510
- // If the call returns something with some lifetime ,
534
+ // If the call returns something with lifetimes ,
511
535
// let's conservatively assume the returned value contains lifetime of all the arguments.
512
- let mut cr = ContainsRegion ;
513
- if !cr . visit_ty ( & self . body . local_decls [ * dest] . ty ) {
536
+ // For example, given ` let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
537
+ if !ContainsRegion . visit_ty ( & self . body . local_decls [ * dest] . ty ) {
514
538
return ;
515
539
}
516
540
@@ -559,14 +583,17 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
559
583
}
560
584
}
561
585
586
+ /// Result of `PossibleBorrowerVisitor`.
562
587
struct PossibleBorrower < ' a , ' tcx > {
588
+ /// Mapping `Local -> its possible borrowers`
563
589
map : FxHashMap < mir:: Local , HybridBitSet < mir:: Local > > ,
564
590
maybe_live : DataflowResultsCursor < ' a , ' tcx , MaybeStorageLive < ' a , ' tcx > > ,
565
591
// Caches to avoid allocation of `BitSet` on every query
566
592
bitset : ( BitSet < mir:: Local > , BitSet < mir:: Local > ) ,
567
593
}
568
594
569
595
impl PossibleBorrower < ' _ , ' _ > {
596
+ /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
570
597
fn only_borrowers ( & mut self , borrowers : & [ mir:: Local ] , borrowed : mir:: Local , at : mir:: Location ) -> bool {
571
598
self . maybe_live . seek ( at) ;
572
599
0 commit comments