]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
2bf531d1d3e56660f45b6138024cab33cb14c70e
[rust.git] / src / librustc_mir / borrow_check / nll / explain_borrow / mod.rs
1 // Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use borrow_check::borrow_set::BorrowData;
12 use borrow_check::error_reporting::UseSpans;
13 use borrow_check::nll::ConstraintDescription;
14 use borrow_check::nll::region_infer::{Cause, RegionName};
15 use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
16 use rustc::ty::{self, TyCtxt};
17 use rustc::mir::{
18     CastKind, ConstraintCategory, FakeReadCause, Local, Location, Mir, Operand,
19     Place, Projection, ProjectionElem, Rvalue, Statement, StatementKind,
20     TerminatorKind
21 };
22 use rustc_errors::DiagnosticBuilder;
23 use syntax_pos::Span;
24
25 mod find_use;
26
27 pub(in borrow_check) enum BorrowExplanation {
28     UsedLater(LaterUseKind, Span),
29     UsedLaterInLoop(LaterUseKind, Span),
30     UsedLaterWhenDropped {
31         drop_loc: Location,
32         dropped_local: Local,
33         should_note_order: bool,
34     },
35     MustBeValidFor {
36         category: ConstraintCategory,
37         from_closure: bool,
38         span: Span,
39         region_name: RegionName,
40         opt_place_desc: Option<String>,
41     },
42     Unexplained,
43 }
44
45 #[derive(Clone, Copy)]
46 pub(in borrow_check) enum LaterUseKind {
47     TraitCapture,
48     ClosureCapture,
49     Call,
50     FakeLetRead,
51     Other,
52 }
53
54 impl BorrowExplanation {
55     pub(in borrow_check) fn add_explanation_to_diagnostic<'cx, 'gcx, 'tcx>(
56         &self,
57         tcx: TyCtxt<'cx, 'gcx, 'tcx>,
58         mir: &Mir<'tcx>,
59         err: &mut DiagnosticBuilder<'_>,
60         borrow_desc: &str,
61     ) {
62         match *self {
63             BorrowExplanation::UsedLater(later_use_kind, var_or_use_span) => {
64                 let message = match later_use_kind {
65                     LaterUseKind::TraitCapture => "borrow later captured here by trait object",
66                     LaterUseKind::ClosureCapture => "borrow later captured here by closure",
67                     LaterUseKind::Call =>  "borrow later used by call",
68                     LaterUseKind::FakeLetRead => "borrow later stored here",
69                     LaterUseKind::Other => "borrow later used here",
70                 };
71                 err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
72             },
73             BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span) => {
74                 let message = match later_use_kind {
75                     LaterUseKind::TraitCapture =>
76                         "borrow captured here by trait object, in later iteration of loop",
77                     LaterUseKind::ClosureCapture =>
78                         "borrow captured here by closure, in later iteration of loop",
79                     LaterUseKind::Call =>  "borrow used by call, in later iteration of loop",
80                     LaterUseKind::FakeLetRead => "borrow later stored here",
81                     LaterUseKind::Other => "borrow used here, in later iteration of loop",
82                 };
83                 err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
84             },
85             BorrowExplanation::UsedLaterWhenDropped { drop_loc, dropped_local,
86                                                       should_note_order } =>
87             {
88                 let local_decl = &mir.local_decls[dropped_local];
89                 let (dtor_desc, type_desc) = match local_decl.ty.sty {
90                     // If type is an ADT that implements Drop, then
91                     // simplify output by reporting just the ADT name.
92                     ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() =>
93                         ("`Drop` code", format!("type `{}`", tcx.item_path_str(adt.did))),
94
95                     // Otherwise, just report the whole type (and use
96                     // the intentionally fuzzy phrase "destructor")
97                     ty::Closure(..) =>
98                         ("destructor", "closure".to_owned()),
99                     ty::Generator(..) =>
100                         ("destructor", "generator".to_owned()),
101
102                     _ => ("destructor", format!("type `{}`", local_decl.ty)),
103                 };
104
105                 match local_decl.name {
106                     Some(local_name) => {
107                         let message =
108                             format!("{B}borrow might be used here, when `{LOC}` is dropped \
109                                      and runs the {DTOR} for {TYPE}",
110                                     B=borrow_desc, LOC=local_name, TYPE=type_desc, DTOR=dtor_desc);
111                         err.span_label(mir.source_info(drop_loc).span, message);
112
113                         if should_note_order {
114                             err.note(
115                                 "values in a scope are dropped \
116                                  in the opposite order they are defined",
117                             );
118                         }
119                     }
120                     None => {
121                         err.span_label(local_decl.source_info.span,
122                                        format!("a temporary with access to the {B}borrow \
123                                                 is created here ...",
124                                                B=borrow_desc));
125                         let message =
126                             format!("... and the {B}borrow might be used here, \
127                                      when that temporary is dropped \
128                                      and runs the {DTOR} for {TYPE}",
129                                     B=borrow_desc, TYPE=type_desc, DTOR=dtor_desc);
130                         err.span_label(mir.source_info(drop_loc).span, message);
131
132                         if let Some(info) = &local_decl.is_block_tail {
133                             // FIXME: use span_suggestion instead, highlighting the
134                             // whole block tail expression.
135                             let msg = if info.tail_result_is_ignored {
136                                 "The temporary is part of an expression at the end of a block. \
137                                  Consider adding semicolon after the expression so its temporaries \
138                                  are dropped sooner, before the local variables declared by the \
139                                  block are dropped."
140                             } else {
141                                 "The temporary is part of an expression at the end of a block. \
142                                  Consider forcing this temporary to be dropped sooner, before \
143                                  the block's local variables are dropped. \
144                                  For example, you could save the expression's value in a new \
145                                  local variable `x` and then make `x` be the expression \
146                                  at the end of the block."
147                             };
148
149                             err.note(msg);
150                         }
151                     }
152                 }
153             },
154             BorrowExplanation::MustBeValidFor {
155                 category,
156                 span,
157                 ref region_name,
158                 ref opt_place_desc,
159                 from_closure: _,
160             } => {
161                 region_name.highlight_region_name(err);
162
163                 if let Some(desc) = opt_place_desc {
164                     err.span_label(span, format!(
165                         "{}requires that `{}` is borrowed for `{}`",
166                         category.description(), desc, region_name,
167                     ));
168                 } else {
169                     err.span_label(span, format!(
170                         "{}requires that {}borrow lasts for `{}`",
171                         category.description(), borrow_desc, region_name,
172                     ));
173                 };
174             },
175             _ => {},
176         }
177     }
178 }
179
180 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
181     /// Returns structured explanation for *why* the borrow contains the
182     /// point from `context`. This is key for the "3-point errors"
183     /// [described in the NLL RFC][d].
184     ///
185     /// # Parameters
186     ///
187     /// - `borrow`: the borrow in question
188     /// - `context`: where the borrow occurs
189     /// - `kind_place`: if Some, this describes the statement that triggered the error.
190     ///   - first half is the kind of write, if any, being performed
191     ///   - second half is the place being accessed
192     ///
193     /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
194     pub(in borrow_check) fn explain_why_borrow_contains_point(
195         &self,
196         context: Context,
197         borrow: &BorrowData<'tcx>,
198         kind_place: Option<(WriteKind, &Place<'tcx>)>,
199     ) -> BorrowExplanation {
200         debug!(
201             "explain_why_borrow_contains_point(context={:?}, borrow={:?}, kind_place={:?})",
202             context, borrow, kind_place
203         );
204
205         let regioncx = &self.nonlexical_regioncx;
206         let mir = self.mir;
207         let tcx = self.infcx.tcx;
208
209         let borrow_region_vid = regioncx.to_region_vid(borrow.region);
210         debug!(
211             "explain_why_borrow_contains_point: borrow_region_vid={:?}",
212             borrow_region_vid
213         );
214
215         let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, context.loc);
216         debug!(
217             "explain_why_borrow_contains_point: region_sub={:?}",
218             region_sub
219         );
220
221          match find_use::find(mir, regioncx, tcx, region_sub, context.loc) {
222             Some(Cause::LiveVar(local, location)) => {
223                 let span = mir.source_info(location).span;
224                 let spans = self.move_spans(&Place::Local(local), location)
225                     .or_else(|| self.borrow_spans(span, location));
226
227                 if self.is_borrow_location_in_loop(context.loc) {
228                     let later_use = self.later_use_kind(borrow, spans, location);
229                     BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1)
230                 } else {
231                     // Check if the location represents a `FakeRead`, and adapt the error
232                     // message to the `FakeReadCause` it is from: in particular,
233                     // the ones inserted in optimized `let var = <expr>` patterns.
234                     let later_use = self.later_use_kind(borrow, spans, location);
235                     BorrowExplanation::UsedLater(later_use.0, later_use.1)
236                 }
237             }
238
239              Some(Cause::DropVar(local, location)) => {
240                  let mut should_note_order = false;
241                  if mir.local_decls[local].name.is_some() {
242                      if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
243                          if let Place::Local(borrowed_local) = place {
244                              let dropped_local_scope = mir.local_decls[local].visibility_scope;
245                              let borrowed_local_scope =
246                                  mir.local_decls[*borrowed_local].visibility_scope;
247
248                              if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope)
249                                  && local != *borrowed_local
250                              {
251                                  should_note_order = true;
252                              }
253                          }
254                      }
255                  }
256
257                  BorrowExplanation::UsedLaterWhenDropped {
258                      drop_loc: location,
259                      dropped_local: local,
260                      should_note_order,
261                  }
262             }
263
264             None => if let Some(region) = regioncx.to_error_region_vid(borrow_region_vid) {
265                 let (category, from_closure, span, region_name) = self
266                     .nonlexical_regioncx
267                     .free_region_constraint_info(
268                         self.mir,
269                         self.mir_def_id,
270                         self.infcx,
271                         borrow_region_vid,
272                         region,
273                     );
274                 let opt_place_desc = self.describe_place(&borrow.borrowed_place);
275                 BorrowExplanation::MustBeValidFor {
276                     category,
277                     from_closure,
278                     span,
279                     region_name,
280                     opt_place_desc,
281                 }
282             } else {
283                 BorrowExplanation::Unexplained
284             }
285         }
286     }
287
288     /// Check if a borrow location is within a loop.
289     fn is_borrow_location_in_loop(
290         &self,
291         borrow_location: Location,
292     ) -> bool {
293         let mut visited_locations = Vec::new();
294         let mut pending_locations = vec![ borrow_location ];
295         debug!("is_in_loop: borrow_location={:?}", borrow_location);
296
297         while let Some(location) = pending_locations.pop() {
298             debug!("is_in_loop: location={:?} pending_locations={:?} visited_locations={:?}",
299                    location, pending_locations, visited_locations);
300             if location == borrow_location && visited_locations.contains(&borrow_location) {
301                 // We've managed to return to where we started (and this isn't the start of the
302                 // search).
303                 debug!("is_in_loop: found!");
304                 return true;
305             }
306
307             // Skip locations we've been.
308             if visited_locations.contains(&location) { continue; }
309
310             let block = &self.mir.basic_blocks()[location.block];
311             if location.statement_index ==  block.statements.len() {
312                 // Add start location of the next blocks to pending locations.
313                 match block.terminator().kind {
314                     TerminatorKind::Goto { target } => {
315                         pending_locations.push(target.start_location());
316                     },
317                     TerminatorKind::SwitchInt { ref targets, .. } => {
318                         pending_locations.extend(
319                             targets.into_iter().map(|target| target.start_location()));
320                     },
321                     TerminatorKind::Drop { target, unwind, .. } |
322                     TerminatorKind::DropAndReplace { target, unwind, .. } |
323                     TerminatorKind::Assert { target, cleanup: unwind, .. } |
324                     TerminatorKind::Yield { resume: target, drop: unwind, .. } |
325                     TerminatorKind::FalseUnwind { real_target: target, unwind, .. } => {
326                         pending_locations.push(target.start_location());
327                         if let Some(unwind) = unwind {
328                             pending_locations.push(unwind.start_location());
329                         }
330                     },
331                     TerminatorKind::Call { ref destination, cleanup, .. } => {
332                         if let Some((_, destination)) = destination {
333                             pending_locations.push(destination.start_location());
334                         }
335                         if let Some(cleanup) = cleanup {
336                             pending_locations.push(cleanup.start_location());
337                         }
338                     },
339                     TerminatorKind::FalseEdges { real_target, ref imaginary_targets, .. } => {
340                         pending_locations.push(real_target.start_location());
341                         pending_locations.extend(
342                             imaginary_targets.into_iter().map(|target| target.start_location()));
343                     },
344                     _ => {},
345                 }
346             } else {
347                 // Add the next statement to pending locations.
348                 pending_locations.push(location.successor_within_block());
349             }
350
351             // Keep track of where we have visited.
352             visited_locations.push(location);
353         }
354
355         false
356     }
357
358     /// Determine how the borrow was later used.
359     fn later_use_kind(
360         &self,
361         borrow: &BorrowData<'tcx>,
362         use_spans: UseSpans,
363         location: Location
364     ) -> (LaterUseKind, Span) {
365         match use_spans {
366             UseSpans::ClosureUse { var_span, .. } => {
367                 // Used in a closure.
368                 (LaterUseKind::ClosureCapture, var_span)
369             },
370             UseSpans::OtherUse(span) => {
371                 let block = &self.mir.basic_blocks()[location.block];
372
373                 let kind = if let Some(&Statement {
374                     kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
375                     ..
376                 }) = block.statements.get(location.statement_index) {
377                     LaterUseKind::FakeLetRead
378                 } else if self.was_captured_by_trait_object(borrow) {
379                     LaterUseKind::TraitCapture
380                 } else if location.statement_index == block.statements.len() {
381                     if let TerminatorKind::Call {
382                         ref func, from_hir_call: true, ..
383                     } = block.terminator().kind {
384                         // Just point to the function, to reduce the chance of overlapping spans.
385                         let function_span = match func {
386                             Operand::Constant(c) => c.span,
387                             Operand::Copy(Place::Local(l)) | Operand::Move(Place::Local(l)) => {
388                                 let local_decl = &self.mir.local_decls[*l];
389                                 if local_decl.name.is_none() {
390                                     local_decl.source_info.span
391                                 } else {
392                                     span
393                                 }
394                             },
395                             _ => span,
396                         };
397                         return (LaterUseKind::Call, function_span);
398                     } else {
399                         LaterUseKind::Other
400                     }
401                 } else {
402                     LaterUseKind::Other
403                 };
404
405                 (kind, span)
406             }
407         }
408     }
409
410     /// Check if a borrowed value was captured by a trait object. We do this by
411     /// looking forward in the MIR from the reserve location and checking if we see
412     /// a unsized cast to a trait object on our data.
413     fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
414         // Start at the reserve location, find the place that we want to see cast to a trait object.
415         let location = borrow.reserve_location;
416         let block = &self.mir[location.block];
417         let stmt = block.statements.get(location.statement_index);
418         debug!("was_captured_by_trait_object: location={:?} stmt={:?}", location, stmt);
419
420         // We make a `queue` vector that has the locations we want to visit. As of writing, this
421         // will only ever have one item at any given time, but by using a vector, we can pop from
422         // it which simplifies the termination logic.
423         let mut queue = vec![location];
424         let mut target = if let Some(&Statement {
425             kind: StatementKind::Assign(Place::Local(local), _),
426             ..
427         }) = stmt {
428             local
429         } else {
430             return false;
431         };
432
433         debug!("was_captured_by_trait: target={:?} queue={:?}", target, queue);
434         while let Some(current_location) = queue.pop() {
435             debug!("was_captured_by_trait: target={:?}", target);
436             let block = &self.mir[current_location.block];
437             // We need to check the current location to find out if it is a terminator.
438             let is_terminator = current_location.statement_index == block.statements.len();
439             if !is_terminator {
440                 let stmt = &block.statements[current_location.statement_index];
441                 debug!("was_captured_by_trait_object: stmt={:?}", stmt);
442
443                 // The only kind of statement that we care about is assignments...
444                 if let StatementKind::Assign(
445                     place,
446                     box rvalue,
447                 ) = &stmt.kind {
448                     let into = match place {
449                         Place::Local(into) => into,
450                         Place::Projection(box Projection {
451                             base: Place::Local(into),
452                             elem: ProjectionElem::Deref,
453                         }) => into,
454                         _ =>  {
455                             // Continue at the next location.
456                             queue.push(current_location.successor_within_block());
457                             continue;
458                         },
459                     };
460
461                     match rvalue {
462                         // If we see a use, we should check whether it is our data, and if so
463                         // update the place that we're looking for to that new place.
464                         Rvalue::Use(operand) => match operand {
465                             Operand::Copy(Place::Local(from)) |
466                             Operand::Move(Place::Local(from)) if *from == target => {
467                                 target = *into;
468                             },
469                             _ => {},
470                         },
471                         // If we see a unsized cast, then if it is our data we should check
472                         // whether it is being cast to a trait object.
473                         Rvalue::Cast(CastKind::Unsize, operand, ty) => match operand {
474                             Operand::Copy(Place::Local(from)) |
475                             Operand::Move(Place::Local(from)) if *from == target => {
476                                 debug!("was_captured_by_trait_object: ty={:?}", ty);
477                                 // Check the type for a trait object.
478                                 return match ty.sty {
479                                     // `&dyn Trait`
480                                     ty::TyKind::Ref(_, ty, _) if ty.is_trait() => true,
481                                     // `Box<dyn Trait>`
482                                     _ if ty.is_box() && ty.boxed_ty().is_trait() =>
483                                         true,
484                                     // `dyn Trait`
485                                     _ if ty.is_trait() => true,
486                                     // Anything else.
487                                     _ => false,
488                                 };
489                             },
490                             _ => return false,
491                         },
492                         _ => {},
493                     }
494                 }
495
496                 // Continue at the next location.
497                 queue.push(current_location.successor_within_block());
498             } else {
499                 // The only thing we need to do for terminators is progress to the next block.
500                 let terminator = block.terminator();
501                 debug!("was_captured_by_trait_object: terminator={:?}", terminator);
502
503                 if let TerminatorKind::Call {
504                     destination: Some((Place::Local(dest), block)),
505                     args,
506                     ..
507                 } = &terminator.kind {
508                     debug!(
509                         "was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
510                         target, dest, args
511                     );
512                     // Check if one of the arguments to this function is the target place.
513                     let found_target = args.iter().any(|arg| {
514                         if let Operand::Move(Place::Local(potential)) = arg {
515                             *potential == target
516                         } else {
517                             false
518                         }
519                     });
520
521                     // If it is, follow this to the next block and update the target.
522                     if found_target {
523                         target = *dest;
524                         queue.push(block.start_location());
525                     }
526                 }
527             }
528
529             debug!("was_captured_by_trait: queue={:?}", queue);
530         }
531
532         // We didn't find anything and ran out of locations to check.
533         false
534     }
535 }