Skip to content

Commit

Permalink
Add three point error handling to borrowck
Browse files Browse the repository at this point in the history
Closes #45988
  • Loading branch information
spastorino authored and nikomatsakis committed Dec 20, 2017
1 parent 6d2987c commit 3a185a5
Show file tree
Hide file tree
Showing 24 changed files with 274 additions and 23 deletions.
9 changes: 9 additions & 0 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use rustc_data_structures::control_flow_graph::ControlFlowGraph;
use rustc_serialize as serialize;
use hir::def::CtorKind;
use hir::def_id::DefId;
use mir::visit::MirVisitable;
use ty::subst::{Subst, Substs};
use ty::{self, AdtDef, ClosureSubsts, Region, Ty, TyCtxt, GeneratorInterior};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
Expand Down Expand Up @@ -868,6 +869,14 @@ impl<'tcx> BasicBlockData<'tcx> {
}
}
}

pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> {
if index < self.statements.len() {
&self.statements[index]
} else {
&self.terminator
}
}
}

impl<'tcx> Debug for TerminatorKind<'tcx> {
Expand Down
14 changes: 0 additions & 14 deletions src/librustc_mir/borrow_check/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use rustc::mir::{BorrowKind, Field, Local, Location, Operand};
use rustc::mir::{Place, ProjectionElem, Rvalue, Statement, StatementKind};
use rustc::ty::{self, RegionKind};
use rustc_data_structures::indexed_vec::Idx;
use rustc_errors::DiagnosticBuilder;

use std::rc::Rc;

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

fn explain_why_borrow_contains_point(
&self,
context: Context,
borrow: &BorrowData<'_>,
err: &mut DiagnosticBuilder<'_>,
) {
if let Some(regioncx) = &self.nonlexical_regioncx {
if let Some(cause) = regioncx.why_region_contains_point(borrow.region, context.loc) {
cause.label_diagnostic(self.mir, err);
}
}
}

/// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
/// the local assigned at `location`.
/// This is done by searching in statements succeeding `location`
Expand Down
210 changes: 210 additions & 0 deletions src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use borrow_check::{Context, MirBorrowckCtxt};
use borrow_check::nll::region_infer::{Cause, RegionInferenceContext};
use dataflow::BorrowData;
use rustc::mir::{Local, Location, Mir};
use rustc::mir::visit::{MirVisitable, PlaceContext, Visitor};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::DiagnosticBuilder;
use util::liveness::{self, DefUse, LivenessMode};

impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
pub(in borrow_check) fn explain_why_borrow_contains_point(
&self,
context: Context,
borrow: &BorrowData<'_>,
err: &mut DiagnosticBuilder<'_>,
) {
if let Some(regioncx) = &self.nonlexical_regioncx {
if let Some(cause) = regioncx.why_region_contains_point(borrow.region, context.loc) {
let mir = self.mir;

cause.label_diagnostic(mir, err);

match *cause.root_cause() {
Cause::LiveVar(local, location) => {
match find_regular_use(&mir, regioncx, borrow, location, local) {
Some(p) => {
err.span_label(
mir.source_info(p).span,
format!("borrow later used here"),
);
}

None => {
span_bug!(
mir.source_info(context.loc).span,
"Cause should end in a LiveVar"
);
}
}
}

Cause::DropVar(local, location) => {
match find_drop_use(&mir, regioncx, borrow, location, local) {
Some(p) => {
let local_name = &mir.local_decls[local].name.unwrap();

err.span_label(
mir.source_info(p).span,
format!(
"borrow later used here, when `{}` is dropped",
local_name
),
);
}

None => {
span_bug!(
mir.source_info(context.loc).span,
"Cause should end in a DropVar"
);
}
}
}

_ => (),
}
}
}
}
}

fn find_regular_use<'gcx, 'tcx>(
mir: &'gcx Mir,
regioncx: &'tcx RegionInferenceContext,
borrow: &'tcx BorrowData,
start_point: Location,
local: Local,
) -> Option<Location> {
let mut uf = UseFinder {
mir,
regioncx,
borrow,
start_point,
local,
liveness_mode: LivenessMode {
include_regular_use: true,
include_drops: false,
},
};

uf.find()
}

fn find_drop_use<'gcx, 'tcx>(
mir: &'gcx Mir,
regioncx: &'tcx RegionInferenceContext,
borrow: &'tcx BorrowData,
start_point: Location,
local: Local,
) -> Option<Location> {
let mut uf = UseFinder {
mir,
regioncx,
borrow,
start_point,
local,
liveness_mode: LivenessMode {
include_regular_use: false,
include_drops: true,
},
};

uf.find()
}

struct UseFinder<'gcx, 'tcx> {
mir: &'gcx Mir<'gcx>,
regioncx: &'tcx RegionInferenceContext<'tcx>,
borrow: &'tcx BorrowData<'tcx>,
start_point: Location,
local: Local,
liveness_mode: LivenessMode,
}

impl<'gcx, 'tcx> UseFinder<'gcx, 'tcx> {
fn find(&mut self) -> Option<Location> {
let mut stack = vec![];
let mut visited = FxHashSet();

stack.push(self.start_point);
while let Some(p) = stack.pop() {
if !self.regioncx.region_contains_point(self.borrow.region, p) {
continue;
}

if !visited.insert(p) {
continue;
}

let block_data = &self.mir[p.block];
let (defined, used) = self.def_use(p, block_data.visitable(p.statement_index));

if used {
return Some(p);
} else if !defined {
if p.statement_index < block_data.statements.len() {
stack.push(Location {
statement_index: p.statement_index + 1,
..p
});
} else {
stack.extend(
block_data
.terminator()
.successors()
.iter()
.map(|&basic_block| Location {
statement_index: 0,
block: basic_block,
}),
);
}
}
}

None
}

fn def_use(&self, location: Location, thing: &MirVisitable<'tcx>) -> (bool, bool) {
let mut visitor = DefUseVisitor {
defined: false,
used: false,
local: self.local,
liveness_mode: self.liveness_mode,
};

thing.apply(location, &mut visitor);

(visitor.defined, visitor.used)
}
}

struct DefUseVisitor {
defined: bool,
used: bool,
local: Local,
liveness_mode: LivenessMode,
}

impl<'tcx> Visitor<'tcx> for DefUseVisitor {
fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) {
if local == self.local {
match liveness::categorize(context, self.liveness_mode) {
Some(DefUse::Def) => self.defined = true,
Some(DefUse::Use) => self.used = true,
None => (),
}
}
}
}
1 change: 1 addition & 0 deletions src/librustc_mir/borrow_check/nll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use util::pretty::{self, ALIGN};
use self::mir_util::PassWhere;

mod constraint_generation;
pub mod explain_borrow;
pub(crate) mod region_infer;
mod renumber;
mod subtype_constraint_generation;
Expand Down
18 changes: 18 additions & 0 deletions src/librustc_mir/borrow_check/nll/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,4 +1229,22 @@ impl Cause {
}
}
}

pub(crate) fn root_cause(&self) -> &Cause {
match self {
Cause::LiveVar(..) |
Cause::DropVar(..) |
Cause::LiveOther(..) |
Cause::UniversalRegion(..) => {
self
}

Cause::Outlives {
original_cause,
..
} => {
original_cause.root_cause()
}
}
}
}
1 change: 1 addition & 0 deletions src/librustc_mir/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(decl_macro)]
#![feature(dyn_trait)]
#![feature(i128_type)]
#![feature(inclusive_range_syntax)]
#![feature(inclusive_range)]
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/nll/capture-ref-in-struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags:-Znll -Zborrowck=mir
// compile-flags:-Znll -Zborrowck=mir -Znll-dump-cause

// Test that a structure which tries to store a pointer to `y` into
// `p` (indirectly) fails to compile.
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/nll/capture-ref-in-struct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ error[E0597]: `y` does not live long enough
...
37 | }
| - borrowed value only lives until here
38 |
39 | deref(p);
| - borrow later used here
|
= note: borrowed value must be valid for lifetime '_#5r...

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/nll/closure-requirements/escape-argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
// basically checking that the MIR type checker correctly enforces the
// closure signature.

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

#![feature(rustc_attrs)]

Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/nll/closure-requirements/escape-argument.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ error[E0597]: `y` does not live long enough
38 | //~^ ERROR `y` does not live long enough [E0597]
39 | }
| - borrowed value only lives until here
40 |
41 | deref(p);
| - borrow later used here
|
= note: borrowed value must be valid for lifetime '_#6r...

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//
// except that the closure does so via a second closure.

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

#![feature(rustc_attrs)]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ error[E0597]: `y` does not live long enough
...
36 | }
| - borrowed value only lives until here
37 |
38 | deref(p);
| - borrow later used here
|
= note: borrowed value must be valid for lifetime '_#4r...

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/nll/closure-requirements/escape-upvar-ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
// `'b`. This relationship is propagated to the closure creator,
// which reports an error.

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

#![feature(rustc_attrs)]

Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ error[E0597]: `y` does not live long enough
...
36 | }
| - borrowed value only lives until here
37 |
38 | deref(p);
| - borrow later used here
|
= note: borrowed value must be valid for lifetime '_#4r...

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/nll/get_default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// a variety of errors from the older, AST-based machinery (notably
// borrowck), and then we get the NLL error at the end.

// compile-flags:-Znll -Zborrowck=compare
// compile-flags:-Znll -Zborrowck=compare -Znll-dump-cause

struct Map {
}
Expand Down
3 changes: 3 additions & 0 deletions src/test/ui/nll/get_default.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as imm
43 | Some(v) => {
44 | map.set(String::new()); // Both AST and MIR error here
| ^^^ mutable borrow occurs here
...
47 | return v;
| - borrow later used here

error: aborting due to 4 previous errors

Loading

0 comments on commit 3a185a5

Please sign in to comment.