Skip to content

Commit

Permalink
Add tracking of causes for nll
Browse files Browse the repository at this point in the history
  • Loading branch information
Nashenas88 authored and nikomatsakis committed Dec 20, 2017
1 parent f6741d0 commit e447b54
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 39 deletions.
14 changes: 7 additions & 7 deletions src/librustc_mir/borrow_check/nll/constraint_generation.rs
Expand Up @@ -20,7 +20,7 @@ use rustc::ty::subst::Substs;
use rustc::ty::fold::TypeFoldable;

use super::ToRegionVid;
use super::region_infer::RegionInferenceContext;
use super::region_infer::{RegionInferenceContext, Cause};

pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
Expand Down Expand Up @@ -53,14 +53,14 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
/// We sometimes have `substs` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_substs(&mut self, substs: &&'tcx Substs<'tcx>, location: Location) {
self.add_regular_live_constraint(*substs, location);
self.add_regular_live_constraint(*substs, location, Cause::LiveOther(location));
self.super_substs(substs);
}

/// We sometimes have `region` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) {
self.add_regular_live_constraint(*region, location);
self.add_regular_live_constraint(*region, location, Cause::LiveOther(location));
self.super_region(region);
}

Expand All @@ -75,7 +75,7 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
ty_context);
}
TyContext::Location(location) => {
self.add_regular_live_constraint(*ty, location);
self.add_regular_live_constraint(*ty, location, Cause::LiveOther(location));
}
}

Expand All @@ -85,7 +85,7 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
/// We sometimes have `closure_substs` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_closure_substs(&mut self, substs: &ClosureSubsts<'tcx>, location: Location) {
self.add_regular_live_constraint(*substs, location);
self.add_regular_live_constraint(*substs, location, Cause::LiveOther(location));
self.super_closure_substs(substs);
}

Expand All @@ -112,7 +112,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
/// `location` -- i.e., it may be used later. This means that all
/// regions appearing in the type `live_ty` must be live at
/// `location`.
fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location, cause: Cause)
where
T: TypeFoldable<'tcx>,
{
Expand All @@ -126,7 +126,7 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
.tcx
.for_each_free_region(&live_ty, |live_region| {
let vid = live_region.to_region_vid();
self.regioncx.add_live_point(vid, location);
self.regioncx.add_live_point(vid, location, &cause);
});
}

Expand Down
20 changes: 15 additions & 5 deletions src/librustc_mir/borrow_check/nll/region_infer/dfs.rs
Expand Up @@ -15,6 +15,7 @@ use borrow_check::nll::universal_regions::UniversalRegions;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::region_infer::values::{RegionElementIndex, RegionValueElements,
RegionValues};
use syntax::codemap::Span;
use rustc::mir::{Location, Mir};
use rustc::ty::RegionVid;
use rustc_data_structures::fx::FxHashSet;
Expand Down Expand Up @@ -127,6 +128,7 @@ pub(super) struct CopyFromSourceToTarget<'v> {
pub target_region: RegionVid,
pub inferred_values: &'v mut RegionValues,
pub constraint_point: Location,
pub constraint_span: Span,
}

impl<'v> DfsOp for CopyFromSourceToTarget<'v> {
Expand All @@ -143,14 +145,22 @@ impl<'v> DfsOp for CopyFromSourceToTarget<'v> {
}

fn add_to_target_region(&mut self, point_index: RegionElementIndex) -> Result<bool, !> {
Ok(self.inferred_values.add(self.target_region, point_index))
Ok(self.inferred_values.add_due_to_outlives(
self.source_region,
self.target_region,
point_index,
self.constraint_point,
self.constraint_span,
))
}

fn add_universal_regions_outlived_by_source_to_target(&mut self) -> Result<bool, !> {
Ok(
self.inferred_values
.add_universal_regions_outlived_by(self.source_region, self.target_region),
)
Ok(self.inferred_values.add_universal_regions_outlived_by(
self.source_region,
self.target_region,
self.constraint_point,
self.constraint_span,
))
}
}

Expand Down
70 changes: 62 additions & 8 deletions src/librustc_mir/borrow_check/nll/region_infer/mod.rs
Expand Up @@ -17,7 +17,7 @@ use rustc::infer::RegionVariableOrigin;
use rustc::infer::SubregionOrigin;
use rustc::infer::region_constraints::{GenericKind, VarOrigins};
use rustc::mir::{ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements,
Location, Mir};
Local, Location, Mir};
use rustc::traits::ObligationCause;
use rustc::ty::{self, RegionVid, Ty, TypeFoldable};
use rustc_data_structures::indexed_vec::IndexVec;
Expand Down Expand Up @@ -65,6 +65,8 @@ pub struct RegionInferenceContext<'tcx> {
universal_regions: UniversalRegions<'tcx>,
}

struct TrackCauses(bool);

struct RegionDefinition<'tcx> {
/// Why we created this variable. Mostly these will be
/// `RegionVariableOrigin::NLL`, but some variables get created
Expand All @@ -83,6 +85,38 @@ struct RegionDefinition<'tcx> {
external_name: Option<ty::Region<'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)]
pub(crate) enum Cause {
/// point inserted because Local was live at the given Location
LiveVar(Local, Location),

/// point inserted because Local was dropped at the given Location
DropVar(Local, Location),

/// point inserted because the type was live at the given Location,
/// but not as part of some local variable
LiveOther(Location),

/// part of the initial set of values for a universally quantified region
UniversalRegion,

/// 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,
},
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Constraint {
// NB. The ordering here is not significant for correctness, but
Expand Down Expand Up @@ -212,7 +246,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let mut result = Self {
definitions,
elements: elements.clone(),
liveness_constraints: RegionValues::new(elements, num_region_variables),
liveness_constraints: RegionValues::new(
elements,
num_region_variables,
TrackCauses(true),
),
inferred_values: None,
constraints: Vec::new(),
type_tests: Vec::new(),
Expand Down Expand Up @@ -262,11 +300,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {

// Add all nodes in the CFG to liveness constraints
for point_index in self.elements.all_point_indices() {
self.liveness_constraints.add(variable, point_index);
self.liveness_constraints.add(variable, point_index, &Cause::UniversalRegion);
}

// Add `end(X)` into the set for X.
self.liveness_constraints.add(variable, variable);
self.liveness_constraints.add(variable, variable, &Cause::UniversalRegion);
}
}

Expand Down Expand Up @@ -306,13 +344,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
///
/// Returns `true` if this constraint is new and `false` is the
/// constraint was already present.
pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) -> bool {
pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location, cause: &Cause) -> bool {
debug!("add_live_point({:?}, {:?})", v, point);
assert!(self.inferred_values.is_none(), "values already inferred");
debug!("add_live_point: @{:?}", point);
debug!("add_live_point: @{:?} Adding cause {:?}", point, cause);

let element = self.elements.index(point);
if self.liveness_constraints.add(v, element) {
if self.liveness_constraints.add(v, element, &cause) {
true
} else {
false
Expand Down Expand Up @@ -416,6 +454,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
target_region: constraint.sup,
inferred_values: &mut inferred_values,
constraint_point: constraint.point,
constraint_span: constraint.span,
},
);

Expand Down Expand Up @@ -973,7 +1012,7 @@ impl<'gcx, 'tcx> ClosureRegionRequirementsExt<'gcx, 'tcx> for ClosureRegionRequi
/// Given an instance T of the closure type, this method
/// instantiates the "extra" requirements that we computed for the
/// closure into the inference context. This has the effect of
/// adding new subregion obligations to existing variables.
/// adding new outlives obligations to existing variables.
///
/// As described on `ClosureRegionRequirements`, the extra
/// requirements are expressed in terms of regionvids that index
Expand Down Expand Up @@ -1075,3 +1114,18 @@ 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,
}
}
}
83 changes: 77 additions & 6 deletions src/librustc_mir/borrow_check/nll/region_infer/values.rs
Expand Up @@ -10,10 +10,14 @@

use std::rc::Rc;
use rustc_data_structures::bitvec::BitMatrix;
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::RegionVid;
use syntax::codemap::Span;

use super::{Cause, CauseExt, TrackCauses};

/// Maps between the various kinds of elements of a region value to
/// the internal indices that w use.
Expand Down Expand Up @@ -187,10 +191,22 @@ impl ToElementIndex for RegionElementIndex {
pub(super) struct RegionValues {
elements: Rc<RegionValueElements>,
matrix: BitMatrix,

/// If cause tracking is enabled, maps from a pair (r, e)
/// consisting of a region `r` that contains some element `e` to
/// the reason that the element is contained. There should be an
/// entry for every bit set to 1 in `BitMatrix`.
causes: Option<CauseMap>,
}

type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc<Cause>>;

impl RegionValues {
pub(super) fn new(elements: &Rc<RegionValueElements>, num_region_variables: usize) -> Self {
pub(super) fn new(
elements: &Rc<RegionValueElements>,
num_region_variables: usize,
track_causes: TrackCauses,
) -> Self {
assert!(
elements.num_universal_regions <= num_region_variables,
"universal regions are a subset of the region variables"
Expand All @@ -199,31 +215,80 @@ impl RegionValues {
Self {
elements: elements.clone(),
matrix: BitMatrix::new(num_region_variables, elements.num_elements()),
causes: if track_causes.0 {
Some(CauseMap::default())
} else {
None
},
}
}

/// Adds the given element to the value for the given region. Returns true if
/// the element is newly added (i.e., was not already present).
pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E) -> bool {
pub(super) fn add<E: ToElementIndex>(&mut self, r: RegionVid, elem: E, cause: &Cause) -> bool {
let i = self.elements.index(elem);
self.add_internal(r, i, |_| cause.clone())
}

/// Internal method to add an element to a region.
///
/// Takes a "lazy" cause -- this function will return the cause, but it will only
/// be invoked if cause tracking is enabled.
fn add_internal<F>(&mut self, r: RegionVid, i: RegionElementIndex, make_cause: F) -> bool
where
F: FnOnce(&CauseMap) -> Cause,
{
if self.matrix.add(r.index(), i.index()) {
debug!("add(r={:?}, i={:?})", r, self.elements.to_element(i));

if let Some(causes) = &mut self.causes {
let cause = Rc::new(make_cause(causes));
causes.insert((r, i), cause);
}

true
} else {
if let Some(causes) = &mut self.causes {
let cause = make_cause(causes);
let old_cause = causes.get_mut(&(r, i)).unwrap();
if cause < **old_cause {
*old_cause = Rc::new(cause);
return true;
}
}

false
}
}

/// Adds all the universal regions outlived by `from_region` to
/// `to_region`.
pub(super) fn add_due_to_outlives<T: ToElementIndex>(
&mut self,
from_region: RegionVid,
to_region: RegionVid,
elem: T,
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)
})
}

/// Adds all the universal regions outlived by `from_region` to
/// `to_region`.
pub(super) fn add_universal_regions_outlived_by(
&mut self,
from_region: RegionVid,
to_region: RegionVid,
constraint_location: Location,
constraint_span: Span,
) -> bool {
// FIXME. We could optimize this by improving
// `BitMatrix::merge` so it does not always merge an entire
// row.
// We could optimize this by improving `BitMatrix::merge` so
// it does not always merge an entire row. That would
// complicate causal tracking though.
debug!(
"add_universal_regions_outlived_by(from_region={:?}, to_region={:?})",
from_region,
Expand All @@ -232,7 +297,13 @@ impl RegionValues {
let mut changed = false;
for elem in self.elements.all_universal_region_indices() {
if self.contains(from_region, elem) {
changed |= self.add(to_region, elem);
changed |= self.add_due_to_outlives(
from_region,
to_region,
elem,
constraint_location,
constraint_span,
);
}
}
changed
Expand Down
Expand Up @@ -51,10 +51,10 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
outlives_sets.len()
);

for (region, location) in liveness_set {
for (region, location, cause) in liveness_set {
debug!("generate: {:#?} is live at {:#?}", region, location);
let region_vid = self.to_region_vid(region);
self.regioncx.add_live_point(region_vid, *location);
self.regioncx.add_live_point(region_vid, *location, &cause);
}

for OutlivesSet { locations, data } in outlives_sets {
Expand Down

0 comments on commit e447b54

Please sign in to comment.