]> git.lizzy.rs Git - rust.git/commitdiff
Make causal tracking lazy
authorSantiago Pastorino <spastorino@gmail.com>
Mon, 5 Mar 2018 19:12:19 +0000 (16:12 -0300)
committerSantiago Pastorino <spastorino@gmail.com>
Tue, 6 Mar 2018 21:03:05 +0000 (18:03 -0300)
src/librustc/infer/error_reporting/mod.rs
src/librustc_mir/borrow_check/error_reporting.rs
src/librustc_mir/borrow_check/mod.rs
src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
src/librustc_mir/borrow_check/nll/region_infer/mod.rs
src/librustc_mir/borrow_check/nll/region_infer/values.rs
src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr

index 559b2720076f447030d042710fa0a698c55ddc3a..630d87c6098edeb358461cd414c12994694d4dd0 100644 (file)
@@ -138,9 +138,9 @@ pub fn note_and_explain_region(
                 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 @@ pub fn note_and_explain_free_region(
     }
 
     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;
index b77e7cf2ec8b5f1e597802e54cb39d3bb6ab613c..cfa1890e182eadc4e7dd9568c7eceb7d47222d1d 100644 (file)
@@ -124,6 +124,7 @@ pub(super) fn report_move_out_while_borrowed(
         (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 @@ pub(super) fn report_move_out_while_borrowed(
             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 @@ pub(super) fn report_use_while_mutably_borrowed(
         (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 @@ pub(super) fn report_conflicting_borrow(
             .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 @@ pub(super) fn report_conflicting_borrow(
             "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 @@ pub(super) fn report_conflicting_borrow(
                     Origin::Mir,
                 ),
 
-            (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => self.tcx
+            (BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => tcx
                 .cannot_mutably_borrow_multiply(
                     span,
                     &desc_place,
@@ -290,7 +293,7 @@ pub(super) fn report_conflicting_borrow(
                     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 @@ pub(super) fn report_conflicting_borrow(
                     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 @@ pub(super) fn report_conflicting_borrow(
                 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 @@ pub(super) fn report_conflicting_borrow(
                     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 @@ fn report_scoped_local_value_does_not_live_long_enough(
         _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 @@ fn report_scoped_temporary_value_does_not_live_long_enough(
         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 @@ fn report_unscoped_local_value_does_not_live_long_enough(
             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 @@ fn report_unscoped_local_value_does_not_live_long_enough(
         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 @@ fn report_unscoped_temporary_value_does_not_live_long_enough(
             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 @@ pub(super) fn report_illegal_mutation_of_borrowed(
         (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()),
index 1ff0ffaaa68b398e7166b2a6c01b4cc632cfc71d..06412a386e84a6fe52a6da387ebec0b8d58709c4 100644 (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:
index 19f95aeec70950f5170ddc13ed07ff7f6704a9ea..843407d0810f598ff201ea908b0d59435632142f 100644 (file)
 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) {
index 2151592fd663b45474a3337ca2dfeeeb466dd742..9b598b8dd5d1190aad26eabf3ab20cb69d689ce8 100644 (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 @@ pub fn region_contains_point<R>(&self, r: R, p: Location) -> bool
         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 @@ pub(super) fn solve<'gcx>(
         }
     }
 
+    /// 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 @@ fn propagate_constraints(&mut self, mir: &Mir<'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 @@ fn propagate_constraints(&mut self, mir: &Mir<'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 @@ fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, Option<usize>> {
     }
 }
 
+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
index 74ee04e0fb15e79f977c05104e8f070df7bd9e94..eb2756e2245d409bffc260beb4629ba08aa120d4 100644 (file)
@@ -17,7 +17,7 @@
 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 @@ fn to_element_index(self, _elements: &RegionValueElements) -> 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 @@ pub(super) fn new(
         }
     }
 
+    /// 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 {
index 21ed421fe96ca80e52355e77a62e37e76500724f..86653138a185f3652c1356bc318fb44bd36c3c95 100644 (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