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:
parent
1fb17aba69
commit
ed72950fde
5 changed files with 42 additions and 100 deletions
|
@ -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>>,
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue