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