• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 use rustc_infer::infer::InferCtxt;
4 use rustc_middle::mir::visit::TyContext;
5 use rustc_middle::mir::visit::Visitor;
6 use rustc_middle::mir::{
7     Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement,
8     StatementKind, Terminator, TerminatorKind, UserTypeProjection,
9 };
10 use rustc_middle::ty::subst::SubstsRef;
11 use rustc_middle::ty::visit::TypeVisitable;
12 use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
13 
14 use crate::{
15     borrow_set::BorrowSet, facts::AllFacts, location::LocationTable, places_conflict,
16     region_infer::values::LivenessValues,
17 };
18 
generate_constraints<'tcx>( infcx: &InferCtxt<'tcx>, liveness_constraints: &mut LivenessValues<RegionVid>, all_facts: &mut Option<AllFacts>, location_table: &LocationTable, body: &Body<'tcx>, borrow_set: &BorrowSet<'tcx>, )19 pub(super) fn generate_constraints<'tcx>(
20     infcx: &InferCtxt<'tcx>,
21     liveness_constraints: &mut LivenessValues<RegionVid>,
22     all_facts: &mut Option<AllFacts>,
23     location_table: &LocationTable,
24     body: &Body<'tcx>,
25     borrow_set: &BorrowSet<'tcx>,
26 ) {
27     let mut cg = ConstraintGeneration {
28         borrow_set,
29         infcx,
30         liveness_constraints,
31         location_table,
32         all_facts,
33         body,
34     };
35 
36     for (bb, data) in body.basic_blocks.iter_enumerated() {
37         cg.visit_basic_block_data(bb, data);
38     }
39 }
40 
41 /// 'cg = the duration of the constraint generation process itself.
42 struct ConstraintGeneration<'cg, 'tcx> {
43     infcx: &'cg InferCtxt<'tcx>,
44     all_facts: &'cg mut Option<AllFacts>,
45     location_table: &'cg LocationTable,
46     liveness_constraints: &'cg mut LivenessValues<RegionVid>,
47     borrow_set: &'cg BorrowSet<'tcx>,
48     body: &'cg Body<'tcx>,
49 }
50 
51 impl<'cg, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'tcx> {
52     /// We sometimes have `substs` within an rvalue, or within a
53     /// call. Make them live at the location where they appear.
visit_substs(&mut self, substs: &SubstsRef<'tcx>, location: Location)54     fn visit_substs(&mut self, substs: &SubstsRef<'tcx>, location: Location) {
55         self.add_regular_live_constraint(*substs, location);
56         self.super_substs(substs);
57     }
58 
59     /// We sometimes have `region` within an rvalue, or within a
60     /// call. Make them live at the location where they appear.
visit_region(&mut self, region: ty::Region<'tcx>, location: Location)61     fn visit_region(&mut self, region: ty::Region<'tcx>, location: Location) {
62         self.add_regular_live_constraint(region, location);
63         self.super_region(region);
64     }
65 
66     /// We sometimes have `ty` within an rvalue, or within a
67     /// call. Make them live at the location where they appear.
visit_ty(&mut self, ty: Ty<'tcx>, ty_context: TyContext)68     fn visit_ty(&mut self, ty: Ty<'tcx>, ty_context: TyContext) {
69         match ty_context {
70             TyContext::ReturnTy(SourceInfo { span, .. })
71             | TyContext::YieldTy(SourceInfo { span, .. })
72             | TyContext::UserTy(span)
73             | TyContext::LocalDecl { source_info: SourceInfo { span, .. }, .. } => {
74                 span_bug!(span, "should not be visiting outside of the CFG: {:?}", ty_context);
75             }
76             TyContext::Location(location) => {
77                 self.add_regular_live_constraint(ty, location);
78             }
79         }
80 
81         self.super_ty(ty);
82     }
83 
visit_statement(&mut self, statement: &Statement<'tcx>, location: Location)84     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
85         if let Some(all_facts) = self.all_facts {
86             let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
87             all_facts.cfg_edge.push((
88                 self.location_table.start_index(location),
89                 self.location_table.mid_index(location),
90             ));
91 
92             all_facts.cfg_edge.push((
93                 self.location_table.mid_index(location),
94                 self.location_table.start_index(location.successor_within_block()),
95             ));
96 
97             // If there are borrows on this now dead local, we need to record them as `killed`.
98             if let StatementKind::StorageDead(local) = statement.kind {
99                 record_killed_borrows_for_local(
100                     all_facts,
101                     self.borrow_set,
102                     self.location_table,
103                     local,
104                     location,
105                 );
106             }
107         }
108 
109         self.super_statement(statement, location);
110     }
111 
visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location)112     fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
113         // When we see `X = ...`, then kill borrows of
114         // `(*X).foo` and so forth.
115         self.record_killed_borrows_for_place(*place, location);
116 
117         self.super_assign(place, rvalue, location);
118     }
119 
visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location)120     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
121         if let Some(all_facts) = self.all_facts {
122             let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
123             all_facts.cfg_edge.push((
124                 self.location_table.start_index(location),
125                 self.location_table.mid_index(location),
126             ));
127 
128             let successor_blocks = terminator.successors();
129             all_facts.cfg_edge.reserve(successor_blocks.size_hint().0);
130             for successor_block in successor_blocks {
131                 all_facts.cfg_edge.push((
132                     self.location_table.mid_index(location),
133                     self.location_table.start_index(successor_block.start_location()),
134                 ));
135             }
136         }
137 
138         // A `Call` terminator's return value can be a local which has borrows,
139         // so we need to record those as `killed` as well.
140         if let TerminatorKind::Call { destination, .. } = terminator.kind {
141             self.record_killed_borrows_for_place(destination, location);
142         }
143 
144         self.super_terminator(terminator, location);
145     }
146 
visit_ascribe_user_ty( &mut self, _place: &Place<'tcx>, _variance: ty::Variance, _user_ty: &UserTypeProjection, _location: Location, )147     fn visit_ascribe_user_ty(
148         &mut self,
149         _place: &Place<'tcx>,
150         _variance: ty::Variance,
151         _user_ty: &UserTypeProjection,
152         _location: Location,
153     ) {
154     }
155 }
156 
157 impl<'cx, 'tcx> ConstraintGeneration<'cx, 'tcx> {
158     /// Some variable with type `live_ty` is "regular live" at
159     /// `location` -- i.e., it may be used later. This means that all
160     /// regions appearing in the type `live_ty` must be live at
161     /// `location`.
add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location) where T: TypeVisitable<TyCtxt<'tcx>>,162     fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
163     where
164         T: TypeVisitable<TyCtxt<'tcx>>,
165     {
166         debug!("add_regular_live_constraint(live_ty={:?}, location={:?})", live_ty, location);
167 
168         self.infcx.tcx.for_each_free_region(&live_ty, |live_region| {
169             let vid = live_region.as_var();
170             self.liveness_constraints.add_element(vid, location);
171         });
172     }
173 
174     /// When recording facts for Polonius, records the borrows on the specified place
175     /// as `killed`. For example, when assigning to a local, or on a call's return destination.
record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location)176     fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
177         if let Some(all_facts) = self.all_facts {
178             let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
179 
180             // Depending on the `Place` we're killing:
181             // - if it's a local, or a single deref of a local,
182             //   we kill all the borrows on the local.
183             // - if it's a deeper projection, we have to filter which
184             //   of the borrows are killed: the ones whose `borrowed_place`
185             //   conflicts with the `place`.
186             match place.as_ref() {
187                 PlaceRef { local, projection: &[] }
188                 | PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
189                     debug!(
190                         "Recording `killed` facts for borrows of local={:?} at location={:?}",
191                         local, location
192                     );
193 
194                     record_killed_borrows_for_local(
195                         all_facts,
196                         self.borrow_set,
197                         self.location_table,
198                         local,
199                         location,
200                     );
201                 }
202 
203                 PlaceRef { local, projection: &[.., _] } => {
204                     // Kill conflicting borrows of the innermost local.
205                     debug!(
206                         "Recording `killed` facts for borrows of \
207                             innermost projected local={:?} at location={:?}",
208                         local, location
209                     );
210 
211                     if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
212                         for &borrow_index in borrow_indices {
213                             let places_conflict = places_conflict::places_conflict(
214                                 self.infcx.tcx,
215                                 self.body,
216                                 self.borrow_set[borrow_index].borrowed_place,
217                                 place,
218                                 places_conflict::PlaceConflictBias::NoOverlap,
219                             );
220 
221                             if places_conflict {
222                                 let location_index = self.location_table.mid_index(location);
223                                 all_facts.loan_killed_at.push((borrow_index, location_index));
224                             }
225                         }
226                     }
227                 }
228             }
229         }
230     }
231 }
232 
233 /// When recording facts for Polonius, records the borrows on the specified local as `killed`.
record_killed_borrows_for_local( all_facts: &mut AllFacts, borrow_set: &BorrowSet<'_>, location_table: &LocationTable, local: Local, location: Location, )234 fn record_killed_borrows_for_local(
235     all_facts: &mut AllFacts,
236     borrow_set: &BorrowSet<'_>,
237     location_table: &LocationTable,
238     local: Local,
239     location: Location,
240 ) {
241     if let Some(borrow_indices) = borrow_set.local_map.get(&local) {
242         all_facts.loan_killed_at.reserve(borrow_indices.len());
243         for &borrow_index in borrow_indices {
244             let location_index = location_table.mid_index(location);
245             all_facts.loan_killed_at.push((borrow_index, location_index));
246         }
247     }
248 }
249