Skip to content

Commit

Permalink
remove "approx env bounds" if we already know from trait
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed Nov 18, 2018
1 parent 485397e commit a5b4cb2
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 7 deletions.
30 changes: 23 additions & 7 deletions src/librustc/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
37 changes: 37 additions & 0 deletions src/test/ui/nll/ty-outlives/issue-55756.rs
Original file line number Diff line number Diff line change
@@ -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!()
}
}

0 comments on commit a5b4cb2

Please sign in to comment.