Add comments

This commit is contained in:
Shotaro Yamada 2019-09-30 16:16:09 +09:00
parent 1cee3fe00e
commit 866729f5db

View file

@ -127,9 +127,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
continue; continue;
} }
// _1 in MIR `{ _2 = &_1; clone(move _2); }` or `{ _2 = _1; to_path_buf(_2); } (from_deref) // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
// In case of `from_deref`, `arg` is already a reference since it is `deref`ed in the previous
// block.
let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb)); let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
let loc = mir::Location { let loc = mir::Location {
@ -137,18 +135,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
statement_index: bbdata.statements.len(), statement_index: bbdata.statements.len(),
}; };
if from_borrow && (cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc)) { // Cloned local
continue; let local = if from_borrow {
} // `res = clone(arg)` can be turned into `res = move arg;`
// if `arg` is the only borrow of `cloned` at this point.
if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
continue;
}
cloned
} else {
// `arg` is a reference as it is `.deref()`ed in the previous block.
// Look into the predecessor block and find out the source of deref.
// _1 in MIR `{ _2 = &_1; _3 = deref(move _2); } -> { _4 = _3; to_path_buf(move _4); }`
let referent = if from_deref {
let ps = mir.predecessors_for(bb); let ps = mir.predecessors_for(bb);
if ps.len() != 1 { if ps.len() != 1 {
continue; continue;
} }
let pred_terminator = mir[ps[0]].terminator(); let pred_terminator = mir[ps[0]].terminator();
// receiver of the `deref()` call
let pred_arg = if_chain! { let pred_arg = if_chain! {
if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) = if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
is_call_with_ref_arg(cx, mir, &pred_terminator.kind); is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
@ -169,14 +176,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
block: bb, block: bb,
statement_index: mir.basic_blocks()[bb].statements.len(), statement_index: mir.basic_blocks()[bb].statements.len(),
}; };
// This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
// at the last statement:
//
// ```
// pred_arg = &local;
// cloned = deref(pred_arg);
// arg = &cloned;
// StorageDead(pred_arg);
// res = to_path_buf(cloned);
// ```
if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) { if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
continue; continue;
} }
local local
} else {
cloned
}; };
// `local` cannot be moved out if it is used later
let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| { let used_later = traversal::ReversePostorder::new(&mir, bb).skip(1).any(|(tbb, tdata)| {
// Give up on loops // Give up on loops
if tdata.terminator().successors().any(|s| *s == bb) { if tdata.terminator().successors().any(|s| *s == bb) {
@ -184,7 +202,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
} }
let mut vis = LocalUseVisitor { let mut vis = LocalUseVisitor {
local: referent, local,
used_other_than_drop: false, used_other_than_drop: false,
}; };
vis.visit_basic_block_data(tbb, tdata); vis.visit_basic_block_data(tbb, tdata);
@ -207,7 +225,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
span.lo() + BytePos(u32::try_from(dot).unwrap()) span.lo() + BytePos(u32::try_from(dot).unwrap())
); );
let mut app = Applicability::MaybeIncorrect; let mut app = Applicability::MaybeIncorrect;
let mut call_snip = &snip[dot + 1..]; let mut call_snip = &snip[dot + 1..];
// Machine applicable when `call_snip` looks like `foobar()`
if call_snip.ends_with("()") { if call_snip.ends_with("()") {
call_snip = call_snip[..call_snip.len()-2].trim(); call_snip = call_snip[..call_snip.len()-2].trim();
if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { 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 {
} }
} }
/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
struct MaybeStorageLive<'a, 'tcx> { struct MaybeStorageLive<'a, 'tcx> {
body: &'a mir::Body<'tcx>, body: &'a mir::Body<'tcx>,
@ -420,6 +441,9 @@ impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
const BOTTOM_VALUE: bool = false; const BOTTOM_VALUE: bool = false;
} }
/// Collects the possible borrowers of each local.
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
/// possible borrowers of `a`.
struct PossibleBorrowerVisitor<'a, 'tcx> { struct PossibleBorrowerVisitor<'a, 'tcx> {
possible_borrower: TransitiveRelation<mir::Local>, possible_borrower: TransitiveRelation<mir::Local>,
body: &'a mir::Body<'tcx>, body: &'a mir::Body<'tcx>,
@ -507,10 +531,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
.. ..
} = &terminator.kind } = &terminator.kind
{ {
// If the call returns something with some lifetime, // If the call returns something with lifetimes,
// let's conservatively assume the returned value contains lifetime of all the arguments. // let's conservatively assume the returned value contains lifetime of all the arguments.
let mut cr = ContainsRegion; // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
if !cr.visit_ty(&self.body.local_decls[*dest].ty) { if !ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty) {
return; return;
} }
@ -559,7 +583,9 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
} }
} }
/// Result of `PossibleBorrowerVisitor`.
struct PossibleBorrower<'a, 'tcx> { struct PossibleBorrower<'a, 'tcx> {
/// Mapping `Local -> its possible borrowers`
map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>, map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
maybe_live: DataflowResultsCursor<'a, 'tcx, MaybeStorageLive<'a, 'tcx>>, maybe_live: DataflowResultsCursor<'a, 'tcx, MaybeStorageLive<'a, 'tcx>>,
// Caches to avoid allocation of `BitSet` on every query // Caches to avoid allocation of `BitSet` on every query
@ -567,6 +593,7 @@ struct PossibleBorrower<'a, 'tcx> {
} }
impl PossibleBorrower<'_, '_> { impl PossibleBorrower<'_, '_> {
/// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool { fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
self.maybe_live.seek(at); self.maybe_live.seek(at);