]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
Remove irrelevant message about drop order
[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::nll::region_infer::Cause;
13 use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
14 use rustc::ty::{Region, TyCtxt};
15 use rustc::mir::{FakeReadCause, Location, Place, TerminatorKind};
16 use rustc_errors::DiagnosticBuilder;
17 use syntax_pos::Span;
18 use syntax_pos::symbol::Symbol;
19
20 mod find_use;
21
22 pub(in borrow_check) enum BorrowExplanation<'tcx> {
23     UsedLater(bool, Option<FakeReadCause>, Span),
24     UsedLaterInLoop(bool, Span),
25     UsedLaterWhenDropped(Span, Symbol, bool),
26     MustBeValidFor(Region<'tcx>),
27     Unexplained,
28 }
29
30 impl<'tcx> BorrowExplanation<'tcx> {
31     pub(in borrow_check) fn emit<'cx, 'gcx>(
32         &self,
33         tcx: TyCtxt<'cx, 'gcx, 'tcx>,
34         err: &mut DiagnosticBuilder<'_>
35     ) {
36         match *self {
37             BorrowExplanation::UsedLater(is_in_closure, fake_read_cause, var_or_use_span) => {
38                 let message = if is_in_closure {
39                     "borrow later captured here by closure"
40                 } else if let Some(FakeReadCause::ForLet) = fake_read_cause {
41                     "borrow later stored here"
42                 } else {
43                     "borrow later used here"
44                 };
45                 err.span_label(var_or_use_span, message);
46             },
47             BorrowExplanation::UsedLaterInLoop(is_in_closure, var_or_use_span) => {
48                 let message = if is_in_closure {
49                     "borrow captured here by closure in later iteration of loop"
50                 } else {
51                     "borrow used here in later iteration of loop"
52                 };
53                 err.span_label(var_or_use_span, message);
54             },
55             BorrowExplanation::UsedLaterWhenDropped(span, local_name, should_note_order) => {
56                 err.span_label(
57                     span,
58                     format!("borrow later used here, when `{}` is dropped", local_name),
59                 );
60
61                 if should_note_order {
62                     err.note(
63                         "values in a scope are dropped \
64                          in the opposite order they are defined",
65                     );
66                 }
67             },
68             BorrowExplanation::MustBeValidFor(region) => {
69                 tcx.note_and_explain_free_region(
70                     err,
71                     "borrowed value must be valid for ",
72                     region,
73                     "...",
74                 );
75             },
76             _ => {},
77         }
78     }
79 }
80
81 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
82     /// Adds annotations to `err` explaining *why* the borrow contains the
83     /// point from `context`. This is key for the "3-point errors"
84     /// [described in the NLL RFC][d].
85     ///
86     /// # Parameters
87     ///
88     /// - `borrow`: the borrow in question
89     /// - `context`: where the borrow occurs
90     /// - `kind_place`: if Some, this describes the statement that triggered the error.
91     ///   - first half is the kind of write, if any, being performed
92     ///   - second half is the place being accessed
93     /// - `err`: where the error annotations are going to be added
94     ///
95     /// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
96     pub(in borrow_check) fn explain_why_borrow_contains_point(
97         &self,
98         context: Context,
99         borrow: &BorrowData<'tcx>,
100         kind_place: Option<(WriteKind, &Place<'tcx>)>,
101     ) -> BorrowExplanation<'tcx> {
102         debug!(
103             "find_why_borrow_contains_point(context={:?}, borrow={:?})",
104             context, borrow,
105         );
106
107         let regioncx = &self.nonlexical_regioncx;
108         let mir = self.mir;
109         let tcx = self.infcx.tcx;
110
111         let borrow_region_vid = regioncx.to_region_vid(borrow.region);
112         debug!(
113             "explain_why_borrow_contains_point: borrow_region_vid={:?}",
114             borrow_region_vid
115         );
116
117         let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, context.loc);
118         debug!(
119             "explain_why_borrow_contains_point: region_sub={:?}",
120             region_sub
121         );
122
123          match find_use::find(mir, regioncx, tcx, region_sub, context.loc) {
124             Some(Cause::LiveVar(local, location)) => {
125                 let span = mir.source_info(location).span;
126                 let spans = self.move_spans(&Place::Local(local), location)
127                     .or_else(|| self.borrow_spans(span, location));
128
129                 if self.is_borrow_location_in_loop(context.loc) {
130                     BorrowExplanation::UsedLaterInLoop(spans.for_closure(), spans.var_or_use())
131                 } else {
132                     // Check if the location represents a `FakeRead`, and adapt the error
133                     // message to the `FakeReadCause` it is from: in particular,
134                     // the ones inserted in optimized `let var = <expr>` patterns.
135                     BorrowExplanation::UsedLater(
136                         spans.for_closure(),
137                         self.retrieve_fake_read_cause_for_location(&location),
138                         spans.var_or_use()
139                     )
140                 }
141             }
142
143             Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name {
144                 Some(local_name) => {
145                     let mut should_note_order = false;
146                     if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
147                         if let Place::Local(borrowed_local) = place {
148                             let dropped_local_scope = mir.local_decls[local].visibility_scope;
149                             let borrowed_local_scope =
150                                 mir.local_decls[*borrowed_local].visibility_scope;
151
152                             if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope)
153                                 && local != *borrowed_local
154                             {
155                                 should_note_order = true;
156                             }
157                         }
158                     }
159
160                     BorrowExplanation::UsedLaterWhenDropped(
161                         mir.source_info(location).span,
162                         *local_name,
163                         should_note_order
164                     )
165                 },
166
167                 None => BorrowExplanation::Unexplained,
168             },
169
170             None => if let Some(region) = regioncx.to_error_region(region_sub) {
171                 BorrowExplanation::MustBeValidFor(region)
172             } else {
173                 BorrowExplanation::Unexplained
174             },
175         }
176     }
177
178     /// Check if a borrow location is within a loop.
179     fn is_borrow_location_in_loop(
180         &self,
181         borrow_location: Location,
182     ) -> bool {
183         let mut visited_locations = Vec::new();
184         let mut pending_locations = vec![ borrow_location ];
185         debug!("is_in_loop: borrow_location={:?}", borrow_location);
186
187         while let Some(location) = pending_locations.pop() {
188             debug!("is_in_loop: location={:?} pending_locations={:?} visited_locations={:?}",
189                    location, pending_locations, visited_locations);
190             if location == borrow_location && visited_locations.contains(&borrow_location) {
191                 // We've managed to return to where we started (and this isn't the start of the
192                 // search).
193                 debug!("is_in_loop: found!");
194                 return true;
195             }
196
197             // Skip locations we've been.
198             if visited_locations.contains(&location) { continue; }
199
200             let block = &self.mir.basic_blocks()[location.block];
201             if location.statement_index ==  block.statements.len() {
202                 // Add start location of the next blocks to pending locations.
203                 match block.terminator().kind {
204                     TerminatorKind::Goto { target } => {
205                         pending_locations.push(target.start_location());
206                     },
207                     TerminatorKind::SwitchInt { ref targets, .. } => {
208                         for target in targets {
209                             pending_locations.push(target.start_location());
210                         }
211                     },
212                     TerminatorKind::Drop { target, unwind, .. } |
213                     TerminatorKind::DropAndReplace { target, unwind, .. } |
214                     TerminatorKind::Assert { target, cleanup: unwind, .. } |
215                     TerminatorKind::Yield { resume: target, drop: unwind, .. } |
216                     TerminatorKind::FalseUnwind { real_target: target, unwind, .. } => {
217                         pending_locations.push(target.start_location());
218                         if let Some(unwind) = unwind {
219                             pending_locations.push(unwind.start_location());
220                         }
221                     },
222                     TerminatorKind::Call { ref destination, cleanup, .. } => {
223                         if let Some((_, destination)) = destination {
224                             pending_locations.push(destination.start_location());
225                         }
226                         if let Some(cleanup) = cleanup {
227                             pending_locations.push(cleanup.start_location());
228                         }
229                     },
230                     TerminatorKind::FalseEdges { real_target, ref imaginary_targets, .. } => {
231                         pending_locations.push(real_target.start_location());
232                         for target in imaginary_targets {
233                             pending_locations.push(target.start_location());
234                         }
235                     },
236                     _ => {},
237                 }
238             } else {
239                 // Add the next statement to pending locations.
240                 pending_locations.push(location.successor_within_block());
241             }
242
243             // Keep track of where we have visited.
244             visited_locations.push(location);
245         }
246
247         false
248     }
249 }