1
Fork 0

Add three point error handling to borrowck

Closes #45988
This commit is contained in:
Santiago Pastorino 2017-12-12 17:29:37 -03:00 committed by Niko Matsakis
parent 6d2987ca03
commit 3a185a510d
24 changed files with 274 additions and 23 deletions

View file

@ -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};
@ -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> {

View file

@ -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;
@ -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`

View file

@ -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 => (),
}
}
}
}

View file

@ -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;

View file

@ -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()
}
}
}
}

View file

@ -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)]

View file

@ -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.

View file

@ -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...

View file

@ -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)]

View file

@ -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...

View file

@ -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)]

View file

@ -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...

View file

@ -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)]

View file

@ -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...

View file

@ -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 {
}

View file

@ -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

View file

@ -8,7 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll -Znll-dump-cause
#![allow(warnings)]

View file

@ -6,6 +6,9 @@ error[E0506]: cannot assign to `x` because it is borrowed
...
31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
32 | // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones
33 | }
| - borrow later used here, when `foo` is dropped
error: aborting due to previous error

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause
#![allow(warnings)]

View file

@ -6,6 +6,8 @@ error[E0506]: cannot assign to `x` because it is borrowed
...
31 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
32 | }
| - borrow later used here, when `foo` is dropped
error: aborting due to previous error

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause
#![allow(warnings)]

View file

@ -6,6 +6,9 @@ error[E0506]: cannot assign to `x` because it is borrowed
...
32 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
33 | // FIXME ^ This currently errors and it should not.
34 | }
| - borrow later used here, when `foo` is dropped
error: aborting due to previous error

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
//compile-flags: -Z emit-end-regions -Zborrowck=mir -Znll -Znll-dump-cause
#![allow(warnings)]

View file

@ -5,6 +5,8 @@ error[E0506]: cannot assign to `x` because it is borrowed
| ------ borrow of `x` occurs here
26 | x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
| ^^^^^ assignment to borrowed `x` occurs here
27 | }
| - borrow later used here, when `wrap` is dropped
error: aborting due to previous error