1
Fork 0

rework causal tracking to explore outlives relationships

Instead of tracking the "cause" of each bit that gets added, try to
recover that by walking outlives relationships. This is currently
imprecise, since it ignores the "point" where the outlives relationship
is incurred -- but that's ok, since we're about to stop considering that
overall in a later commit. This does seem to affect one error message
negatively, I didn't dig *too* hard to find out why.
This commit is contained in:
Niko Matsakis 2018-05-07 23:25:05 -04:00
parent 1fb17aba69
commit ed72950fde
5 changed files with 42 additions and 100 deletions

View file

@ -10,7 +10,7 @@
//! This query borrow-checks the MIR to (further) ensure it is not broken.
use borrow_check::nll::region_infer::{RegionCausalInfo, RegionInferenceContext};
use borrow_check::nll::region_infer::RegionInferenceContext;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
@ -248,7 +248,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
nonlexical_regioncx: regioncx,
used_mut: FxHashSet(),
used_mut_upvars: SmallVec::new(),
nonlexical_cause_info: None,
borrow_set,
dominators,
};
@ -367,7 +366,6 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
/// contains the results from region inference and lets us e.g.
/// find out which CFG points are contained in each borrow region.
nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
nonlexical_cause_info: Option<RegionCausalInfo>,
/// The set of borrows extracted from the MIR
borrow_set: Rc<BorrowSet<'tcx>>,

View file

@ -32,13 +32,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
let regioncx = &&self.nonlexical_regioncx;
let mir = self.mir;
if self.nonlexical_cause_info.is_none() {
self.nonlexical_cause_info = Some(regioncx.compute_causal_info(mir));
}
let cause_info = self.nonlexical_cause_info.as_ref().unwrap();
if let Some(cause) = cause_info.why_region_contains_point(borrow.region, context.loc) {
match *cause.root_cause() {
let borrow_region_vid = regioncx.to_region_vid(borrow.region);
if let Some(cause) = regioncx.why_region_contains_point(borrow_region_vid, context.loc) {
match cause {
Cause::LiveVar(local, location) => {
match find_regular_use(mir, regioncx, borrow, location, local) {
Some(p) => {

View file

@ -101,7 +101,7 @@ struct RegionDefinition<'tcx> {
/// NB: The variants in `Cause` are intentionally ordered. Lower
/// values are preferred when it comes to error messages. Do not
/// reorder willy nilly.
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub(crate) enum Cause {
/// point inserted because Local was live at the given Location
LiveVar(Local, Location),
@ -115,23 +115,6 @@ pub(crate) enum Cause {
/// part of the initial set of values for a universally quantified region
UniversalRegion(RegionVid),
/// Element E was added to R because there was some
/// outlives obligation `R: R1 @ P` and `R1` contained `E`.
Outlives {
/// the reason that R1 had E
original_cause: Rc<Cause>,
/// the point P from the relation
constraint_location: Location,
/// The span indicating why we added the outlives constraint.
constraint_span: Span,
},
}
pub(crate) struct RegionCausalInfo {
inferred_values: RegionValues,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -477,21 +460,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
/// Re-execute the region inference, this time tracking causal information.
/// This is significantly slower, so it is done only when an error is being reported.
pub(super) fn compute_causal_info(&self, mir: &Mir<'tcx>) -> RegionCausalInfo {
let dfs_storage = &mut self.new_dfs_storage();
let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(true));
RegionCausalInfo { inferred_values }
}
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later.
fn propagate_constraints(&mut self, mir: &Mir<'tcx>, dfs_storage: &mut dfs::DfsStorage) {
self.dependency_map = Some(self.build_dependency_map());
let inferred_values = self.compute_region_values(mir, dfs_storage, TrackCauses(false));
let inferred_values = self.compute_region_values(mir, dfs_storage);
self.inferred_values = Some(inferred_values);
}
@ -500,7 +475,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
&self,
mir: &Mir<'tcx>,
dfs_storage: &mut dfs::DfsStorage,
track_causes: TrackCauses,
) -> RegionValues {
debug!("compute_region_values()");
debug!("compute_region_values: constraints={:#?}", {
@ -511,7 +485,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// The initial values for each region are derived from the liveness
// constraints we have accumulated.
let mut inferred_values = self.liveness_constraints.duplicate(track_causes);
let mut inferred_values = self.liveness_constraints.duplicate(TrackCauses(false));
let dependency_map = self.dependency_map.as_ref().unwrap();
@ -1095,6 +1069,17 @@ impl<'tcx> RegionInferenceContext<'tcx> {
diag.emit();
}
crate fn why_region_contains_point(&self, fr1: RegionVid, elem: Location) -> Option<Cause> {
// Find some constraint `X: Y` where:
// - `fr1: X` transitively
// - and `Y` is live at `elem`
let index = self.blame_constraint(fr1, elem);
let region_sub = self.constraints[index].sub;
// then return why `Y` was live at `elem`
self.liveness_constraints.cause(region_sub, elem)
}
/// Tries to finds a good span to blame for the fact that `fr1`
/// contains `fr2`.
fn blame_constraint(&self, fr1: RegionVid, elem: impl ToElementIndex) -> ConstraintIndex {
@ -1112,7 +1097,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let relevant_constraint = self.constraints
.iter_enumerated()
.filter_map(|(i, constraint)| {
if self.liveness_constraints.contains(constraint.sub, elem) {
if !self.liveness_constraints.contains(constraint.sub, elem) {
None
} else {
influenced_fr1[constraint.sup]
@ -1163,16 +1148,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
impl RegionCausalInfo {
/// Returns the *reason* that the region `r` contains the given point.
pub(super) fn why_region_contains_point<R>(&self, r: R, p: Location) -> Option<Rc<Cause>>
where
R: ToRegionVid,
{
self.inferred_values.cause(r.to_region_vid(), p)
}
}
impl<'tcx> RegionDefinition<'tcx> {
fn new(origin: RegionVariableOrigin) -> Self {
// Create a new region definition. Note that, for free
@ -1316,31 +1291,3 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi
})
}
}
trait CauseExt {
fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause;
}
impl CauseExt for Rc<Cause> {
/// Creates a derived cause due to an outlives constraint.
fn outlives(&self, constraint_location: Location, constraint_span: Span) -> Cause {
Cause::Outlives {
original_cause: self.clone(),
constraint_location,
constraint_span,
}
}
}
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

@ -8,17 +8,18 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use borrow_check::nll::region_infer::TrackCauses;
use rustc_data_structures::bitvec::SparseBitMatrix;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc::ty::RegionVid;
use std::fmt::Debug;
use std::rc::Rc;
use syntax::codemap::Span;
use super::{Cause, CauseExt, TrackCauses};
use super::Cause;
/// Maps between the various kinds of elements of a region value to
/// the internal indices that w use.
@ -196,7 +197,7 @@ pub(super) struct RegionValues {
causes: Option<CauseMap>,
}
type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc<Cause>>;
type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Cause>;
impl RegionValues {
/// Creates a new set of "region values" that tracks causal information.
@ -255,7 +256,7 @@ impl RegionValues {
debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));
if let Some(causes) = &mut self.causes {
let cause = Rc::new(make_cause(causes));
let cause = make_cause(causes);
causes.insert((r, i), cause);
}
@ -267,15 +268,8 @@ impl RegionValues {
// #49998: compare using root cause alone to avoid
// useless traffic from similar outlives chains.
let overwrite = if ty::tls::with(|tcx| {
tcx.sess.opts.debugging_opts.nll_subminimal_causes
}) {
cause.root_cause() < old_cause.root_cause()
} else {
cause < **old_cause
};
if overwrite {
*old_cause = Rc::new(cause);
if cause < *old_cause {
*old_cause = cause;
return true;
}
}
@ -294,13 +288,11 @@ impl RegionValues {
from_region: RegionVid,
to_region: RegionVid,
elem: T,
constraint_location: Location,
constraint_span: Span,
_constraint_location: Location,
_constraint_span: Span,
) -> bool {
let elem = self.elements.index(elem);
self.add_internal(to_region, elem, |causes| {
causes[&(from_region, elem)].outlives(constraint_location, constraint_span)
})
self.add_internal(to_region, elem, |causes| causes[&(from_region, elem)])
}
/// Adds all the universal regions outlived by `from_region` to
@ -445,7 +437,7 @@ impl RegionValues {
///
/// Returns None if cause tracking is disabled or `elem` is not
/// actually found in `r`.
pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Rc<Cause>> {
pub(super) fn cause<T: ToElementIndex>(&self, r: RegionVid, elem: T) -> Option<Cause> {
let index = self.elements.index(elem);
if let Some(causes) = &self.causes {
causes.get(&(r, index)).cloned()

View file

@ -2,19 +2,28 @@ error[E0499]: cannot borrow `*arg` as mutable more than once at a time
--> $DIR/mut-borrow-in-loop.rs:20:25
|
LL | (self.func)(arg) //~ ERROR cannot borrow
| ^^^ mutable borrow starts here in previous iteration of loop
| ------------^^^-
| | |
| | mutable borrow starts here in previous iteration of loop
| borrow later used here
error[E0499]: cannot borrow `*arg` as mutable more than once at a time
--> $DIR/mut-borrow-in-loop.rs:26:25
|
LL | (self.func)(arg) //~ ERROR cannot borrow
| ^^^ mutable borrow starts here in previous iteration of loop
| ------------^^^-
| | |
| | mutable borrow starts here in previous iteration of loop
| borrow later used here
error[E0499]: cannot borrow `*arg` as mutable more than once at a time
--> $DIR/mut-borrow-in-loop.rs:33:25
|
LL | (self.func)(arg) //~ ERROR cannot borrow
| ^^^ mutable borrow starts here in previous iteration of loop
| ------------^^^-
| | |
| | mutable borrow starts here in previous iteration of loop
| borrow later used here
error: aborting due to 3 previous errors