1use super::possible_origin::PossibleOriginVisitor;
2use super::transitive_relation::TransitiveRelation;
3use crate::ty::is_copy;
4use rustc_data_structures::fx::FxHashMap;
5use rustc_index::bit_set::DenseBitSet;
6use rustc_lint::LateContext;
7use rustc_middle::mir::visit::Visitor as _;
8use rustc_middle::mir::{self, Mutability};
9use rustc_middle::ty::{self, TyCtxt, TypeVisitor};
10use rustc_mir_dataflow::impls::MaybeStorageLive;
11use rustc_mir_dataflow::{Analysis, ResultsCursor};
12use std::borrow::Cow;
13use std::ops::ControlFlow;
14
15#[allow(clippy::module_name_repetitions)]
19struct PossibleBorrowerVisitor<'a, 'b, 'tcx> {
20 possible_borrower: TransitiveRelation,
21 body: &'b mir::Body<'tcx>,
22 cx: &'a LateContext<'tcx>,
23 possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>,
24}
25
26impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
27 fn new(
28 cx: &'a LateContext<'tcx>,
29 body: &'b mir::Body<'tcx>,
30 possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>,
31 ) -> Self {
32 Self {
33 possible_borrower: TransitiveRelation::default(),
34 body,
35 cx,
36 possible_origin,
37 }
38 }
39
40 fn into_map(
41 self,
42 cx: &'a LateContext<'tcx>,
43 maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>,
44 ) -> PossibleBorrowerMap<'b, 'tcx> {
45 let mut map = FxHashMap::default();
46 for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
47 if is_copy(cx, self.body.local_decls[row].ty) {
48 continue;
49 }
50
51 let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
52 borrowers.remove(mir::Local::from_usize(0));
53 if !borrowers.is_empty() {
54 map.insert(row, borrowers);
55 }
56 }
57
58 let bs = DenseBitSet::new_empty(self.body.local_decls.len());
59 PossibleBorrowerMap {
60 map,
61 maybe_live,
62 bitset: (bs.clone(), bs),
63 }
64 }
65}
66
67impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> {
68 fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
69 let lhs = place.local;
70 match rvalue {
71 mir::Rvalue::Ref(_, _, borrowed) | mir::Rvalue::CopyForDeref(borrowed) => {
72 self.possible_borrower.add(borrowed.local, lhs);
73 },
74 other => {
75 if ContainsRegion
76 .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
77 .is_continue()
78 {
79 return;
80 }
81 rvalue_locals(other, |rhs| {
82 if lhs != rhs {
83 self.possible_borrower.add(rhs, lhs);
84 }
85 });
86 },
87 }
88 }
89
90 fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
91 if let mir::TerminatorKind::Call {
92 args,
93 destination: mir::Place { local: dest, .. },
94 ..
95 } = &terminator.kind
96 {
97 let mut immutable_borrowers = vec![];
103 let mut mutable_borrowers = vec![];
104
105 for op in args {
106 match &op.node {
107 mir::Operand::Copy(p) | mir::Operand::Move(p) => {
108 if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
109 mutable_borrowers.push(p.local);
110 } else {
111 immutable_borrowers.push(p.local);
112 }
113 },
114 mir::Operand::Constant(..) => (),
115 }
116 }
117
118 let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
119 .iter()
120 .filter_map(|r| self.possible_origin.get(r))
121 .flat_map(DenseBitSet::iter)
122 .collect();
123
124 if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
125 mutable_variables.push(*dest);
126 }
127
128 for y in mutable_variables {
129 for x in &immutable_borrowers {
130 self.possible_borrower.add(*x, y);
131 }
132 for x in &mutable_borrowers {
133 self.possible_borrower.add(*x, y);
134 }
135 }
136 }
137 }
138}
139
140struct ContainsRegion;
141
142impl TypeVisitor<TyCtxt<'_>> for ContainsRegion {
143 type Result = ControlFlow<()>;
144
145 fn visit_region(&mut self, _: ty::Region<'_>) -> Self::Result {
146 ControlFlow::Break(())
147 }
148}
149
150fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
151 use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, Repeat, UnaryOp, Use};
152
153 let mut visit_op = |op: &mir::Operand<'_>| match op {
154 mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
155 mir::Operand::Constant(..) => (),
156 };
157
158 match rvalue {
159 Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
160 Aggregate(_, ops) => ops.iter().for_each(visit_op),
161 BinaryOp(_, box (lhs, rhs)) => {
162 visit_op(lhs);
163 visit_op(rhs);
164 },
165 _ => (),
166 }
167}
168
169#[allow(clippy::module_name_repetitions)]
171pub struct PossibleBorrowerMap<'b, 'tcx> {
172 pub map: FxHashMap<mir::Local, DenseBitSet<mir::Local>>,
174 maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>,
175 pub bitset: (DenseBitSet<mir::Local>, DenseBitSet<mir::Local>),
177}
178
179impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
180 pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self {
181 let possible_origin = {
182 let mut vis = PossibleOriginVisitor::new(mir);
183 vis.visit_body(mir);
184 vis.into_map(cx)
185 };
186 let maybe_storage_live_result =
187 MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len())))
188 .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone"))
189 .into_results_cursor(mir);
190 let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
191 vis.visit_body(mir);
192 vis.into_map(cx, maybe_storage_live_result)
193 }
194
195 pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
197 self.bounded_borrowers(borrowers, borrowers, borrowed, at)
198 }
199
200 pub fn bounded_borrowers(
203 &mut self,
204 below: &[mir::Local],
205 above: &[mir::Local],
206 borrowed: mir::Local,
207 at: mir::Location,
208 ) -> bool {
209 self.maybe_live.seek_after_primary_effect(at);
210
211 self.bitset.0.clear();
212 let maybe_live = &mut self.maybe_live;
213 if let Some(bitset) = self.map.get(&borrowed) {
214 for b in bitset.iter().filter(move |b| maybe_live.get().contains(*b)) {
215 self.bitset.0.insert(b);
216 }
217 } else {
218 return false;
219 }
220
221 self.bitset.1.clear();
222 for b in below {
223 self.bitset.1.insert(*b);
224 }
225
226 if !self.bitset.0.superset(&self.bitset.1) {
227 return false;
228 }
229
230 for b in above {
231 self.bitset.0.remove(*b);
232 }
233
234 self.bitset.0.is_empty()
235 }
236
237 pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
238 self.maybe_live.seek_after_primary_effect(at);
239 self.maybe_live.get().contains(local)
240 }
241}