]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
Merge branch 'refactor-select' of https://github.com/aravind-pg/rust into update...
[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::{Context, MirBorrowckCtxt};
12 use borrow_check::nll::region_infer::{Cause, RegionInferenceContext};
13 use dataflow::BorrowData;
14 use rustc::mir::{Local, Location, Mir};
15 use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor};
16 use rustc_data_structures::fx::FxHashSet;
17 use rustc_errors::DiagnosticBuilder;
18 use util::liveness::{self, DefUse, LivenessMode};
19
20 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
21     pub(in borrow_check) fn explain_why_borrow_contains_point(
22         &self,
23         context: Context,
24         borrow: &BorrowData<'tcx>,
25         err: &mut DiagnosticBuilder<'_>,
26     ) {
27         if let Some(regioncx) = &self.nonlexical_regioncx {
28             if let Some(cause) = regioncx.why_region_contains_point(borrow.region, context.loc) {
29                 let mir = self.mir;
30
31                 match *cause.root_cause() {
32                     Cause::LiveVar(local, location) => {
33                         match find_regular_use(&mir, regioncx, borrow, location, local) {
34                             Some(p) => {
35                                 err.span_label(
36                                     mir.source_info(p).span,
37                                     format!("borrow later used here"),
38                                 );
39                             }
40
41                             None => {
42                                 span_bug!(
43                                     mir.source_info(context.loc).span,
44                                     "Cause should end in a LiveVar"
45                                 );
46                             }
47                         }
48                     }
49
50                     Cause::DropVar(local, location) => {
51                         match find_drop_use(&mir, regioncx, borrow, location, local) {
52                             Some(p) => {
53                                 let local_name = &mir.local_decls[local].name.unwrap();
54
55                                 err.span_label(
56                                     mir.source_info(p).span,
57                                     format!(
58                                         "borrow later used here, when `{}` is dropped",
59                                         local_name
60                                     ),
61                                 );
62                             }
63
64                             None => {
65                                 span_bug!(
66                                     mir.source_info(context.loc).span,
67                                     "Cause should end in a DropVar"
68                                 );
69                             }
70                         }
71                     }
72
73                     Cause::UniversalRegion(region_vid) => {
74                         if let Some(region) = regioncx.to_error_region(region_vid) {
75                             self.tcx.note_and_explain_free_region(
76                                 err,
77                                 "borrowed value must be valid for ",
78                                 region,
79                                 "...",
80                             );
81                         }
82                     }
83
84                     _ => {}
85                 }
86             }
87         }
88     }
89 }
90
91 fn find_regular_use<'gcx, 'tcx>(
92     mir: &'gcx Mir,
93     regioncx: &'tcx RegionInferenceContext,
94     borrow: &'tcx BorrowData,
95     start_point: Location,
96     local: Local,
97 ) -> Option<Location> {
98     let mut uf = UseFinder {
99         mir,
100         regioncx,
101         borrow,
102         start_point,
103         local,
104         liveness_mode: LivenessMode {
105             include_regular_use: true,
106             include_drops: false,
107         },
108     };
109
110     uf.find()
111 }
112
113 fn find_drop_use<'gcx, 'tcx>(
114     mir: &'gcx Mir,
115     regioncx: &'tcx RegionInferenceContext,
116     borrow: &'tcx BorrowData,
117     start_point: Location,
118     local: Local,
119 ) -> Option<Location> {
120     let mut uf = UseFinder {
121         mir,
122         regioncx,
123         borrow,
124         start_point,
125         local,
126         liveness_mode: LivenessMode {
127             include_regular_use: false,
128             include_drops: true,
129         },
130     };
131
132     uf.find()
133 }
134
135 struct UseFinder<'gcx, 'tcx> {
136     mir: &'gcx Mir<'gcx>,
137     regioncx: &'tcx RegionInferenceContext<'tcx>,
138     borrow: &'tcx BorrowData<'tcx>,
139     start_point: Location,
140     local: Local,
141     liveness_mode: LivenessMode,
142 }
143
144 impl<'gcx, 'tcx> UseFinder<'gcx, 'tcx> {
145     fn find(&mut self) -> Option<Location> {
146         let mut stack = vec![];
147         let mut visited = FxHashSet();
148
149         stack.push(self.start_point);
150         while let Some(p) = stack.pop() {
151             if !self.regioncx.region_contains_point(self.borrow.region, p) {
152                 continue;
153             }
154
155             if !visited.insert(p) {
156                 continue;
157             }
158
159             let block_data = &self.mir[p.block];
160             let (defined, used) = self.def_use(p, block_data.visitable(p.statement_index));
161
162             if used {
163                 return Some(p);
164             } else if !defined {
165                 if p.statement_index < block_data.statements.len() {
166                     stack.push(Location {
167                         statement_index: p.statement_index + 1,
168                         ..p
169                     });
170                 } else {
171                     stack.extend(
172                         block_data
173                             .terminator()
174                             .successors()
175                             .iter()
176                             .map(|&basic_block| Location {
177                                 statement_index: 0,
178                                 block: basic_block,
179                             }),
180                     );
181                 }
182             }
183         }
184
185         None
186     }
187
188     fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> (bool, bool) {
189         let mut visitor = DefUseVisitor {
190             defined: false,
191             used: false,
192             local: self.local,
193             liveness_mode: self.liveness_mode,
194         };
195
196         thing.apply(location, &mut visitor);
197
198         (visitor.defined, visitor.used)
199     }
200 }
201
202 struct DefUseVisitor {
203     defined: bool,
204     used: bool,
205     local: Local,
206     liveness_mode: LivenessMode,
207 }
208
209 impl<'tcx> Visitor<'tcx> for DefUseVisitor {
210     fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) {
211         if local == self.local {
212             match liveness::categorize(context, self.liveness_mode) {
213                 Some(DefUse::Def) => self.defined = true,
214                 Some(DefUse::Use) => self.used = true,
215                 None => (),
216             }
217         }
218     }
219 }