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.
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.
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;
18 use syntax_pos::symbol::Symbol;
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>),
30 impl<'tcx> BorrowExplanation<'tcx> {
31 pub(in borrow_check) fn emit<'cx, 'gcx>(
33 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
34 err: &mut DiagnosticBuilder<'_>
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"
43 "borrow later used here"
45 err.span_label(var_or_use_span, message);
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"
51 "borrow used here in later iteration of loop"
53 err.span_label(var_or_use_span, message);
55 BorrowExplanation::UsedLaterWhenDropped(span, local_name, should_note_order) => {
58 format!("borrow later used here, when `{}` is dropped", local_name),
61 if should_note_order {
63 "values in a scope are dropped \
64 in the opposite order they are defined",
68 BorrowExplanation::MustBeValidFor(region) => {
69 tcx.note_and_explain_free_region(
71 "borrowed value must be valid for ",
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].
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
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(
99 borrow: &BorrowData<'tcx>,
100 kind_place: Option<(WriteKind, &Place<'tcx>)>,
101 ) -> BorrowExplanation<'tcx> {
103 "find_why_borrow_contains_point(context={:?}, borrow={:?})",
107 let regioncx = &self.nonlexical_regioncx;
111 let borrow_region_vid = regioncx.to_region_vid(borrow.region);
113 "explain_why_borrow_contains_point: borrow_region_vid={:?}",
117 let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, context.loc);
119 "explain_why_borrow_contains_point: region_sub={:?}",
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));
129 if self.is_borrow_location_in_loop(context.loc) {
130 BorrowExplanation::UsedLaterInLoop(spans.for_closure(), spans.var_or_use())
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(
137 self.retrieve_fake_read_cause_for_location(&location),
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;
152 if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) {
153 should_note_order = true;
158 BorrowExplanation::UsedLaterWhenDropped(
159 mir.source_info(location).span,
165 None => BorrowExplanation::Unexplained,
168 None => if let Some(region) = regioncx.to_error_region(region_sub) {
169 BorrowExplanation::MustBeValidFor(region)
171 BorrowExplanation::Unexplained
176 /// Check if a borrow location is within a loop.
177 fn is_borrow_location_in_loop(
179 borrow_location: Location,
181 let mut visited_locations = Vec::new();
182 let mut pending_locations = vec![ borrow_location ];
183 debug!("is_in_loop: borrow_location={:?}", borrow_location);
185 while let Some(location) = pending_locations.pop() {
186 debug!("is_in_loop: location={:?} pending_locations={:?} visited_locations={:?}",
187 location, pending_locations, visited_locations);
188 if location == borrow_location && visited_locations.contains(&borrow_location) {
189 // We've managed to return to where we started (and this isn't the start of the
191 debug!("is_in_loop: found!");
195 // Skip locations we've been.
196 if visited_locations.contains(&location) { continue; }
198 let block = &self.mir.basic_blocks()[location.block];
199 if location.statement_index == block.statements.len() {
200 // Add start location of the next blocks to pending locations.
201 match block.terminator().kind {
202 TerminatorKind::Goto { target } => {
203 pending_locations.push(target.start_location());
205 TerminatorKind::SwitchInt { ref targets, .. } => {
206 for target in targets {
207 pending_locations.push(target.start_location());
210 TerminatorKind::Drop { target, unwind, .. } |
211 TerminatorKind::DropAndReplace { target, unwind, .. } |
212 TerminatorKind::Assert { target, cleanup: unwind, .. } |
213 TerminatorKind::Yield { resume: target, drop: unwind, .. } |
214 TerminatorKind::FalseUnwind { real_target: target, unwind, .. } => {
215 pending_locations.push(target.start_location());
216 if let Some(unwind) = unwind {
217 pending_locations.push(unwind.start_location());
220 TerminatorKind::Call { ref destination, cleanup, .. } => {
221 if let Some((_, destination)) = destination {
222 pending_locations.push(destination.start_location());
224 if let Some(cleanup) = cleanup {
225 pending_locations.push(cleanup.start_location());
228 TerminatorKind::FalseEdges { real_target, ref imaginary_targets, .. } => {
229 pending_locations.push(real_target.start_location());
230 for target in imaginary_targets {
231 pending_locations.push(target.start_location());
237 // Add the next statement to pending locations.
238 pending_locations.push(location.successor_within_block());
241 // Keep track of where we have visited.
242 visited_locations.push(location);