]>
Commit | Line | Data |
---|---|---|
60c5eb7d XL |
1 | //! Print diagnostics to explain why values are borrowed. |
2 | ||
9fa01778 XL |
3 | use std::collections::VecDeque; |
4 | ||
9fa01778 | 5 | use rustc_data_structures::fx::FxHashSet; |
5e7ed085 | 6 | use rustc_errors::{Applicability, Diagnostic}; |
dfeec247 | 7 | use rustc_index::vec::IndexVec; |
5869c6ff | 8 | use rustc_infer::infer::NllRegionVariableOrigin; |
ba9703b0 XL |
9 | use rustc_middle::mir::{ |
10 | Body, CastKind, ConstraintCategory, FakeReadCause, Local, Location, Operand, Place, Rvalue, | |
11 | Statement, StatementKind, TerminatorKind, | |
12 | }; | |
13 | use rustc_middle::ty::adjustment::PointerCast; | |
14 | use rustc_middle::ty::{self, RegionVid, TyCtxt}; | |
dfeec247 | 15 | use rustc_span::symbol::Symbol; |
3c0e092e | 16 | use rustc_span::{sym, DesugaringKind, Span}; |
60c5eb7d | 17 | |
c295e0f8 XL |
18 | use crate::region_infer::BlameConstraint; |
19 | use crate::{ | |
dfeec247 XL |
20 | borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt, |
21 | WriteKind, | |
60c5eb7d | 22 | }; |
8faf50e0 | 23 | |
dfeec247 | 24 | use super::{find_use, RegionName, UseSpans}; |
ff7c6d11 | 25 | |
e74abb32 | 26 | #[derive(Debug)] |
c295e0f8 | 27 | pub(crate) enum BorrowExplanation { |
17df50a5 XL |
28 | UsedLater(LaterUseKind, Span, Option<Span>), |
29 | UsedLaterInLoop(LaterUseKind, Span, Option<Span>), | |
0bf4aa26 XL |
30 | UsedLaterWhenDropped { |
31 | drop_loc: Location, | |
32 | dropped_local: Local, | |
33 | should_note_order: bool, | |
b7449926 | 34 | }, |
0bf4aa26 XL |
35 | MustBeValidFor { |
36 | category: ConstraintCategory, | |
37 | from_closure: bool, | |
38 | span: Span, | |
39 | region_name: RegionName, | |
40 | opt_place_desc: Option<String>, | |
b7449926 | 41 | }, |
0bf4aa26 XL |
42 | Unexplained, |
43 | } | |
44 | ||
e74abb32 | 45 | #[derive(Clone, Copy, Debug)] |
c295e0f8 | 46 | pub(crate) enum LaterUseKind { |
0bf4aa26 XL |
47 | TraitCapture, |
48 | ClosureCapture, | |
49 | Call, | |
50 | FakeLetRead, | |
51 | Other, | |
52 | } | |
53 | ||
54 | impl BorrowExplanation { | |
c295e0f8 | 55 | pub(crate) fn is_explained(&self) -> bool { |
3c0e092e | 56 | !matches!(self, BorrowExplanation::Unexplained) |
0731742a | 57 | } |
c295e0f8 | 58 | pub(crate) fn add_explanation_to_diagnostic<'tcx>( |
0bf4aa26 | 59 | &self, |
dc9dc135 XL |
60 | tcx: TyCtxt<'tcx>, |
61 | body: &Body<'tcx>, | |
60c5eb7d | 62 | local_names: &IndexVec<Local, Option<Symbol>>, |
5e7ed085 | 63 | err: &mut Diagnostic, |
0bf4aa26 | 64 | borrow_desc: &str, |
532ac7d7 | 65 | borrow_span: Option<Span>, |
136023e0 | 66 | multiple_borrow_span: Option<(Span, Span)>, |
0bf4aa26 XL |
67 | ) { |
68 | match *self { | |
17df50a5 | 69 | BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => { |
0bf4aa26 | 70 | let message = match later_use_kind { |
532ac7d7 XL |
71 | LaterUseKind::TraitCapture => "captured here by trait object", |
72 | LaterUseKind::ClosureCapture => "captured here by closure", | |
73 | LaterUseKind::Call => "used by call", | |
74 | LaterUseKind::FakeLetRead => "stored here", | |
75 | LaterUseKind::Other => "used here", | |
0bf4aa26 | 76 | }; |
17df50a5 XL |
77 | // We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same |
78 | if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) { | |
79 | if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) { | |
80 | err.span_label( | |
81 | var_or_use_span, | |
82 | format!("{}borrow later {}", borrow_desc, message), | |
83 | ); | |
84 | } | |
85 | } else { | |
86 | // path_span must be `Some` as otherwise the if condition is true | |
87 | let path_span = path_span.unwrap(); | |
88 | // path_span is only present in the case of closure capture | |
89 | assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture)); | |
90 | if !borrow_span.map_or(false, |sp| sp.overlaps(var_or_use_span)) { | |
91 | let path_label = "used here by closure"; | |
92 | let capture_kind_label = message; | |
93 | err.span_label( | |
94 | var_or_use_span, | |
95 | format!("{}borrow later {}", borrow_desc, capture_kind_label), | |
96 | ); | |
97 | err.span_label(path_span, path_label); | |
98 | } | |
532ac7d7 | 99 | } |
9fa01778 | 100 | } |
17df50a5 | 101 | BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span) => { |
0bf4aa26 | 102 | let message = match later_use_kind { |
9fa01778 XL |
103 | LaterUseKind::TraitCapture => { |
104 | "borrow captured here by trait object, in later iteration of loop" | |
105 | } | |
106 | LaterUseKind::ClosureCapture => { | |
107 | "borrow captured here by closure, in later iteration of loop" | |
108 | } | |
109 | LaterUseKind::Call => "borrow used by call, in later iteration of loop", | |
0bf4aa26 XL |
110 | LaterUseKind::FakeLetRead => "borrow later stored here", |
111 | LaterUseKind::Other => "borrow used here, in later iteration of loop", | |
112 | }; | |
17df50a5 XL |
113 | // We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same |
114 | if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) { | |
115 | err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message)); | |
116 | } else { | |
117 | // path_span must be `Some` as otherwise the if condition is true | |
118 | let path_span = path_span.unwrap(); | |
119 | // path_span is only present in the case of closure capture | |
120 | assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture)); | |
121 | if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) { | |
122 | let path_label = "used here by closure"; | |
123 | let capture_kind_label = message; | |
124 | err.span_label( | |
125 | var_or_use_span, | |
126 | format!("{}borrow later {}", borrow_desc, capture_kind_label), | |
127 | ); | |
128 | err.span_label(path_span, path_label); | |
129 | } | |
130 | } | |
9fa01778 XL |
131 | } |
132 | BorrowExplanation::UsedLaterWhenDropped { | |
133 | drop_loc, | |
134 | dropped_local, | |
135 | should_note_order, | |
136 | } => { | |
dc9dc135 | 137 | let local_decl = &body.local_decls[dropped_local]; |
3c0e092e XL |
138 | let mut ty = local_decl.ty; |
139 | if local_decl.source_info.span.desugaring_kind() == Some(DesugaringKind::ForLoop) { | |
140 | if let ty::Adt(adt, substs) = local_decl.ty.kind() { | |
5e7ed085 | 141 | if tcx.is_diagnostic_item(sym::Option, adt.did()) { |
3c0e092e XL |
142 | // in for loop desugaring, only look at the `Some(..)` inner type |
143 | ty = substs.type_at(0); | |
144 | } | |
145 | } | |
146 | } | |
147 | let (dtor_desc, type_desc) = match ty.kind() { | |
0bf4aa26 XL |
148 | // If type is an ADT that implements Drop, then |
149 | // simplify output by reporting just the ADT name. | |
dfeec247 | 150 | ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() => { |
5e7ed085 | 151 | ("`Drop` code", format!("type `{}`", tcx.def_path_str(adt.did()))) |
dfeec247 | 152 | } |
0bf4aa26 XL |
153 | |
154 | // Otherwise, just report the whole type (and use | |
155 | // the intentionally fuzzy phrase "destructor") | |
9fa01778 XL |
156 | ty::Closure(..) => ("destructor", "closure".to_owned()), |
157 | ty::Generator(..) => ("destructor", "generator".to_owned()), | |
0bf4aa26 XL |
158 | |
159 | _ => ("destructor", format!("type `{}`", local_decl.ty)), | |
160 | }; | |
161 | ||
60c5eb7d | 162 | match local_names[dropped_local] { |
dc9dc135 | 163 | Some(local_name) if !local_decl.from_compiler_desugaring() => { |
9fa01778 XL |
164 | let message = format!( |
165 | "{B}borrow might be used here, when `{LOC}` is dropped \ | |
166 | and runs the {DTOR} for {TYPE}", | |
167 | B = borrow_desc, | |
168 | LOC = local_name, | |
169 | TYPE = type_desc, | |
170 | DTOR = dtor_desc | |
171 | ); | |
dc9dc135 | 172 | err.span_label(body.source_info(drop_loc).span, message); |
0bf4aa26 XL |
173 | |
174 | if should_note_order { | |
175 | err.note( | |
176 | "values in a scope are dropped \ | |
177 | in the opposite order they are defined", | |
178 | ); | |
179 | } | |
180 | } | |
dc9dc135 | 181 | _ => { |
9fa01778 XL |
182 | err.span_label( |
183 | local_decl.source_info.span, | |
184 | format!( | |
185 | "a temporary with access to the {B}borrow \ | |
186 | is created here ...", | |
187 | B = borrow_desc | |
188 | ), | |
189 | ); | |
190 | let message = format!( | |
191 | "... and the {B}borrow might be used here, \ | |
192 | when that temporary is dropped \ | |
193 | and runs the {DTOR} for {TYPE}", | |
194 | B = borrow_desc, | |
195 | TYPE = type_desc, | |
196 | DTOR = dtor_desc | |
197 | ); | |
dc9dc135 | 198 | err.span_label(body.source_info(drop_loc).span, message); |
0bf4aa26 XL |
199 | |
200 | if let Some(info) = &local_decl.is_block_tail { | |
f9f354fc | 201 | if info.tail_result_is_ignored { |
136023e0 XL |
202 | // #85581: If the first mutable borrow's scope contains |
203 | // the second borrow, this suggestion isn't helpful. | |
204 | if !multiple_borrow_span | |
205 | .map(|(old, new)| { | |
206 | old.to(info.span.shrink_to_hi()).contains(new) | |
207 | }) | |
208 | .unwrap_or(false) | |
209 | { | |
210 | err.span_suggestion_verbose( | |
211 | info.span.shrink_to_hi(), | |
212 | "consider adding semicolon after the expression so its \ | |
213 | temporaries are dropped sooner, before the local variables \ | |
214 | declared by the block are dropped", | |
215 | ";".to_string(), | |
216 | Applicability::MaybeIncorrect, | |
217 | ); | |
218 | } | |
0bf4aa26 | 219 | } else { |
f9f354fc XL |
220 | err.note( |
221 | "the temporary is part of an expression at the end of a \ | |
222 | block;\nconsider forcing this temporary to be dropped sooner, \ | |
223 | before the block's local variables are dropped", | |
224 | ); | |
225 | err.multipart_suggestion( | |
226 | "for example, you could save the expression's value in a new \ | |
227 | local variable `x` and then make `x` be the expression at the \ | |
228 | end of the block", | |
229 | vec![ | |
230 | (info.span.shrink_to_lo(), "let x = ".to_string()), | |
231 | (info.span.shrink_to_hi(), "; x".to_string()), | |
232 | ], | |
233 | Applicability::MaybeIncorrect, | |
234 | ); | |
0bf4aa26 | 235 | }; |
0bf4aa26 XL |
236 | } |
237 | } | |
238 | } | |
9fa01778 | 239 | } |
0bf4aa26 XL |
240 | BorrowExplanation::MustBeValidFor { |
241 | category, | |
242 | span, | |
243 | ref region_name, | |
244 | ref opt_place_desc, | |
245 | from_closure: _, | |
246 | } => { | |
247 | region_name.highlight_region_name(err); | |
248 | ||
249 | if let Some(desc) = opt_place_desc { | |
9fa01778 XL |
250 | err.span_label( |
251 | span, | |
252 | format!( | |
253 | "{}requires that `{}` is borrowed for `{}`", | |
254 | category.description(), | |
255 | desc, | |
256 | region_name, | |
257 | ), | |
258 | ); | |
0bf4aa26 | 259 | } else { |
9fa01778 XL |
260 | err.span_label( |
261 | span, | |
262 | format!( | |
263 | "{}requires that {}borrow lasts for `{}`", | |
264 | category.description(), | |
265 | borrow_desc, | |
266 | region_name, | |
267 | ), | |
268 | ); | |
0bf4aa26 | 269 | }; |
dfeec247 | 270 | |
ba9703b0 | 271 | self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); |
dfeec247 XL |
272 | } |
273 | _ => {} | |
274 | } | |
275 | } | |
c295e0f8 | 276 | pub(crate) fn add_lifetime_bound_suggestion_to_diagnostic( |
dfeec247 | 277 | &self, |
5e7ed085 | 278 | err: &mut Diagnostic, |
dfeec247 XL |
279 | category: &ConstraintCategory, |
280 | span: Span, | |
281 | region_name: &RegionName, | |
282 | ) { | |
ba9703b0 XL |
283 | if let ConstraintCategory::OpaqueType = category { |
284 | let suggestable_name = | |
285 | if region_name.was_named() { region_name.to_string() } else { "'_".to_string() }; | |
dfeec247 | 286 | |
ba9703b0 XL |
287 | let msg = format!( |
288 | "you can add a bound to the {}to make it last less than `'static` and match `{}`", | |
289 | category.description(), | |
290 | region_name, | |
291 | ); | |
292 | ||
293 | err.span_suggestion_verbose( | |
294 | span.shrink_to_hi(), | |
295 | &msg, | |
296 | format!(" + {}", suggestable_name), | |
297 | Applicability::Unspecified, | |
298 | ); | |
0bf4aa26 XL |
299 | } |
300 | } | |
b7449926 XL |
301 | } |
302 | ||
dc9dc135 | 303 | impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { |
dfeec247 XL |
304 | fn free_region_constraint_info( |
305 | &self, | |
306 | borrow_region: RegionVid, | |
307 | outlived_region: RegionVid, | |
308 | ) -> (ConstraintCategory, bool, Span, Option<RegionName>) { | |
c295e0f8 | 309 | let BlameConstraint { category, from_closure, cause, variance_info: _ } = |
17df50a5 XL |
310 | self.regioncx.best_blame_constraint( |
311 | &self.body, | |
312 | borrow_region, | |
313 | NllRegionVariableOrigin::FreeRegion, | |
314 | |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region), | |
315 | ); | |
dfeec247 XL |
316 | |
317 | let outlived_fr_name = self.give_region_a_name(outlived_region); | |
318 | ||
c295e0f8 | 319 | (category, from_closure, cause.span, outlived_fr_name) |
dfeec247 XL |
320 | } |
321 | ||
0bf4aa26 | 322 | /// Returns structured explanation for *why* the borrow contains the |
48663c56 | 323 | /// point from `location`. This is key for the "3-point errors" |
0531ce1d XL |
324 | /// [described in the NLL RFC][d]. |
325 | /// | |
94b46f34 XL |
326 | /// # Parameters |
327 | /// | |
328 | /// - `borrow`: the borrow in question | |
48663c56 | 329 | /// - `location`: where the borrow occurs |
94b46f34 XL |
330 | /// - `kind_place`: if Some, this describes the statement that triggered the error. |
331 | /// - first half is the kind of write, if any, being performed | |
332 | /// - second half is the place being accessed | |
94b46f34 | 333 | /// |
0531ce1d | 334 | /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points |
c295e0f8 | 335 | pub(crate) fn explain_why_borrow_contains_point( |
b7449926 | 336 | &self, |
48663c56 | 337 | location: Location, |
0531ce1d | 338 | borrow: &BorrowData<'tcx>, |
ba9703b0 | 339 | kind_place: Option<(WriteKind, Place<'tcx>)>, |
0bf4aa26 | 340 | ) -> BorrowExplanation { |
8faf50e0 | 341 | debug!( |
48663c56 XL |
342 | "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})", |
343 | location, borrow, kind_place | |
8faf50e0 XL |
344 | ); |
345 | ||
dfeec247 | 346 | let regioncx = &self.regioncx; |
60c5eb7d | 347 | let body: &Body<'_> = &self.body; |
0bf4aa26 | 348 | let tcx = self.infcx.tcx; |
ff7c6d11 | 349 | |
a1dfa0c6 | 350 | let borrow_region_vid = borrow.region; |
dfeec247 | 351 | debug!("explain_why_borrow_contains_point: borrow_region_vid={:?}", borrow_region_vid); |
94b46f34 | 352 | |
dfeec247 XL |
353 | let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location); |
354 | debug!("explain_why_borrow_contains_point: region_sub={:?}", region_sub); | |
94b46f34 | 355 | |
dc9dc135 | 356 | match find_use::find(body, regioncx, tcx, region_sub, location) { |
0bf4aa26 | 357 | Some(Cause::LiveVar(local, location)) => { |
dc9dc135 | 358 | let span = body.source_info(location).span; |
9fa01778 | 359 | let spans = self |
416331ca | 360 | .move_spans(Place::from(local).as_ref(), location) |
b7449926 | 361 | .or_else(|| self.borrow_spans(span, location)); |
0bf4aa26 | 362 | |
48663c56 | 363 | let borrow_location = location; |
9fa01778 | 364 | if self.is_use_in_later_iteration_of_loop(borrow_location, location) { |
0bf4aa26 | 365 | let later_use = self.later_use_kind(borrow, spans, location); |
17df50a5 | 366 | BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2) |
b7449926 | 367 | } else { |
0bf4aa26 XL |
368 | // Check if the location represents a `FakeRead`, and adapt the error |
369 | // message to the `FakeReadCause` it is from: in particular, | |
370 | // the ones inserted in optimized `let var = <expr>` patterns. | |
371 | let later_use = self.later_use_kind(borrow, spans, location); | |
17df50a5 | 372 | BorrowExplanation::UsedLater(later_use.0, later_use.1, later_use.2) |
0bf4aa26 | 373 | } |
b7449926 | 374 | } |
8faf50e0 | 375 | |
9fa01778 XL |
376 | Some(Cause::DropVar(local, location)) => { |
377 | let mut should_note_order = false; | |
5e7ed085 FG |
378 | if self.local_names[local].is_some() |
379 | && let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place | |
380 | && let Some(borrowed_local) = place.as_local() | |
381 | && self.local_names[borrowed_local].is_some() && local != borrowed_local | |
382 | { | |
383 | should_note_order = true; | |
9fa01778 XL |
384 | } |
385 | ||
386 | BorrowExplanation::UsedLaterWhenDropped { | |
387 | drop_loc: location, | |
388 | dropped_local: local, | |
389 | should_note_order, | |
390 | } | |
b7449926 | 391 | } |
0bf4aa26 | 392 | |
9fa01778 | 393 | None => { |
dfeec247 | 394 | if let Some(region) = self.to_error_region_vid(borrow_region_vid) { |
9fa01778 | 395 | let (category, from_closure, span, region_name) = |
dfeec247 | 396 | self.free_region_constraint_info(borrow_region_vid, region); |
9fa01778 | 397 | if let Some(region_name) = region_name { |
dfeec247 | 398 | let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref()); |
9fa01778 XL |
399 | BorrowExplanation::MustBeValidFor { |
400 | category, | |
401 | from_closure, | |
402 | span, | |
403 | region_name, | |
404 | opt_place_desc, | |
405 | } | |
406 | } else { | |
dfeec247 XL |
407 | debug!( |
408 | "explain_why_borrow_contains_point: \ | |
409 | Could not generate a region name" | |
410 | ); | |
9fa01778 | 411 | BorrowExplanation::Unexplained |
0731742a XL |
412 | } |
413 | } else { | |
dfeec247 XL |
414 | debug!( |
415 | "explain_why_borrow_contains_point: \ | |
416 | Could not generate an error region vid" | |
417 | ); | |
0731742a | 418 | BorrowExplanation::Unexplained |
0bf4aa26 | 419 | } |
b7449926 | 420 | } |
b7449926 XL |
421 | } |
422 | } | |
8faf50e0 | 423 | |
9fa01778 XL |
424 | /// true if `borrow_location` can reach `use_location` by going through a loop and |
425 | /// `use_location` is also inside of that loop | |
426 | fn is_use_in_later_iteration_of_loop( | |
b7449926 XL |
427 | &self, |
428 | borrow_location: Location, | |
9fa01778 | 429 | use_location: Location, |
b7449926 | 430 | ) -> bool { |
9fa01778 | 431 | let back_edge = self.reach_through_backedge(borrow_location, use_location); |
dfeec247 | 432 | back_edge.map_or(false, |back_edge| self.can_reach_head_of_loop(use_location, back_edge)) |
9fa01778 | 433 | } |
b7449926 | 434 | |
9fa01778 XL |
435 | /// Returns the outmost back edge if `from` location can reach `to` location passing through |
436 | /// that back edge | |
437 | fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> { | |
438 | let mut visited_locations = FxHashSet::default(); | |
439 | let mut pending_locations = VecDeque::new(); | |
440 | visited_locations.insert(from); | |
441 | pending_locations.push_back(from); | |
442 | debug!("reach_through_backedge: from={:?} to={:?}", from, to,); | |
443 | ||
444 | let mut outmost_back_edge = None; | |
445 | while let Some(location) = pending_locations.pop_front() { | |
446 | debug!( | |
447 | "reach_through_backedge: location={:?} outmost_back_edge={:?} | |
448 | pending_locations={:?} visited_locations={:?}", | |
449 | location, outmost_back_edge, pending_locations, visited_locations | |
450 | ); | |
451 | ||
452 | if location == to && outmost_back_edge.is_some() { | |
453 | // We've managed to reach the use location | |
454 | debug!("reach_through_backedge: found!"); | |
455 | return outmost_back_edge; | |
456 | } | |
b7449926 | 457 | |
dc9dc135 | 458 | let block = &self.body.basic_blocks()[location.block]; |
9fa01778 XL |
459 | |
460 | if location.statement_index < block.statements.len() { | |
461 | let successor = location.successor_within_block(); | |
462 | if visited_locations.insert(successor) { | |
463 | pending_locations.push_back(successor); | |
ff7c6d11 | 464 | } |
b7449926 | 465 | } else { |
9fa01778 XL |
466 | pending_locations.extend( |
467 | block | |
468 | .terminator() | |
469 | .successors() | |
dfeec247 | 470 | .map(|bb| Location { statement_index: 0, block: *bb }) |
9fa01778 XL |
471 | .filter(|s| visited_locations.insert(*s)) |
472 | .map(|s| { | |
473 | if self.is_back_edge(location, s) { | |
474 | match outmost_back_edge { | |
475 | None => { | |
476 | outmost_back_edge = Some(location); | |
477 | } | |
478 | ||
479 | Some(back_edge) | |
480 | if location.dominates(back_edge, &self.dominators) => | |
481 | { | |
482 | outmost_back_edge = Some(location); | |
483 | } | |
484 | ||
485 | Some(_) => {} | |
486 | } | |
487 | } | |
488 | ||
489 | s | |
490 | }), | |
491 | ); | |
ff7c6d11 | 492 | } |
9fa01778 XL |
493 | } |
494 | ||
495 | None | |
496 | } | |
497 | ||
498 | /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the | |
499 | /// intermediate nodes | |
500 | fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool { | |
501 | self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default()) | |
502 | } | |
503 | ||
504 | fn find_loop_head_dfs( | |
505 | &self, | |
506 | from: Location, | |
507 | loop_head: Location, | |
508 | visited_locations: &mut FxHashSet<Location>, | |
509 | ) -> bool { | |
510 | visited_locations.insert(from); | |
511 | ||
512 | if from == loop_head { | |
513 | return true; | |
514 | } | |
515 | ||
516 | if loop_head.dominates(from, &self.dominators) { | |
dc9dc135 | 517 | let block = &self.body.basic_blocks()[from.block]; |
9fa01778 XL |
518 | |
519 | if from.statement_index < block.statements.len() { | |
520 | let successor = from.successor_within_block(); | |
521 | ||
522 | if !visited_locations.contains(&successor) | |
523 | && self.find_loop_head_dfs(successor, loop_head, visited_locations) | |
524 | { | |
525 | return true; | |
526 | } | |
527 | } else { | |
528 | for bb in block.terminator().successors() { | |
dfeec247 | 529 | let successor = Location { statement_index: 0, block: *bb }; |
b7449926 | 530 | |
9fa01778 XL |
531 | if !visited_locations.contains(&successor) |
532 | && self.find_loop_head_dfs(successor, loop_head, visited_locations) | |
533 | { | |
534 | return true; | |
535 | } | |
536 | } | |
537 | } | |
ff7c6d11 | 538 | } |
b7449926 XL |
539 | |
540 | false | |
ff7c6d11 | 541 | } |
b7449926 | 542 | |
9fa01778 XL |
543 | /// True if an edge `source -> target` is a backedge -- in other words, if the target |
544 | /// dominates the source. | |
545 | fn is_back_edge(&self, source: Location, target: Location) -> bool { | |
e1599b0c | 546 | target.dominates(source, &self.dominators) |
9fa01778 XL |
547 | } |
548 | ||
0bf4aa26 | 549 | /// Determine how the borrow was later used. |
17df50a5 XL |
550 | /// First span returned points to the location of the conflicting use |
551 | /// Second span if `Some` is returned in the case of closures and points | |
552 | /// to the use of the path | |
0bf4aa26 XL |
553 | fn later_use_kind( |
554 | &self, | |
555 | borrow: &BorrowData<'tcx>, | |
1b1a35ee | 556 | use_spans: UseSpans<'tcx>, |
9fa01778 | 557 | location: Location, |
17df50a5 | 558 | ) -> (LaterUseKind, Span, Option<Span>) { |
0bf4aa26 | 559 | match use_spans { |
17df50a5 | 560 | UseSpans::ClosureUse { capture_kind_span, path_span, .. } => { |
0bf4aa26 | 561 | // Used in a closure. |
17df50a5 | 562 | (LaterUseKind::ClosureCapture, capture_kind_span, Some(path_span)) |
9fa01778 | 563 | } |
f035d41b XL |
564 | UseSpans::PatUse(span) |
565 | | UseSpans::OtherUse(span) | |
566 | | UseSpans::FnSelfUse { var_span: span, .. } => { | |
dc9dc135 | 567 | let block = &self.body.basic_blocks()[location.block]; |
0bf4aa26 XL |
568 | |
569 | let kind = if let Some(&Statement { | |
cdc7bbd5 | 570 | kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)), |
0bf4aa26 | 571 | .. |
9fa01778 XL |
572 | }) = block.statements.get(location.statement_index) |
573 | { | |
0bf4aa26 XL |
574 | LaterUseKind::FakeLetRead |
575 | } else if self.was_captured_by_trait_object(borrow) { | |
576 | LaterUseKind::TraitCapture | |
577 | } else if location.statement_index == block.statements.len() { | |
dfeec247 XL |
578 | if let TerminatorKind::Call { ref func, from_hir_call: true, .. } = |
579 | block.terminator().kind | |
9fa01778 | 580 | { |
0bf4aa26 XL |
581 | // Just point to the function, to reduce the chance of overlapping spans. |
582 | let function_span = match func { | |
583 | Operand::Constant(c) => c.span, | |
dfeec247 | 584 | Operand::Copy(place) | Operand::Move(place) => { |
e74abb32 XL |
585 | if let Some(l) = place.as_local() { |
586 | let local_decl = &self.body.local_decls[l]; | |
60c5eb7d | 587 | if self.local_names[l].is_none() { |
e74abb32 XL |
588 | local_decl.source_info.span |
589 | } else { | |
590 | span | |
591 | } | |
0bf4aa26 XL |
592 | } else { |
593 | span | |
594 | } | |
9fa01778 | 595 | } |
0bf4aa26 | 596 | }; |
17df50a5 | 597 | return (LaterUseKind::Call, function_span, None); |
0bf4aa26 XL |
598 | } else { |
599 | LaterUseKind::Other | |
600 | } | |
601 | } else { | |
602 | LaterUseKind::Other | |
603 | }; | |
604 | ||
17df50a5 | 605 | (kind, span, None) |
0bf4aa26 XL |
606 | } |
607 | } | |
608 | } | |
609 | ||
9fa01778 | 610 | /// Checks if a borrowed value was captured by a trait object. We do this by |
0bf4aa26 | 611 | /// looking forward in the MIR from the reserve location and checking if we see |
94222f64 | 612 | /// an unsized cast to a trait object on our data. |
0bf4aa26 XL |
613 | fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool { |
614 | // Start at the reserve location, find the place that we want to see cast to a trait object. | |
615 | let location = borrow.reserve_location; | |
dc9dc135 | 616 | let block = &self.body[location.block]; |
0bf4aa26 | 617 | let stmt = block.statements.get(location.statement_index); |
dfeec247 | 618 | debug!("was_captured_by_trait_object: location={:?} stmt={:?}", location, stmt); |
0bf4aa26 XL |
619 | |
620 | // We make a `queue` vector that has the locations we want to visit. As of writing, this | |
621 | // will only ever have one item at any given time, but by using a vector, we can pop from | |
622 | // it which simplifies the termination logic. | |
623 | let mut queue = vec![location]; | |
624 | let mut target = if let Some(&Statement { | |
dfeec247 | 625 | kind: StatementKind::Assign(box (ref place, _)), |
0bf4aa26 | 626 | .. |
dfeec247 XL |
627 | }) = stmt |
628 | { | |
e74abb32 XL |
629 | if let Some(local) = place.as_local() { |
630 | local | |
631 | } else { | |
632 | return false; | |
633 | } | |
0bf4aa26 XL |
634 | } else { |
635 | return false; | |
636 | }; | |
637 | ||
dfeec247 | 638 | debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue); |
0bf4aa26 XL |
639 | while let Some(current_location) = queue.pop() { |
640 | debug!("was_captured_by_trait: target={:?}", target); | |
dc9dc135 | 641 | let block = &self.body[current_location.block]; |
0bf4aa26 XL |
642 | // We need to check the current location to find out if it is a terminator. |
643 | let is_terminator = current_location.statement_index == block.statements.len(); | |
644 | if !is_terminator { | |
645 | let stmt = &block.statements[current_location.statement_index]; | |
646 | debug!("was_captured_by_trait_object: stmt={:?}", stmt); | |
647 | ||
648 | // The only kind of statement that we care about is assignments... | |
dfeec247 | 649 | if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind { |
5e7ed085 FG |
650 | let Some(into) = place.local_or_deref_local() else { |
651 | // Continue at the next location. | |
652 | queue.push(current_location.successor_within_block()); | |
653 | continue; | |
0bf4aa26 XL |
654 | }; |
655 | ||
656 | match rvalue { | |
657 | // If we see a use, we should check whether it is our data, and if so | |
658 | // update the place that we're looking for to that new place. | |
659 | Rvalue::Use(operand) => match operand { | |
dfeec247 | 660 | Operand::Copy(place) | Operand::Move(place) => { |
e74abb32 XL |
661 | if let Some(from) = place.as_local() { |
662 | if from == target { | |
663 | target = into; | |
664 | } | |
665 | } | |
9fa01778 XL |
666 | } |
667 | _ => {} | |
0bf4aa26 | 668 | }, |
94222f64 | 669 | // If we see an unsized cast, then if it is our data we should check |
0bf4aa26 | 670 | // whether it is being cast to a trait object. |
dfeec247 XL |
671 | Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), operand, ty) => { |
672 | match operand { | |
673 | Operand::Copy(place) | Operand::Move(place) => { | |
674 | if let Some(from) = place.as_local() { | |
675 | if from == target { | |
676 | debug!("was_captured_by_trait_object: ty={:?}", ty); | |
677 | // Check the type for a trait object. | |
1b1a35ee | 678 | return match ty.kind() { |
dfeec247 XL |
679 | // `&dyn Trait` |
680 | ty::Ref(_, ty, _) if ty.is_trait() => true, | |
681 | // `Box<dyn Trait>` | |
682 | _ if ty.is_box() && ty.boxed_ty().is_trait() => { | |
683 | true | |
684 | } | |
685 | // `dyn Trait` | |
686 | _ if ty.is_trait() => true, | |
687 | // Anything else. | |
688 | _ => false, | |
689 | }; | |
690 | } | |
e74abb32 | 691 | } |
dfeec247 | 692 | return false; |
e74abb32 | 693 | } |
dfeec247 | 694 | _ => return false, |
9fa01778 | 695 | } |
dfeec247 | 696 | } |
9fa01778 | 697 | _ => {} |
0bf4aa26 XL |
698 | } |
699 | } | |
700 | ||
701 | // Continue at the next location. | |
702 | queue.push(current_location.successor_within_block()); | |
703 | } else { | |
704 | // The only thing we need to do for terminators is progress to the next block. | |
705 | let terminator = block.terminator(); | |
706 | debug!("was_captured_by_trait_object: terminator={:?}", terminator); | |
707 | ||
dfeec247 XL |
708 | if let TerminatorKind::Call { destination: Some((place, block)), args, .. } = |
709 | &terminator.kind | |
710 | { | |
e74abb32 XL |
711 | if let Some(dest) = place.as_local() { |
712 | debug!( | |
713 | "was_captured_by_trait_object: target={:?} dest={:?} args={:?}", | |
714 | target, dest, args | |
715 | ); | |
716 | // Check if one of the arguments to this function is the target place. | |
717 | let found_target = args.iter().any(|arg| { | |
718 | if let Operand::Move(place) = arg { | |
719 | if let Some(potential) = place.as_local() { | |
720 | potential == target | |
721 | } else { | |
722 | false | |
723 | } | |
724 | } else { | |
725 | false | |
726 | } | |
727 | }); | |
0bf4aa26 | 728 | |
e74abb32 XL |
729 | // If it is, follow this to the next block and update the target. |
730 | if found_target { | |
731 | target = dest; | |
732 | queue.push(block.start_location()); | |
733 | } | |
0bf4aa26 XL |
734 | } |
735 | } | |
736 | } | |
737 | ||
738 | debug!("was_captured_by_trait: queue={:?}", queue); | |
739 | } | |
740 | ||
741 | // We didn't find anything and ran out of locations to check. | |
742 | false | |
743 | } | |
744 | } |