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