Make causal tracking lazy

This commit is contained in:
Santiago Pastorino 2018-03-05 16:12:19 -03:00
parent ec761903ec
commit c85c5a0423
No known key found for this signature in database
GPG Key ID: 88C941CDA1D46432
7 changed files with 113 additions and 43 deletions

View File

@ -138,9 +138,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.explain_span(scope_decorated_tag, span)
}
ty::ReEarlyBound(_) | ty::ReFree(_) => self.msg_span_from_free_region(region),
ty::ReStatic => ("the static lifetime".to_owned(), None),
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
self.msg_span_from_free_region(region)
}
ty::ReEmpty => ("the empty lifetime".to_owned(), None),
@ -175,6 +175,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option<Span>) {
match *region {
ty::ReEarlyBound(_) | ty::ReFree(_) => {
self.msg_span_from_early_bound_and_free_regions(region)
},
ty::ReStatic => ("the static lifetime".to_owned(), None),
_ => bug!(),
}
}
fn msg_span_from_early_bound_and_free_regions(
self,
region: ty::Region<'tcx>,
) -> (String, Option<Span>) {
let scope = region.free_region_binding_scope(self);
let node = self.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
let unknown;

View File

@ -124,6 +124,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
(place, span): (&Place<'tcx>, Span),
borrow: &BorrowData<'tcx>,
) {
let tcx = self.tcx;
let value_msg = match self.describe_place(place) {
Some(name) => format!("`{}`", name),
None => "value".to_owned(),
@ -132,7 +133,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Some(name) => format!("`{}`", name),
None => "value".to_owned(),
};
let mut err = self.tcx.cannot_move_when_borrowed(
let mut err = tcx.cannot_move_when_borrowed(
span,
&self.describe_place(place).unwrap_or("_".to_owned()),
Origin::Mir,
@ -152,7 +153,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
(place, span): (&Place<'tcx>, Span),
borrow: &BorrowData<'tcx>,
) {
let mut err = self.tcx.cannot_use_when_mutably_borrowed(
let tcx = self.tcx;
let mut err = tcx.cannot_use_when_mutably_borrowed(
span,
&self.describe_place(place).unwrap_or("_".to_owned()),
self.retrieve_borrow_span(borrow),
@ -254,6 +256,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
.unwrap_or(issued_span);
let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
let tcx = self.tcx;
// FIXME: supply non-"" `opt_via` when appropriate
let mut err = match (
@ -265,7 +268,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
"mutable",
) {
(BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt)
| (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => self.tcx
| (BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => tcx
.cannot_reborrow_already_borrowed(
span,
&desc_place,
@ -279,7 +282,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
),
(BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => self.tcx
(BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => tcx
.cannot_mutably_borrow_multiply(
span,
&desc_place,
@ -290,7 +293,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
),
(BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx
(BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => tcx
.cannot_uniquely_borrow_by_two_closures(
span,
&desc_place,
@ -299,7 +302,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
),
(BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure(
(BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
span,
&desc_place,
"",
@ -310,7 +313,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
),
(BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => self.tcx
(BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => tcx
.cannot_reborrow_already_uniquely_borrowed(
span,
&desc_place,
@ -322,7 +325,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Origin::Mir,
),
(BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => self.tcx
(BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => tcx
.cannot_reborrow_already_uniquely_borrowed(
span,
&desc_place,
@ -466,7 +469,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
_proper_span: Span,
end_span: Option<Span>,
) {
let mut err = self.tcx.path_does_not_live_long_enough(
let tcx = self.tcx;
let mut err = tcx.path_does_not_live_long_enough(
borrow_span,
&format!("`{}`", name),
Origin::Mir,
@ -493,9 +497,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
proper_span: Span,
end_span: Option<Span>,
) {
let tcx = self.tcx;
let mut err =
self.tcx
.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
err.span_label(proper_span, "temporary value does not live long enough");
err.span_label(
drop_span,
@ -527,7 +531,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
context, name, scope_tree, borrow, drop_span, borrow_span
);
let mut err = self.tcx.path_does_not_live_long_enough(
let tcx = self.tcx;
let mut err = tcx.path_does_not_live_long_enough(
borrow_span,
&format!("`{}`", name),
Origin::Mir,
@ -535,8 +540,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
err.span_label(borrow_span, "borrowed value does not live long enough");
err.span_label(drop_span, "borrowed value only lives until here");
if !self.tcx.nll() {
self.tcx.note_and_explain_region(
if !tcx.nll() {
tcx.note_and_explain_region(
scope_tree,
&mut err,
"borrowed value must be valid for ",
@ -566,14 +571,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
context, scope_tree, borrow, drop_span, proper_span
);
let tcx = self.tcx;
let mut err =
self.tcx
.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
err.span_label(proper_span, "temporary value does not live long enough");
err.span_label(drop_span, "temporary value only lives until here");
if !self.tcx.nll() {
self.tcx.note_and_explain_region(
if !tcx.nll() {
tcx.note_and_explain_region(
scope_tree,
&mut err,
"borrowed value must be valid for ",
@ -592,7 +597,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
(place, span): (&Place<'tcx>, Span),
loan: &BorrowData<'tcx>,
) {
let mut err = self.tcx.cannot_assign_to_borrowed(
let tcx = self.tcx;
let mut err = tcx.cannot_assign_to_borrowed(
span,
self.retrieve_borrow_span(loan),
&self.describe_place(place).unwrap_or("_".to_owned()),

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::RegionInferenceContext;
use borrow_check::nll::region_infer::{RegionInferenceContext, RegionCausalInfo};
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
@ -231,6 +231,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
access_place_error_reported: FxHashSet(),
reservation_error_reported: FxHashSet(),
nonlexical_regioncx: opt_regioncx.clone(),
nonlexical_cause_info: None,
};
let borrows = Borrows::new(tcx, mir, opt_regioncx, def_id, body_id);
@ -311,6 +312,7 @@ 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: Option<Rc<RegionInferenceContext<'tcx>>>,
nonlexical_cause_info: Option<RegionCausalInfo>,
}
// Check that:

View File

@ -18,16 +18,26 @@ use rustc_errors::DiagnosticBuilder;
use util::liveness::{self, DefUse, LivenessMode};
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
/// Adds annotations to `err` explaining *why* the borrow contains the
/// point from `context`. This is key for the "3-point errors"
/// [described in the NLL RFC][d].
///
/// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
pub(in borrow_check) fn explain_why_borrow_contains_point(
&self,
&mut self,
context: Context,
borrow: &BorrowData<'tcx>,
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;
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() {
Cause::LiveVar(local, location) => {
match find_regular_use(&mir, regioncx, borrow, location, local) {

View File

@ -72,6 +72,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
@ -122,6 +124,10 @@ pub(crate) enum Cause {
},
}
pub(crate) struct RegionCausalInfo {
inferred_values: RegionValues,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Constraint {
// NB. The ordering here is not significant for correctness, but
@ -343,17 +349,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
inferred_values.contains(r.to_region_vid(), p)
}
/// Returns the *reason* that the region `r` contains the given point.
pub(crate) fn why_region_contains_point<R>(&self, r: R, p: Location) -> Option<Rc<Cause>>
where
R: ToRegionVid,
{
let inferred_values = self.inferred_values
.as_ref()
.expect("region values not yet inferred");
inferred_values.cause(r.to_region_vid(), p)
}
/// Returns access to the value of `r` for debugging purposes.
pub(super) fn region_value_str(&self, r: RegionVid) -> String {
let inferred_values = self.inferred_values
@ -444,13 +439,25 @@ 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 inferred_values = self.compute_region_values(mir, 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>) {
debug!("propagate_constraints()");
debug!("propagate_constraints: constraints={:#?}", {
let inferred_values = self.compute_region_values(mir, TrackCauses(false));
self.inferred_values = Some(inferred_values);
}
fn compute_region_values(&self, mir: &Mir<'tcx>, track_causes: TrackCauses) -> RegionValues {
debug!("compute_region_values()");
debug!("compute_region_values: constraints={:#?}", {
let mut constraints: Vec<_> = self.constraints.iter().collect();
constraints.sort();
constraints
@ -458,7 +465,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.clone();
let mut inferred_values = self.liveness_constraints.duplicate(track_causes);
let dependency_map = self.build_dependency_map();
@ -502,7 +509,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
debug!("\n");
}
self.inferred_values = Some(inferred_values);
inferred_values
}
/// Builds up a map from each region variable X to a vector with the
@ -1092,6 +1099,16 @@ 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

View File

@ -17,7 +17,7 @@ use rustc::mir::{BasicBlock, Location, Mir};
use rustc::ty::RegionVid;
use syntax::codemap::Span;
use super::{Cause, CauseExt};
use super::{Cause, CauseExt, TrackCauses};
/// Maps between the various kinds of elements of a region value to
/// the internal indices that w use.
@ -184,7 +184,6 @@ impl ToElementIndex for RegionElementIndex {
/// compact `SparseBitMatrix` representation, with one row per region
/// variable. The columns consist of either universal regions or
/// points in the CFG.
#[derive(Clone)]
pub(super) struct RegionValues {
elements: Rc<RegionValueElements>,
matrix: SparseBitMatrix<RegionVid, RegionElementIndex>,
@ -199,6 +198,9 @@ pub(super) struct RegionValues {
type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc<Cause>>;
impl RegionValues {
/// Creates a new set of "region values" that tracks causal information.
/// Each of the regions in num_region_variables will be initialized with an
/// empty set of points and no causal information.
pub(super) fn new(
elements: &Rc<RegionValueElements>,
num_region_variables: usize,
@ -218,6 +220,24 @@ impl RegionValues {
}
}
/// Duplicates the region values. If track_causes is false, then the
/// resulting value will not track causal information (and any existing
/// causal information is dropped). Otherwise, the causal information is
/// preserved and maintained. Tracking the causal information makes region
/// propagation significantly slower, so we prefer not to do it until an
/// error is reported.
pub(super) fn duplicate(&self, track_causes: TrackCauses) -> Self {
Self {
elements: self.elements.clone(),
matrix: self.matrix.clone(),
causes: if track_causes.0 {
self.causes.clone()
} 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, cause: &Cause) -> bool {

View File

@ -78,6 +78,8 @@ LL | let cell = Cell::new(&a);
...
LL | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to 2 previous errors