about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-08-27 15:13:59 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-09-07 17:08:22 -0400
commit43e758798c34581b313fae54d28e95fca628b167 (patch)
tree13edf5f8c013226dfcd346f7b4110dd6c0f24f23
parentdb169e53e5c88484718c1be2be6dcb7dab4a278f (diff)
downloadrust-43e758798c34581b313fae54d28e95fca628b167.tar.gz
rust-43e758798c34581b313fae54d28e95fca628b167.zip
avoid propagating outlives obligations on locals if we can
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs17
-rw-r--r--src/test/ui/nll/issue-53570.rs46
2 files changed, 63 insertions, 0 deletions
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 46b3c708695..cb15c88bb3e 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -701,6 +701,23 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // `ClosureOutlivesRequirement`.
         let r_scc = self.constraint_sccs.scc(*lower_bound);
         for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
+            // Check whether we can already prove that the "subject" outlives `ur`.
+            // If so, we don't have to propagate this requirement to our caller.
+            //
+            // To continue the example from the function, if we are trying to promote
+            // a requirement that `T: 'X`, and we know that `'X = '1 + '2` (i.e., the union
+            // `'1` and `'2`), then in this loop `ur` will be `'1` (and `'2`). So here
+            // we check whether `T: '1` is something we *can* prove. If so, no need
+            // to propagate that requirement.
+            //
+            // This is needed because -- particularly in the case
+            // where `ur` is a local bound -- we are sometimes in a
+            // position to prove things that our caller cannot.  See
+            // #53570 for an example.
+            if self.eval_region_test(mir, ur, &type_test.test) {
+                continue;
+            }
+
             debug!("try_promote_type_test: ur={:?}", ur);
 
             let non_local_ub = self.universal_region_relations.non_local_upper_bound(ur);
diff --git a/src/test/ui/nll/issue-53570.rs b/src/test/ui/nll/issue-53570.rs
new file mode 100644
index 00000000000..9617e40ec39
--- /dev/null
+++ b/src/test/ui/nll/issue-53570.rs
@@ -0,0 +1,46 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Regression test for #53570. Here, we need to propagate that `T: 'a`
+// but in some versions of NLL we were propagating a stronger
+// requirement that `T: 'static`. This arose because we actually had
+// to propagate both that `T: 'a` but also `T: 'b` where `'b` is the
+// higher-ranked lifetime that appears in the type of the closure
+// parameter `x` -- since `'b` cannot be expressed in the caller's
+// space, that got promoted th `'static`.
+//
+// compile-pass
+
+#![feature(nll)]
+#![feature(rustc_attrs)]
+#![allow(dead_code)]
+
+use std::cell::{RefCell, Ref};
+
+trait AnyVec<'a> {
+}
+
+trait GenericVec<T> {
+    fn unwrap<'a, 'b>(vec: &'b AnyVec<'a>) -> &'b [T] where T: 'a;
+}
+
+struct Scratchpad<'a> {
+    buffers: RefCell<Box<AnyVec<'a>>>,
+}
+
+impl<'a> Scratchpad<'a> {
+    fn get<T: GenericVec<T>>(&self) -> Ref<[T]>
+    where T: 'a
+    {
+        Ref::map(self.buffers.borrow(), |x| T::unwrap(x.as_ref()))
+    }
+}
+
+fn main() { }