diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 559b2720076f..630d87c6098e 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -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) { + 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) { let scope = region.free_region_binding_scope(self); let node = self.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID); let unknown; diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs index b77e7cf2ec8b..cfa1890e182e 100644 --- a/src/librustc_mir/borrow_check/error_reporting.rs +++ b/src/librustc_mir/borrow_check/error_reporting.rs @@ -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, ) { - 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, ) { + 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()), diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 1ff0ffaaa68b..06412a386e84 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -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>>, + nonlexical_cause_info: Option, } // Check that: diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs index 19f95aeec709..843407d0810f 100644 --- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs +++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs @@ -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) { diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 2151592fd663..9b598b8dd5d1 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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(&self, r: R, p: Location) -> Option> - 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(&self, r: R, p: Location) -> Option> + 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 diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs index 74ee04e0fb15..eb2756e2245d 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs @@ -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, matrix: SparseBitMatrix, @@ -199,6 +198,9 @@ pub(super) struct RegionValues { type CauseMap = FxHashMap<(RegionVid, RegionElementIndex), Rc>; 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, 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(&mut self, r: RegionVid, elem: E, cause: &Cause) -> bool { diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 21ed421fe96c..86653138a185 100644 --- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -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