]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/error_reporting.rs
Refactor explain_borrow to return explanation.
[rust.git] / src / librustc_mir / borrow_check / error_reporting.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::{WriteKind, StorageDeadOrDrop};
12 use borrow_check::prefixes::IsPrefixOf;
13 use borrow_check::nll::explain_borrow::BorrowExplanation;
14 use rustc::middle::region::ScopeTree;
15 use rustc::mir::{
16     BindingForm, BorrowKind, ClearCrossCrate, Field, FakeReadCause, Local,
17     LocalDecl, LocalKind, Location, Operand, Place,
18     ProjectionElem, Rvalue, Statement, StatementKind,
19     VarBindingForm,
20 };
21 use rustc::ty;
22 use rustc_data_structures::fx::FxHashSet;
23 use rustc_data_structures::sync::Lrc;
24 use rustc_errors::{Applicability, DiagnosticBuilder};
25 use syntax_pos::Span;
26
27 use super::borrow_set::BorrowData;
28 use super::{Context, MirBorrowckCtxt};
29 use super::{InitializationRequiringAction, PrefixSet};
30
31 use dataflow::drop_flag_effects;
32 use dataflow::move_paths::indexes::MoveOutIndex;
33 use dataflow::move_paths::MovePathIndex;
34 use util::borrowck_errors::{BorrowckErrors, Origin};
35
36 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
37     pub(super) fn report_use_of_moved_or_uninitialized(
38         &mut self,
39         context: Context,
40         desired_action: InitializationRequiringAction,
41         (place, span): (&Place<'tcx>, Span),
42         mpi: MovePathIndex,
43     ) {
44         debug!(
45             "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} place={:?} \
46             span={:?} mpi={:?}",
47             context, desired_action, place, span, mpi
48         );
49
50         let use_spans = self
51             .move_spans(place, context.loc)
52             .or_else(|| self.borrow_spans(span, context.loc));
53         let span = use_spans.args_or_use();
54
55         let mois = self.get_moved_indexes(context, mpi);
56         debug!("report_use_of_moved_or_uninitialized: mois={:?}", mois);
57
58         if mois.is_empty() {
59             let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
60
61             if self.uninitialized_error_reported.contains(&root_place.clone()) {
62                 debug!(
63                     "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
64                     root_place
65                 );
66                 return;
67             }
68
69             self.uninitialized_error_reported.insert(root_place.clone());
70
71             let item_msg = match self.describe_place_with_options(place, IncludingDowncast(true)) {
72                 Some(name) => format!("`{}`", name),
73                 None => "value".to_owned(),
74             };
75             let mut err = self.tcx.cannot_act_on_uninitialized_variable(
76                 span,
77                 desired_action.as_noun(),
78                 &self
79                     .describe_place_with_options(place, IncludingDowncast(true))
80                     .unwrap_or("_".to_owned()),
81                 Origin::Mir,
82             );
83             err.span_label(span, format!("use of possibly uninitialized {}", item_msg));
84
85             use_spans.var_span_label(
86                 &mut err,
87                 format!("{} occurs due to use in closure", desired_action.as_noun()),
88             );
89
90             err.buffer(&mut self.errors_buffer);
91         } else {
92             if let Some((reported_place, _)) = self.move_error_reported.get(&mois) {
93                 if self.prefixes(&reported_place, PrefixSet::All).any(|p| p == place) {
94                     debug!("report_use_of_moved_or_uninitialized place: error suppressed \
95                            mois={:?}", mois);
96                     return;
97                 }
98             }
99
100             let msg = ""; //FIXME: add "partially " or "collaterally "
101
102             let mut err = self.tcx.cannot_act_on_moved_value(
103                 span,
104                 desired_action.as_noun(),
105                 msg,
106                 self.describe_place_with_options(&place, IncludingDowncast(true)),
107                 Origin::Mir,
108             );
109
110             let mut is_loop_move = false;
111             for moi in &mois {
112                 let move_out = self.move_data.moves[*moi];
113                 let moved_place = &self.move_data.move_paths[move_out.path].place;
114
115                 let move_spans = self.move_spans(moved_place, move_out.source);
116                 let move_span = move_spans.args_or_use();
117
118                 let move_msg = if move_spans.for_closure() {
119                     " into closure"
120                 } else {
121                     ""
122                 };
123
124                 if span == move_span {
125                     err.span_label(
126                         span,
127                         format!("value moved{} here in previous iteration of loop", move_msg),
128                     );
129                     is_loop_move = true;
130                 } else {
131                     err.span_label(move_span, format!("value moved{} here", move_msg));
132                     move_spans.var_span_label(&mut err, "variable moved due to use in closure");
133                 };
134             }
135
136             use_spans.var_span_label(
137                 &mut err,
138                 format!("{} occurs due to use in closure", desired_action.as_noun()),
139             );
140
141             if !is_loop_move {
142                 err.span_label(
143                     span,
144                     format!(
145                         "value {} here after move",
146                         desired_action.as_verb_in_past_tense()
147                     ),
148                 );
149             }
150
151             if let Some(ty) = self.retrieve_type_for_place(place) {
152                 let needs_note = match ty.sty {
153                     ty::Closure(id, _) => {
154                         let tables = self.tcx.typeck_tables_of(id);
155                         let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
156                         let hir_id = self.tcx.hir.node_to_hir_id(node_id);
157                         if tables.closure_kind_origins().get(hir_id).is_some() {
158                             false
159                         } else {
160                             true
161                         }
162                     }
163                     _ => true,
164                 };
165
166                 if needs_note {
167                     let mpi = self.move_data.moves[mois[0]].path;
168                     let place = &self.move_data.move_paths[mpi].place;
169
170                     if let Some(ty) = self.retrieve_type_for_place(place) {
171                         let note_msg = match self
172                             .describe_place_with_options(place, IncludingDowncast(true))
173                         {
174                             Some(name) => format!("`{}`", name),
175                             None => "value".to_owned(),
176                         };
177
178                         err.note(&format!(
179                             "move occurs because {} has type `{}`, \
180                              which does not implement the `Copy` trait",
181                             note_msg, ty
182                         ));
183                     }
184                 }
185             }
186
187             if let Some((_, mut old_err)) = self.move_error_reported.insert(
188                 mois,
189                 (place.clone(), err)
190             ) {
191                 // Cancel the old error so it doesn't ICE.
192                 old_err.cancel();
193             }
194         }
195     }
196
197     pub(super) fn report_move_out_while_borrowed(
198         &mut self,
199         context: Context,
200         (place, _span): (&Place<'tcx>, Span),
201         borrow: &BorrowData<'tcx>,
202     ) {
203         let tcx = self.tcx;
204         let value_msg = match self.describe_place(place) {
205             Some(name) => format!("`{}`", name),
206             None => "value".to_owned(),
207         };
208         let borrow_msg = match self.describe_place(&borrow.borrowed_place) {
209             Some(name) => format!("`{}`", name),
210             None => "value".to_owned(),
211         };
212
213         let borrow_spans = self.retrieve_borrow_spans(borrow);
214         let borrow_span = borrow_spans.args_or_use();
215
216         let move_spans = self.move_spans(place, context.loc);
217         let span = move_spans.args_or_use();
218
219         let mut err = tcx.cannot_move_when_borrowed(
220             span,
221             &self.describe_place(place).unwrap_or("_".to_owned()),
222             Origin::Mir,
223         );
224         err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_msg));
225         err.span_label(span, format!("move out of {} occurs here", value_msg));
226
227         borrow_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
228
229         move_spans.var_span_label(&mut err, "move occurs due to use in closure");
230
231         self.explain_why_borrow_contains_point(context, borrow, None).emit(self.tcx, &mut err);
232         err.buffer(&mut self.errors_buffer);
233     }
234
235     pub(super) fn report_use_while_mutably_borrowed(
236         &mut self,
237         context: Context,
238         (place, _span): (&Place<'tcx>, Span),
239         borrow: &BorrowData<'tcx>,
240     ) {
241         let tcx = self.tcx;
242
243         let borrow_spans = self.retrieve_borrow_spans(borrow);
244         let borrow_span = borrow_spans.args_or_use();
245
246         // Conflicting borrows are reported separately, so only check for move
247         // captures.
248         let use_spans = self.move_spans(place, context.loc);
249         let span = use_spans.var_or_use();
250
251         let mut err = tcx.cannot_use_when_mutably_borrowed(
252             span,
253             &self.describe_place(place).unwrap_or("_".to_owned()),
254             borrow_span,
255             &self
256                 .describe_place(&borrow.borrowed_place)
257                 .unwrap_or("_".to_owned()),
258             Origin::Mir,
259         );
260
261         borrow_spans.var_span_label(&mut err, {
262             let place = &borrow.borrowed_place;
263             let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
264
265             format!("borrow occurs due to use of `{}` in closure", desc_place)
266         });
267
268         self.explain_why_borrow_contains_point(context, borrow, None).emit(self.tcx, &mut err);
269         err.buffer(&mut self.errors_buffer);
270     }
271
272     pub(super) fn report_conflicting_borrow(
273         &mut self,
274         context: Context,
275         (place, span): (&Place<'tcx>, Span),
276         gen_borrow_kind: BorrowKind,
277         issued_borrow: &BorrowData<'tcx>,
278     ) {
279         let issued_spans = self.retrieve_borrow_spans(issued_borrow);
280         let issued_span = issued_spans.args_or_use();
281
282         let borrow_spans = self.borrow_spans(span, context.loc);
283         let span = borrow_spans.args_or_use();
284
285         let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
286         let tcx = self.tcx;
287
288         // FIXME: supply non-"" `opt_via` when appropriate
289         let mut err = match (
290             gen_borrow_kind,
291             "immutable",
292             "mutable",
293             issued_borrow.kind,
294             "immutable",
295             "mutable",
296         ) {
297             (BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
298             | (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => tcx
299                 .cannot_reborrow_already_borrowed(
300                     span,
301                     &desc_place,
302                     "",
303                     lft,
304                     issued_span,
305                     "it",
306                     rgt,
307                     "",
308                     None,
309                     Origin::Mir,
310                 ),
311
312             (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => tcx
313                 .cannot_mutably_borrow_multiply(
314                     span,
315                     &desc_place,
316                     "",
317                     issued_span,
318                     "",
319                     None,
320                     Origin::Mir,
321                 ),
322
323             (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => tcx
324                 .cannot_uniquely_borrow_by_two_closures(
325                     span,
326                     &desc_place,
327                     issued_span,
328                     None,
329                     Origin::Mir,
330                 ),
331
332             (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
333                 span,
334                 &desc_place,
335                 "",
336                 issued_span,
337                 "it",
338                 "",
339                 None,
340                 Origin::Mir,
341             ),
342
343             (BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => tcx
344                 .cannot_reborrow_already_uniquely_borrowed(
345                     span,
346                     &desc_place,
347                     "",
348                     lft,
349                     issued_span,
350                     "",
351                     None,
352                     Origin::Mir,
353                 ),
354
355             (BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => tcx
356                 .cannot_reborrow_already_uniquely_borrowed(
357                     span,
358                     &desc_place,
359                     "",
360                     lft,
361                     issued_span,
362                     "",
363                     None,
364                     Origin::Mir,
365                 ),
366
367             (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
368         };
369
370         if issued_spans == borrow_spans {
371             borrow_spans.var_span_label(
372                 &mut err,
373                 format!("borrows occur due to use of `{}` in closure", desc_place),
374             );
375         } else {
376             let borrow_place = &issued_borrow.borrowed_place;
377             let borrow_place_desc = self.describe_place(borrow_place).unwrap_or("_".to_owned());
378             issued_spans.var_span_label(
379                 &mut err,
380                 format!(
381                     "first borrow occurs due to use of `{}` in closure",
382                     borrow_place_desc
383                 ),
384             );
385
386             borrow_spans.var_span_label(
387                 &mut err,
388                 format!(
389                     "second borrow occurs due to use of `{}` in closure",
390                     desc_place
391                 ),
392             );
393         }
394
395         self.explain_why_borrow_contains_point(context, issued_borrow, None)
396             .emit(self.tcx, &mut err);
397
398         err.buffer(&mut self.errors_buffer);
399     }
400
401     /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
402     ///
403     /// This means that some data referenced by `borrow` needs to live
404     /// past the point where the StorageDeadOrDrop of `place` occurs.
405     /// This is usually interpreted as meaning that `place` has too
406     /// short a lifetime. (But sometimes it is more useful to report
407     /// it as a more direct conflict between the execution of a
408     /// `Drop::drop` with an aliasing borrow.)
409     pub(super) fn report_borrowed_value_does_not_live_long_enough(
410         &mut self,
411         context: Context,
412         borrow: &BorrowData<'tcx>,
413         place_span: (&Place<'tcx>, Span),
414         kind: Option<WriteKind>,
415     ) {
416         debug!("report_borrowed_value_does_not_live_long_enough(\
417                 {:?}, {:?}, {:?}, {:?}\
418                 )",
419                context, borrow, place_span, kind
420         );
421
422         let drop_span = place_span.1;
423         let scope_tree = self.tcx.region_scope_tree(self.mir_def_id);
424         let root_place = self
425             .prefixes(&borrow.borrowed_place, PrefixSet::All)
426             .last()
427             .unwrap();
428
429         let borrow_spans = self.retrieve_borrow_spans(borrow);
430         let borrow_span = borrow_spans.var_or_use();
431
432         let proper_span = match *root_place {
433             Place::Local(local) => self.mir.local_decls[local].source_info.span,
434             _ => drop_span,
435         };
436
437         if self
438             .access_place_error_reported
439             .contains(&(root_place.clone(), borrow_span))
440         {
441             debug!(
442                 "suppressing access_place error when borrow doesn't live long enough for {:?}",
443                 borrow_span
444             );
445             return;
446         }
447
448         self.access_place_error_reported
449             .insert((root_place.clone(), borrow_span));
450
451         if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind {
452             // If a borrow of path `B` conflicts with drop of `D` (and
453             // we're not in the uninteresting case where `B` is a
454             // prefix of `D`), then report this as a more interesting
455             // destructor conflict.
456             if !borrow.borrowed_place.is_prefix_of(place_span.0) {
457                 self.report_borrow_conflicts_with_destructor(context, borrow, place_span, kind);
458                 return;
459             }
460         }
461
462         let mut err = match &self.describe_place(&borrow.borrowed_place) {
463             Some(_) if self.is_place_thread_local(root_place) => {
464                 self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
465             }
466             Some(name) => self.report_local_value_does_not_live_long_enough(
467                 context,
468                 name,
469                 &scope_tree,
470                 &borrow,
471                 drop_span,
472                 borrow_span,
473                 kind.map(|k| (k, place_span.0)),
474             ),
475             None => self.report_temporary_value_does_not_live_long_enough(
476                 context,
477                 &scope_tree,
478                 &borrow,
479                 drop_span,
480                 proper_span,
481             ),
482         };
483
484         borrow_spans.args_span_label(&mut err, "value captured here");
485
486         err.buffer(&mut self.errors_buffer);
487     }
488
489     fn report_local_value_does_not_live_long_enough(
490         &mut self,
491         context: Context,
492         name: &String,
493         scope_tree: &Lrc<ScopeTree>,
494         borrow: &BorrowData<'tcx>,
495         drop_span: Span,
496         borrow_span: Span,
497         kind_place: Option<(WriteKind, &Place<'tcx>)>,
498     ) -> DiagnosticBuilder<'cx> {
499         debug!(
500             "report_local_value_does_not_live_long_enough(\
501              {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
502              )",
503             context, name, scope_tree, borrow, drop_span, borrow_span
504         );
505
506         let mut err = self.tcx.path_does_not_live_long_enough(
507             borrow_span,
508             &format!("`{}`", name),
509             Origin::Mir,
510         );
511
512         err.span_label(borrow_span, "borrowed value does not live long enough");
513         err.span_label(
514             drop_span,
515             format!("`{}` dropped here while still borrowed", name),
516         );
517
518         self.explain_why_borrow_contains_point(context, borrow, kind_place)
519             .emit(self.tcx, &mut err);
520
521         err
522     }
523
524     pub(super) fn report_borrow_conflicts_with_destructor(
525         &mut self,
526         context: Context,
527         borrow: &BorrowData<'tcx>,
528         (place, drop_span): (&Place<'tcx>, Span),
529         kind: Option<WriteKind>,
530     ) {
531         debug!(
532             "report_borrow_conflicts_with_destructor(\
533              {:?}, {:?}, ({:?}, {:?}), {:?}\
534              )",
535             context, borrow, place, drop_span, kind,
536         );
537
538         let borrow_spans = self.retrieve_borrow_spans(borrow);
539         let borrow_span = borrow_spans.var_or_use();
540
541         let mut err = self.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir);
542
543         let (what_was_dropped, dropped_ty) = {
544             let desc = match self.describe_place(place) {
545                 Some(name) => format!("`{}`", name.as_str()),
546                 None => format!("temporary value"),
547             };
548             let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
549             (desc, ty)
550         };
551
552         let label = match dropped_ty.sty {
553             ty::Adt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() => {
554                 match self.describe_place(&borrow.borrowed_place) {
555                     Some(borrowed) =>
556                         format!("here, drop of {D} needs exclusive access to `{B}`, \
557                                  because the type `{T}` implements the `Drop` trait",
558                                 D=what_was_dropped, T=dropped_ty, B=borrowed),
559                     None =>
560                         format!("here is drop of {D}; whose type `{T}` implements the `Drop` trait",
561                                 D=what_was_dropped, T=dropped_ty),
562                 }
563             }
564             _ => format!("drop of {D} occurs here", D=what_was_dropped),
565         };
566         err.span_label(drop_span, label);
567
568         // Only give this note and suggestion if they could be relevant.
569         let explanation = self.explain_why_borrow_contains_point(
570             context, borrow, kind.map(|k| (k, place)),
571         );
572         match explanation {
573             BorrowExplanation::UsedLater {..} |
574             BorrowExplanation::UsedLaterWhenDropped {..} => {
575                 err.note("consider using a `let` binding to create a longer lived value");
576             },
577             _ => {},
578         }
579
580         explanation.emit(self.tcx, &mut err);
581
582         err.buffer(&mut self.errors_buffer);
583     }
584
585     fn report_thread_local_value_does_not_live_long_enough(
586         &mut self,
587         drop_span: Span,
588         borrow_span: Span,
589     ) -> DiagnosticBuilder<'cx> {
590         debug!(
591             "report_thread_local_value_does_not_live_long_enough(\
592              {:?}, {:?}\
593              )",
594             drop_span, borrow_span
595         );
596
597         let mut err = self
598             .tcx
599             .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir);
600
601         err.span_label(
602             borrow_span,
603             "thread-local variables cannot be borrowed beyond the end of the function",
604         );
605         err.span_label(drop_span, "end of enclosing function is here");
606
607         err
608     }
609
610     fn report_temporary_value_does_not_live_long_enough(
611         &mut self,
612         context: Context,
613         scope_tree: &Lrc<ScopeTree>,
614         borrow: &BorrowData<'tcx>,
615         drop_span: Span,
616         proper_span: Span,
617     ) -> DiagnosticBuilder<'cx> {
618         debug!(
619             "report_temporary_value_does_not_live_long_enough(\
620              {:?}, {:?}, {:?}, {:?}, {:?}\
621              )",
622             context, scope_tree, borrow, drop_span, proper_span
623         );
624
625         let tcx = self.tcx;
626         let mut err =
627             tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
628         err.span_label(proper_span, "temporary value does not live long enough");
629         err.span_label(drop_span, "temporary value only lives until here");
630
631         let explanation = self.explain_why_borrow_contains_point(context, borrow, None);
632         match explanation {
633             BorrowExplanation::UsedLater(..) |
634             BorrowExplanation::UsedLaterInLoop(..) |
635             BorrowExplanation::UsedLaterWhenDropped(..) => {
636                 // Only give this note and suggestion if it could be relevant.
637                 err.note("consider using a `let` binding to create a longer lived value");
638             },
639             _ => {},
640         }
641         explanation.emit(self.tcx, &mut err);
642
643         err
644     }
645
646     fn get_moved_indexes(&mut self, context: Context, mpi: MovePathIndex) -> Vec<MoveOutIndex> {
647         let mir = self.mir;
648
649         let mut stack = Vec::new();
650         stack.extend(mir.predecessor_locations(context.loc));
651
652         let mut visited = FxHashSet();
653         let mut result = vec![];
654
655         'dfs: while let Some(l) = stack.pop() {
656             debug!(
657                 "report_use_of_moved_or_uninitialized: current_location={:?}",
658                 l
659             );
660
661             if !visited.insert(l) {
662                 continue;
663             }
664
665             // check for moves
666             let stmt_kind = mir[l.block]
667                 .statements
668                 .get(l.statement_index)
669                 .map(|s| &s.kind);
670             if let Some(StatementKind::StorageDead(..)) = stmt_kind {
671                 // this analysis only tries to find moves explicitly
672                 // written by the user, so we ignore the move-outs
673                 // created by `StorageDead` and at the beginning
674                 // of a function.
675             } else {
676                 // If we are found a use of a.b.c which was in error, then we want to look for
677                 // moves not only of a.b.c but also a.b and a.
678                 //
679                 // Note that the moves data already includes "parent" paths, so we don't have to
680                 // worry about the other case: that is, if there is a move of a.b.c, it is already
681                 // marked as a move of a.b and a as well, so we will generate the correct errors
682                 // there.
683                 let mut mpis = vec![mpi];
684                 let move_paths = &self.move_data.move_paths;
685                 mpis.extend(move_paths[mpi].parents(move_paths));
686
687                 for moi in &self.move_data.loc_map[l] {
688                     debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
689                     if mpis.contains(&self.move_data.moves[*moi].path) {
690                         debug!("report_use_of_moved_or_uninitialized: found");
691                         result.push(*moi);
692
693                         // Strictly speaking, we could continue our DFS here. There may be
694                         // other moves that can reach the point of error. But it is kind of
695                         // confusing to highlight them.
696                         //
697                         // Example:
698                         //
699                         // ```
700                         // let a = vec![];
701                         // let b = a;
702                         // let c = a;
703                         // drop(a); // <-- current point of error
704                         // ```
705                         //
706                         // Because we stop the DFS here, we only highlight `let c = a`,
707                         // and not `let b = a`. We will of course also report an error at
708                         // `let c = a` which highlights `let b = a` as the move.
709                         continue 'dfs;
710                     }
711                 }
712             }
713
714             // check for inits
715             let mut any_match = false;
716             drop_flag_effects::for_location_inits(self.tcx, self.mir, self.move_data, l, |m| {
717                 if m == mpi {
718                     any_match = true;
719                 }
720             });
721             if any_match {
722                 continue 'dfs;
723             }
724
725             stack.extend(mir.predecessor_locations(l));
726         }
727
728         result
729     }
730
731     pub(super) fn report_illegal_mutation_of_borrowed(
732         &mut self,
733         context: Context,
734         (place, span): (&Place<'tcx>, Span),
735         loan: &BorrowData<'tcx>,
736     ) {
737         let loan_spans = self.retrieve_borrow_spans(loan);
738         let loan_span = loan_spans.args_or_use();
739
740         let tcx = self.tcx;
741         let mut err = tcx.cannot_assign_to_borrowed(
742             span,
743             loan_span,
744             &self.describe_place(place).unwrap_or("_".to_owned()),
745             Origin::Mir,
746         );
747
748         loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
749
750         self.explain_why_borrow_contains_point(context, loan, None).emit(self.tcx, &mut err);
751
752         err.buffer(&mut self.errors_buffer);
753     }
754
755     /// Reports an illegal reassignment; for example, an assignment to
756     /// (part of) a non-`mut` local that occurs potentially after that
757     /// local has already been initialized. `place` is the path being
758     /// assigned; `err_place` is a place providing a reason why
759     /// `place` is not mutable (e.g. the non-`mut` local `x` in an
760     /// assignment to `x.f`).
761     pub(super) fn report_illegal_reassignment(
762         &mut self,
763         _context: Context,
764         (place, span): (&Place<'tcx>, Span),
765         assigned_span: Span,
766         err_place: &Place<'tcx>,
767     ) {
768         let (from_arg, local_decl) = if let Place::Local(local) = *err_place {
769             if let LocalKind::Arg = self.mir.local_kind(local) {
770                 (true, Some(&self.mir.local_decls[local]))
771             } else {
772                 (false, Some(&self.mir.local_decls[local]))
773             }
774         } else {
775             (false, None)
776         };
777
778         // If root local is initialized immediately (everything apart from let
779         // PATTERN;) then make the error refer to that local, rather than the
780         // place being assigned later.
781         let (place_description, assigned_span) = match local_decl {
782             Some(LocalDecl {
783                 is_user_variable: Some(ClearCrossCrate::Clear),
784                 ..
785             })
786             | Some(LocalDecl {
787                 is_user_variable:
788                     Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
789                         opt_match_place: None,
790                         ..
791                     }))),
792                 ..
793             })
794             | Some(LocalDecl {
795                 is_user_variable: None,
796                 ..
797             })
798             | None => (self.describe_place(place), assigned_span),
799             Some(decl) => (self.describe_place(err_place), decl.source_info.span),
800         };
801
802         let mut err = self.tcx.cannot_reassign_immutable(
803             span,
804             place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
805             from_arg,
806             Origin::Mir,
807         );
808         let msg = if from_arg {
809             "cannot assign to immutable argument"
810         } else {
811             "cannot assign twice to immutable variable"
812         };
813         if span != assigned_span {
814             if !from_arg {
815                 let value_msg = match place_description {
816                     Some(name) => format!("`{}`", name),
817                     None => "value".to_owned(),
818                 };
819                 err.span_label(assigned_span, format!("first assignment to {}", value_msg));
820             }
821         }
822         if let Some(decl) = local_decl {
823             if let Some(name) = decl.name {
824                 if decl.can_be_made_mutable() {
825                     err.span_suggestion_with_applicability(
826                         decl.source_info.span,
827                         "make this binding mutable",
828                         format!("mut {}", name),
829                         Applicability::MachineApplicable,
830                     );
831                 }
832             }
833         }
834         err.span_label(span, msg);
835         err.buffer(&mut self.errors_buffer);
836     }
837 }
838
839 pub(super) struct IncludingDowncast(bool);
840
841 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
842     // End-user visible description of `place` if one can be found. If the
843     // place is a temporary for instance, None will be returned.
844     pub(super) fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
845         self.describe_place_with_options(place, IncludingDowncast(false))
846     }
847
848     // End-user visible description of `place` if one can be found. If the
849     // place is a temporary for instance, None will be returned.
850     // `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
851     // `Downcast` and `IncludingDowncast` is true
852     pub(super) fn describe_place_with_options(
853         &self,
854         place: &Place<'tcx>,
855         including_downcast: IncludingDowncast,
856     ) -> Option<String> {
857         let mut buf = String::new();
858         match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
859             Ok(()) => Some(buf),
860             Err(()) => None,
861         }
862     }
863
864     // Appends end-user visible description of `place` to `buf`.
865     fn append_place_to_string(
866         &self,
867         place: &Place<'tcx>,
868         buf: &mut String,
869         mut autoderef: bool,
870         including_downcast: &IncludingDowncast,
871     ) -> Result<(), ()> {
872         match *place {
873             Place::Promoted(_) => {
874                 buf.push_str("promoted");
875             }
876             Place::Local(local) => {
877                 self.append_local_to_string(local, buf)?;
878             }
879             Place::Static(ref static_) => {
880                 buf.push_str(&self.tcx.item_name(static_.def_id).to_string());
881             }
882             Place::Projection(ref proj) => {
883                 match proj.elem {
884                     ProjectionElem::Deref => {
885                         let upvar_field_projection =
886                             place.is_upvar_field_projection(self.mir, &self.tcx);
887                         if let Some(field) = upvar_field_projection {
888                             let var_index = field.index();
889                             let name = self.mir.upvar_decls[var_index].debug_name.to_string();
890                             if self.mir.upvar_decls[var_index].by_ref {
891                                 buf.push_str(&name);
892                             } else {
893                                 buf.push_str(&format!("*{}", &name));
894                             }
895                         } else {
896                             if autoderef {
897                                 self.append_place_to_string(
898                                     &proj.base,
899                                     buf,
900                                     autoderef,
901                                     &including_downcast,
902                                 )?;
903                             } else if let Place::Local(local) = proj.base {
904                                 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
905                                     self.mir.local_decls[local].is_user_variable
906                                 {
907                                     self.append_place_to_string(
908                                         &proj.base,
909                                         buf,
910                                         autoderef,
911                                         &including_downcast,
912                                     )?;
913                                 } else {
914                                     buf.push_str(&"*");
915                                     self.append_place_to_string(
916                                         &proj.base,
917                                         buf,
918                                         autoderef,
919                                         &including_downcast,
920                                     )?;
921                                 }
922                             } else {
923                                 buf.push_str(&"*");
924                                 self.append_place_to_string(
925                                     &proj.base,
926                                     buf,
927                                     autoderef,
928                                     &including_downcast,
929                                 )?;
930                             }
931                         }
932                     }
933                     ProjectionElem::Downcast(..) => {
934                         self.append_place_to_string(
935                             &proj.base,
936                             buf,
937                             autoderef,
938                             &including_downcast,
939                         )?;
940                         if including_downcast.0 {
941                             return Err(());
942                         }
943                     }
944                     ProjectionElem::Field(field, _ty) => {
945                         autoderef = true;
946
947                         let upvar_field_projection =
948                             place.is_upvar_field_projection(self.mir, &self.tcx);
949                         if let Some(field) = upvar_field_projection {
950                             let var_index = field.index();
951                             let name = self.mir.upvar_decls[var_index].debug_name.to_string();
952                             buf.push_str(&name);
953                         } else {
954                             let field_name = self.describe_field(&proj.base, field);
955                             self.append_place_to_string(
956                                 &proj.base,
957                                 buf,
958                                 autoderef,
959                                 &including_downcast,
960                             )?;
961                             buf.push_str(&format!(".{}", field_name));
962                         }
963                     }
964                     ProjectionElem::Index(index) => {
965                         autoderef = true;
966
967                         self.append_place_to_string(
968                             &proj.base,
969                             buf,
970                             autoderef,
971                             &including_downcast,
972                         )?;
973                         buf.push_str("[");
974                         if self.append_local_to_string(index, buf).is_err() {
975                             buf.push_str("..");
976                         }
977                         buf.push_str("]");
978                     }
979                     ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
980                         autoderef = true;
981                         // Since it isn't possible to borrow an element on a particular index and
982                         // then use another while the borrow is held, don't output indices details
983                         // to avoid confusing the end-user
984                         self.append_place_to_string(
985                             &proj.base,
986                             buf,
987                             autoderef,
988                             &including_downcast,
989                         )?;
990                         buf.push_str(&"[..]");
991                     }
992                 };
993             }
994         }
995
996         Ok(())
997     }
998
999     // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
1000     // a name, then `Err` is returned
1001     fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
1002         let local = &self.mir.local_decls[local_index];
1003         match local.name {
1004             Some(name) => {
1005                 buf.push_str(&name.to_string());
1006                 Ok(())
1007             }
1008             None => Err(()),
1009         }
1010     }
1011
1012     // End-user visible description of the `field`nth field of `base`
1013     fn describe_field(&self, base: &Place, field: Field) -> String {
1014         match *base {
1015             Place::Local(local) => {
1016                 let local = &self.mir.local_decls[local];
1017                 self.describe_field_from_ty(&local.ty, field)
1018             }
1019             Place::Promoted(ref prom) => self.describe_field_from_ty(&prom.1, field),
1020             Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
1021             Place::Projection(ref proj) => match proj.elem {
1022                 ProjectionElem::Deref => self.describe_field(&proj.base, field),
1023                 ProjectionElem::Downcast(def, variant_index) => format!(
1024                     "{}",
1025                     def.variants[variant_index].fields[field.index()].ident
1026                 ),
1027                 ProjectionElem::Field(_, field_type) => {
1028                     self.describe_field_from_ty(&field_type, field)
1029                 }
1030                 ProjectionElem::Index(..)
1031                 | ProjectionElem::ConstantIndex { .. }
1032                 | ProjectionElem::Subslice { .. } => {
1033                     self.describe_field(&proj.base, field).to_string()
1034                 }
1035             },
1036         }
1037     }
1038
1039     // End-user visible description of the `field_index`nth field of `ty`
1040     fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
1041         if ty.is_box() {
1042             // If the type is a box, the field is described from the boxed type
1043             self.describe_field_from_ty(&ty.boxed_ty(), field)
1044         } else {
1045             match ty.sty {
1046                 ty::Adt(def, _) => if def.is_enum() {
1047                     field.index().to_string()
1048                 } else {
1049                     def.non_enum_variant().fields[field.index()]
1050                         .ident
1051                         .to_string()
1052                 },
1053                 ty::Tuple(_) => field.index().to_string(),
1054                 ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
1055                     self.describe_field_from_ty(&ty, field)
1056                 }
1057                 ty::Array(ty, _) | ty::Slice(ty) => self.describe_field_from_ty(&ty, field),
1058                 ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
1059                     // Convert the def-id into a node-id. node-ids are only valid for
1060                     // the local code in the current crate, so this returns an `Option` in case
1061                     // the closure comes from another crate. But in that case we wouldn't
1062                     // be borrowck'ing it, so we can just unwrap:
1063                     let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
1064                     let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
1065
1066                     self.tcx.hir.name(freevar.var_id()).to_string()
1067                 }
1068                 _ => {
1069                     // Might need a revision when the fields in trait RFC is implemented
1070                     // (https://github.com/rust-lang/rfcs/pull/1546)
1071                     bug!(
1072                         "End-user description not implemented for field access on `{:?}`",
1073                         ty.sty
1074                     );
1075                 }
1076             }
1077         }
1078     }
1079
1080     // Retrieve type of a place for the current MIR representation
1081     fn retrieve_type_for_place(&self, place: &Place<'tcx>) -> Option<ty::Ty> {
1082         match place {
1083             Place::Local(local) => {
1084                 let local = &self.mir.local_decls[*local];
1085                 Some(local.ty)
1086             }
1087             Place::Promoted(ref prom) => Some(prom.1),
1088             Place::Static(ref st) => Some(st.ty),
1089             Place::Projection(ref proj) => match proj.elem {
1090                 ProjectionElem::Field(_, ty) => Some(ty),
1091                 _ => None,
1092             },
1093         }
1094     }
1095
1096     /// Check if a place is a thread-local static.
1097     pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
1098         if let Place::Static(statik) = place {
1099             let attrs = self.tcx.get_attrs(statik.def_id);
1100             let is_thread_local = attrs.iter().any(|attr| attr.check_name("thread_local"));
1101
1102             debug!(
1103                 "is_place_thread_local: attrs={:?} is_thread_local={:?}",
1104                 attrs, is_thread_local
1105             );
1106             is_thread_local
1107         } else {
1108             debug!("is_place_thread_local: no");
1109             false
1110         }
1111     }
1112
1113     /// Returns the `FakeReadCause` at this location if it is a `FakeRead` statement.
1114     pub(super) fn retrieve_fake_read_cause_for_location(
1115         &self,
1116         location: &Location,
1117     ) -> Option<FakeReadCause> {
1118         let stmt = self.mir.basic_blocks()[location.block]
1119             .statements
1120             .get(location.statement_index)?;
1121         if let StatementKind::FakeRead(cause, _) = stmt.kind {
1122             Some(cause)
1123         } else {
1124             None
1125         }
1126     }
1127 }
1128
1129 // The span(s) associated to a use of a place.
1130 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1131 pub(super) enum UseSpans {
1132     // The access is caused by capturing a variable for a closure.
1133     ClosureUse {
1134         // The span of the args of the closure, including the `move` keyword if
1135         // it's present.
1136         args_span: Span,
1137         // The span of the first use of the captured variable inside the closure.
1138         var_span: Span,
1139     },
1140     // This access has a single span associated to it: common case.
1141     OtherUse(Span),
1142 }
1143
1144 impl UseSpans {
1145     pub(super) fn args_or_use(self) -> Span {
1146         match self {
1147             UseSpans::ClosureUse {
1148                 args_span: span, ..
1149             }
1150             | UseSpans::OtherUse(span) => span,
1151         }
1152     }
1153
1154     pub(super) fn var_or_use(self) -> Span {
1155         match self {
1156             UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
1157         }
1158     }
1159
1160     // Add a span label to the arguments of the closure, if it exists.
1161     pub(super) fn args_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1162         if let UseSpans::ClosureUse { args_span, .. } = self {
1163             err.span_label(args_span, message);
1164         }
1165     }
1166
1167     // Add a span label to the use of the captured variable, if it exists.
1168     pub(super) fn var_span_label(self, err: &mut DiagnosticBuilder, message: impl Into<String>) {
1169         if let UseSpans::ClosureUse { var_span, .. } = self {
1170             err.span_label(var_span, message);
1171         }
1172     }
1173
1174     pub(super) fn for_closure(self) -> bool {
1175         match self {
1176             UseSpans::ClosureUse { .. } => true,
1177             UseSpans::OtherUse(_) => false,
1178         }
1179     }
1180
1181     pub(super) fn or_else<F>(self, if_other: F) -> Self
1182     where
1183         F: FnOnce() -> Self,
1184     {
1185         match self {
1186             closure @ UseSpans::ClosureUse { .. } => closure,
1187             UseSpans::OtherUse(_) => if_other(),
1188         }
1189     }
1190 }
1191
1192 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1193     /// Finds the spans associated to a move or copy of move_place at location.
1194     pub(super) fn move_spans(
1195         &self,
1196         moved_place: &Place<'tcx>, // Could also be an upvar.
1197         location: Location,
1198     ) -> UseSpans {
1199         use self::UseSpans::*;
1200         use rustc::hir::ExprKind::Closure;
1201         use rustc::mir::AggregateKind;
1202
1203         let stmt = match self.mir[location.block]
1204             .statements
1205             .get(location.statement_index)
1206         {
1207             Some(stmt) => stmt,
1208             None => return OtherUse(self.mir.source_info(location).span),
1209         };
1210
1211         if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1212             if let AggregateKind::Closure(def_id, _) = **kind {
1213                 debug!("find_closure_move_span: found closure {:?}", places);
1214
1215                 if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
1216                     if let Closure(_, _, _, args_span, _) = self.tcx.hir.expect_expr(node_id).node {
1217                         if let Some(var_span) = self.tcx.with_freevars(node_id, |freevars| {
1218                             for (v, place) in freevars.iter().zip(places) {
1219                                 match place {
1220                                     Operand::Copy(place) | Operand::Move(place)
1221                                         if moved_place == place =>
1222                                     {
1223                                         debug!(
1224                                             "find_closure_move_span: found captured local {:?}",
1225                                             place
1226                                         );
1227                                         return Some(v.span);
1228                                     }
1229                                     _ => {}
1230                                 }
1231                             }
1232                             None
1233                         }) {
1234                             return ClosureUse {
1235                                 args_span,
1236                                 var_span,
1237                             };
1238                         }
1239                     }
1240                 }
1241             }
1242         }
1243
1244         return OtherUse(stmt.source_info.span);
1245     }
1246
1247     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
1248     /// and its usage of the local assigned at `location`.
1249     /// This is done by searching in statements succeeding `location`
1250     /// and originating from `maybe_closure_span`.
1251     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
1252         use self::UseSpans::*;
1253         use rustc::hir::ExprKind::Closure;
1254         use rustc::mir::AggregateKind;
1255
1256         let local = match self.mir[location.block]
1257             .statements
1258             .get(location.statement_index)
1259         {
1260             Some(&Statement {
1261                 kind: StatementKind::Assign(Place::Local(local), _),
1262                 ..
1263             }) => local,
1264             _ => return OtherUse(use_span),
1265         };
1266
1267         if self.mir.local_kind(local) != LocalKind::Temp {
1268             // operands are always temporaries.
1269             return OtherUse(use_span);
1270         }
1271
1272         for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
1273             if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
1274                 if let AggregateKind::Closure(def_id, _) = **kind {
1275                     debug!("find_closure_borrow_span: found closure {:?}", places);
1276
1277                     return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
1278                         let args_span = if let Closure(_, _, _, span, _) =
1279                             self.tcx.hir.expect_expr(node_id).node
1280                         {
1281                             span
1282                         } else {
1283                             return OtherUse(use_span);
1284                         };
1285
1286                         self.tcx
1287                             .with_freevars(node_id, |freevars| {
1288                                 for (v, place) in freevars.iter().zip(places) {
1289                                     match *place {
1290                                         Operand::Copy(Place::Local(l))
1291                                         | Operand::Move(Place::Local(l))
1292                                             if local == l =>
1293                                         {
1294                                             debug!(
1295                                                 "find_closure_borrow_span: found captured local \
1296                                                  {:?}",
1297                                                 l
1298                                             );
1299                                             return Some(v.span);
1300                                         }
1301                                         _ => {}
1302                                     }
1303                                 }
1304                                 None
1305                             }).map(|var_span| ClosureUse {
1306                                 args_span,
1307                                 var_span,
1308                             }).unwrap_or(OtherUse(use_span))
1309                     } else {
1310                         OtherUse(use_span)
1311                     };
1312                 }
1313             }
1314
1315             if use_span != stmt.source_info.span {
1316                 break;
1317             }
1318         }
1319
1320         OtherUse(use_span)
1321     }
1322
1323     /// Helper to retrieve span(s) of given borrow from the current MIR
1324     /// representation
1325     pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData) -> UseSpans {
1326         let span = self.mir.source_info(borrow.reserve_location).span;
1327         self.borrow_spans(span, borrow.reserve_location)
1328     }
1329 }