about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-11-18 10:07:25 -0500
committerNiko Matsakis <niko@alum.mit.edu>2018-11-18 15:00:34 -0500
commita5b4cb29919a19a9f92bf57a304461fe4239d46d (patch)
tree353e64a47ea25baaba444aa33288a33fa7a9db7b
parent485397e49a02a3b7ff77c17e4a3f16c653925cb3 (diff)
downloadrust-a5b4cb29919a19a9f92bf57a304461fe4239d46d.tar.gz
rust-a5b4cb29919a19a9f92bf57a304461fe4239d46d.zip
remove "approx env bounds" if we already know from trait
-rw-r--r--src/librustc/infer/outlives/obligations.rs30
-rw-r--r--src/test/ui/nll/ty-outlives/issue-55756.rs37
2 files changed, 60 insertions, 7 deletions
diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs
index f2825887f36..ab75a914d9c 100644
--- a/src/librustc/infer/outlives/obligations.rs
+++ b/src/librustc/infer/outlives/obligations.rs
@@ -389,22 +389,38 @@ where
         // rule might not apply (but another rule might). For now, we err
         // on the side of adding too few edges into the graph.
 
+        // Compute the bounds we can derive from the trait definition.
+        // These are guaranteed to apply, no matter the inference
+        // results.
+        let trait_bounds: Vec<_> = self.verify_bound
+            .projection_declared_bounds_from_trait(projection_ty)
+            .collect();
+
         // Compute the bounds we can derive from the environment. This
         // is an "approximate" match -- in some cases, these bounds
         // may not apply.
-        let approx_env_bounds = self.verify_bound
+        let mut approx_env_bounds = self.verify_bound
             .projection_approx_declared_bounds_from_env(projection_ty);
         debug!(
             "projection_must_outlive: approx_env_bounds={:?}",
             approx_env_bounds
         );
 
-        // Compute the bounds we can derive from the trait definition.
-        // These are guaranteed to apply, no matter the inference
-        // results.
-        let trait_bounds: Vec<_> = self.verify_bound
-            .projection_declared_bounds_from_trait(projection_ty)
-            .collect();
+        // Remove outlives bounds that we get from the environment but
+        // which are also deducable from the trait. This arises (cc
+        // #55756) in cases where you have e.g. `<T as Foo<'a>>::Item:
+        // 'a` in the environment but `trait Foo<'b> { type Item: 'b
+        // }` in the trait definition.
+        approx_env_bounds.retain(|bound| {
+            match bound.0.sty {
+                ty::Projection(projection_ty) => {
+                    self.verify_bound.projection_declared_bounds_from_trait(projection_ty)
+                        .all(|r| r != bound.1)
+                }
+
+                _ => panic!("expected only projection types from env, not {:?}", bound.0),
+            }
+        });
 
         // If declared bounds list is empty, the only applicable rule is
         // OutlivesProjectionComponent. If there are inference variables,
diff --git a/src/test/ui/nll/ty-outlives/issue-55756.rs b/src/test/ui/nll/ty-outlives/issue-55756.rs
new file mode 100644
index 00000000000..cda3915849e
--- /dev/null
+++ b/src/test/ui/nll/ty-outlives/issue-55756.rs
@@ -0,0 +1,37 @@
+// Regression test for #55756.
+//
+// In this test, the result of `self.callee` is a projection `<D as
+// Database<'?0>>::Guard`. As it may contain a destructor, the dropck
+// rules require that this type outlivess the scope of `state`. Unfortunately,
+// our region inference is not smart enough to figure out how to
+// translate a requirement like
+//
+//     <D as Database<'0>>::guard: 'r
+//
+// into a requirement that `'0: 'r` -- in particular, it fails to do
+// so because it *also* knows that `<D as Database<'a>>::Guard: 'a`
+// from the trait definition. Faced with so many choices, the current
+// solver opts to do nothing.
+//
+// Fixed by tweaking the solver to recognize that the constraint from
+// the environment duplicates one from the trait.
+//
+// compile-pass
+
+#![crate_type="lib"]
+
+pub trait Database<'a> {
+    type Guard: 'a;
+}
+
+pub struct Stateful<'a, D: 'a>(&'a D);
+
+impl<'b, D: for <'a> Database<'a>> Stateful<'b, D> {
+    pub fn callee<'a>(&'a self) -> <D as Database<'a>>::Guard {
+        unimplemented!()
+    }
+    pub fn caller<'a>(&'a self) -> <D as Database<'a>>::Guard {
+        let state = self.callee();
+        unimplemented!()
+    }
+}