]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
948c1ac0b136255b35b4d2697a9b219890cb1a04
[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<'_>,
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                     _ => {
74                         cause.label_diagnostic(mir, err);
75                     }
76                 }
77             }
78         }
79     }
80 }
81
82 fn find_regular_use<'gcx, 'tcx>(
83     mir: &'gcx Mir,
84     regioncx: &'tcx RegionInferenceContext,
85     borrow: &'tcx BorrowData,
86     start_point: Location,
87     local: Local,
88 ) -> Option<Location> {
89     let mut uf = UseFinder {
90         mir,
91         regioncx,
92         borrow,
93         start_point,
94         local,
95         liveness_mode: LivenessMode {
96             include_regular_use: true,
97             include_drops: false,
98         },
99     };
100
101     uf.find()
102 }
103
104 fn find_drop_use<'gcx, 'tcx>(
105     mir: &'gcx Mir,
106     regioncx: &'tcx RegionInferenceContext,
107     borrow: &'tcx BorrowData,
108     start_point: Location,
109     local: Local,
110 ) -> Option<Location> {
111     let mut uf = UseFinder {
112         mir,
113         regioncx,
114         borrow,
115         start_point,
116         local,
117         liveness_mode: LivenessMode {
118             include_regular_use: false,
119             include_drops: true,
120         },
121     };
122
123     uf.find()
124 }
125
126 struct UseFinder<'gcx, 'tcx> {
127     mir: &'gcx Mir<'gcx>,
128     regioncx: &'tcx RegionInferenceContext<'tcx>,
129     borrow: &'tcx BorrowData<'tcx>,
130     start_point: Location,
131     local: Local,
132     liveness_mode: LivenessMode,
133 }
134
135 impl<'gcx, 'tcx> UseFinder<'gcx, 'tcx> {
136     fn find(&mut self) -> Option<Location> {
137         let mut stack = vec![];
138         let mut visited = FxHashSet();
139
140         stack.push(self.start_point);
141         while let Some(p) = stack.pop() {
142             if !self.regioncx.region_contains_point(self.borrow.region, p) {
143                 continue;
144             }
145
146             if !visited.insert(p) {
147                 continue;
148             }
149
150             let block_data = &self.mir[p.block];
151             let (defined, used) = self.def_use(p, block_data.visitable(p.statement_index));
152
153             if used {
154                 return Some(p);
155             } else if !defined {
156                 if p.statement_index < block_data.statements.len() {
157                     stack.push(Location {
158                         statement_index: p.statement_index + 1,
159                         ..p
160                     });
161                 } else {
162                     stack.extend(
163                         block_data
164                             .terminator()
165                             .successors()
166                             .iter()
167                             .map(|&basic_block| Location {
168                                 statement_index: 0,
169                                 block: basic_block,
170                             }),
171                     );
172                 }
173             }
174         }
175
176         None
177     }
178
179     fn def_use(&self, location: Location, thing: &MirVisitable<'tcx>) -> (bool, bool) {
180         let mut visitor = DefUseVisitor {
181             defined: false,
182             used: false,
183             local: self.local,
184             liveness_mode: self.liveness_mode,
185         };
186
187         thing.apply(location, &mut visitor);
188
189         (visitor.defined, visitor.used)
190     }
191 }
192
193 struct DefUseVisitor {
194     defined: bool,
195     used: bool,
196     local: Local,
197     liveness_mode: LivenessMode,
198 }
199
200 impl<'tcx> Visitor<'tcx> for DefUseVisitor {
201     fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) {
202         if local == self.local {
203             match liveness::categorize(context, self.liveness_mode) {
204                 Some(DefUse::Def) => self.defined = true,
205                 Some(DefUse::Use) => self.used = true,
206                 None => (),
207             }
208         }
209     }
210 }