Skip to content

Commit 3a185a5

Browse files
spastorinonikomatsakis
authored andcommitted
Add three point error handling to borrowck
Closes #45988
1 parent 6d2987c commit 3a185a5

24 files changed

+274
-23
lines changed

src/librustc/mir/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_data_structures::control_flow_graph::ControlFlowGraph;
2121
use rustc_serialize as serialize;
2222
use hir::def::CtorKind;
2323
use hir::def_id::DefId;
24+
use mir::visit::MirVisitable;
2425
use ty::subst::{Subst, Substs};
2526
use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior};
2627
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
@@ -868,6 +869,14 @@ impl<'tcx> BasicBlockData<'tcx> {
868869
}
869870
}
870871
}
872+
873+
pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> {
874+
if index < self.statements.len() {
875+
&self.statements[index]
876+
} else {
877+
&self.terminator
878+
}
879+
}
871880
}
872881

873882
impl<'tcx> Debug for TerminatorKind<'tcx> {

src/librustc_mir/borrow_check/error_reporting.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use rustc::mir::{BorrowKind, Field, Local, Location, Operand};
1414
use rustc::mir::{Place, ProjectionElem, Rvalue, Statement, StatementKind};
1515
use rustc::ty::{self, RegionKind};
1616
use rustc_data_structures::indexed_vec::Idx;
17-
use rustc_errors::DiagnosticBuilder;
1817

1918
use std::rc::Rc;
2019

@@ -134,19 +133,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
134133
err.emit();
135134
}
136135

137-
fn explain_why_borrow_contains_point(
138-
&self,
139-
context: Context,
140-
borrow: &BorrowData<'_>,
141-
err: &mut DiagnosticBuilder<'_>,
142-
) {
143-
if let Some(regioncx) = &self.nonlexical_regioncx {
144-
if let Some(cause) = regioncx.why_region_contains_point(borrow.region, context.loc) {
145-
cause.label_diagnostic(self.mir, err);
146-
}
147-
}
148-
}
149-
150136
/// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
151137
/// the local assigned at `location`.
152138
/// This is done by searching in statements succeeding `location`
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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+
cause.label_diagnostic(mir, err);
32+
33+
match *cause.root_cause() {
34+
Cause::LiveVar(local, location) => {
35+
match find_regular_use(&mir, regioncx, borrow, location, local) {
36+
Some(p) => {
37+
err.span_label(
38+
mir.source_info(p).span,
39+
format!("borrow later used here"),
40+
);
41+
}
42+
43+
None => {
44+
span_bug!(
45+
mir.source_info(context.loc).span,
46+
"Cause should end in a LiveVar"
47+
);
48+
}
49+
}
50+
}
51+
52+
Cause::DropVar(local, location) => {
53+
match find_drop_use(&mir, regioncx, borrow, location, local) {
54+
Some(p) => {
55+
let local_name = &mir.local_decls[local].name.unwrap();
56+
57+
err.span_label(
58+
mir.source_info(p).span,
59+
format!(
60+
"borrow later used here, when `{}` is dropped",
61+
local_name
62+
),
63+
);
64+
}
65+
66+
None => {
67+
span_bug!(
68+
mir.source_info(context.loc).span,
69+
"Cause should end in a DropVar"
70+
);
71+
}
72+
}
73+
}
74+
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+
}

src/librustc_mir/borrow_check/nll/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use util::pretty::{self, ALIGN};
2727
use self::mir_util::PassWhere;
2828

2929
mod constraint_generation;
30+
pub mod explain_borrow;
3031
pub(crate) mod region_infer;
3132
mod renumber;
3233
mod subtype_constraint_generation;

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,4 +1229,22 @@ impl Cause {
12291229
}
12301230
}
12311231
}
1232+
1233+
pub(crate) fn root_cause(&self) -> &Cause {
1234+
match self {
1235+
Cause::LiveVar(..) |
1236+
Cause::DropVar(..) |
1237+
Cause::LiveOther(..) |
1238+
Cause::UniversalRegion(..) => {
1239+
self
1240+
}
1241+
1242+
Cause::Outlives {
1243+
original_cause,
1244+
..
1245+
} => {
1246+
original_cause.root_cause()
1247+
}
1248+
}
1249+
}
12321250
}

src/librustc_mir/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
2323
#![feature(const_fn)]
2424
#![feature(core_intrinsics)]
2525
#![feature(decl_macro)]
26+
#![feature(dyn_trait)]
2627
#![feature(i128_type)]
2728
#![feature(inclusive_range_syntax)]
2829
#![feature(inclusive_range)]

src/test/ui/nll/capture-ref-in-struct.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-flags:-Znll -Zborrowck=mir
11+
// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause
1212

1313
// Test that a structure which tries to store a pointer to `y` into
1414
// `p` (indirectly) fails to compile.

src/test/ui/nll/capture-ref-in-struct.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ error[E0597]: `y` does not live long enough
66
...
77
37 | }
88
| - borrowed value only lives until here
9+
38 |
10+
39 | deref(p);
11+
| - borrow later used here
912
|
1013
= note: borrowed value must be valid for lifetime '_#5r...
1114

src/test/ui/nll/closure-requirements/escape-argument.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
// basically checking that the MIR type checker correctly enforces the
2323
// closure signature.
2424

25-
// compile-flags:-Znll -Zborrowck=mir -Zverbose
25+
// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose
2626

2727
#![feature(rustc_attrs)]
2828

src/test/ui/nll/closure-requirements/escape-argument.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ error[E0597]: `y` does not live long enough
3131
38 | //~^ ERROR `y` does not live long enough [E0597]
3232
39 | }
3333
| - borrowed value only lives until here
34+
40 |
35+
41 | deref(p);
36+
| - borrow later used here
3437
|
3538
= note: borrowed value must be valid for lifetime '_#6r...
3639

src/test/ui/nll/closure-requirements/escape-upvar-nested.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//
1616
// except that the closure does so via a second closure.
1717

18-
// compile-flags:-Znll -Zborrowck=mir -Zverbose
18+
// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose
1919

2020
#![feature(rustc_attrs)]
2121

src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ error[E0597]: `y` does not live long enough
5858
...
5959
36 | }
6060
| - borrowed value only lives until here
61+
37 |
62+
38 | deref(p);
63+
| - borrow later used here
6164
|
6265
= note: borrowed value must be valid for lifetime '_#4r...
6366

src/test/ui/nll/closure-requirements/escape-upvar-ref.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// `'b`. This relationship is propagated to the closure creator,
2020
// which reports an error.
2121

22-
// compile-flags:-Znll -Zborrowck=mir -Zverbose
22+
// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause -Zverbose
2323

2424
#![feature(rustc_attrs)]
2525

src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ error[E0597]: `y` does not live long enough
3535
...
3636
36 | }
3737
| - borrowed value only lives until here
38+
37 |
39+
38 | deref(p);
40+
| - borrow later used here
3841
|
3942
= note: borrowed value must be valid for lifetime '_#4r...
4043

src/test/ui/nll/get_default.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// a variety of errors from the older, AST-based machinery (notably
1414
// borrowck), and then we get the NLL error at the end.
1515

16-
// compile-flags:-Znll -Zborrowck=compare
16+
// compile-flags:-Znll -Zborrowck=compare -Znll-dump-cause
1717

1818
struct Map {
1919
}

src/test/ui/nll/get_default.stderr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm
4242
43 | Some(v) => {
4343
44 | map.set(String::new()); // Both AST and MIR error here
4444
| ^^^ mutable borrow occurs here
45+
...
46+
47 | return v;
47+
| - borrow later used here
4548

4649
error: aborting due to 4 previous errors
4750

0 commit comments

Comments
 (0)