Project region bounds out of the trait when deciding whether a

projection type outlives a given region. Fixes #20890.
This commit is contained in:
Niko Matsakis 2015-01-16 14:57:07 -05:00
parent ee2bfae011
commit bd621f0ccb
6 changed files with 200 additions and 2 deletions

View file

@ -84,6 +84,7 @@ pub struct FulfillmentContext<'tcx> {
region_obligations: NodeMap<Vec<RegionObligation<'tcx>>>,
}
#[derive(Clone)]
pub struct RegionObligation<'tcx> {
pub sub_region: ty::Region,
pub sup_type: Ty<'tcx>,

View file

@ -297,14 +297,25 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> {
fn visit_region_obligations(&mut self, node_id: ast::NodeId)
{
debug!("visit_region_obligations: node_id={}", node_id);
let fulfillment_cx = self.fcx.inh.fulfillment_cx.borrow();
for r_o in fulfillment_cx.region_obligations(node_id).iter() {
// Make a copy of the region obligations vec because we'll need
// to be able to borrow the fulfillment-cx below when projecting.
let region_obligations =
self.fcx.inh.fulfillment_cx.borrow()
.region_obligations(node_id)
.to_vec();
for r_o in region_obligations.iter() {
debug!("visit_region_obligations: r_o={}",
r_o.repr(self.tcx()));
let sup_type = self.resolve_type(r_o.sup_type);
let origin = infer::RelateRegionParamBound(r_o.cause.span);
type_must_outlive(self, origin, sup_type, r_o.sub_region);
}
// Processing the region obligations should not cause the list to grow further:
assert_eq!(region_obligations.len(),
self.fcx.inh.fulfillment_cx.borrow().region_obligations(node_id).len());
}
/// This method populates the region map's `free_region_map`. It walks over the transformed
@ -1480,6 +1491,15 @@ fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
generic.to_ty(rcx.tcx()),
param_env.caller_bounds.predicates.as_slice().to_vec());
// In the case of a projection T::Foo, we may be able to extract bounds from the trait def:
match *generic {
GenericKind::Param(..) => { }
GenericKind::Projection(ref projection_ty) => {
param_bounds.push_all(
&projection_bounds(rcx, origin.span(), projection_ty)[]);
}
}
// Add in the default bound of fn body that applies to all in
// scope type parameters:
param_bounds.push(param_env.implicit_region_bound);
@ -1511,3 +1531,73 @@ fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
region,
param_bounds);
}
fn projection_bounds<'a,'tcx>(rcx: &Rcx<'a, 'tcx>,
span: Span,
projection_ty: &ty::ProjectionTy<'tcx>)
-> Vec<ty::Region>
{
let fcx = rcx.fcx;
let tcx = fcx.tcx();
let infcx = fcx.infcx();
debug!("projection_bounds(projection_ty={})",
projection_ty.repr(tcx));
let ty = ty::mk_projection(tcx, projection_ty.trait_ref.clone(), projection_ty.item_name);
// Say we have a projection `<T as SomeTrait<'a>>::SomeType`. We are interested
// in looking for a trait definition like:
//
// ```
// trait SomeTrait<'a> {
// type SomeType : 'a;
// }
// ```
//
// we can thus deduce that `<T as SomeTrait<'a>>::SomeType : 'a`.
let trait_def = ty::lookup_trait_def(tcx, projection_ty.trait_ref.def_id);
let predicates = trait_def.generics.predicates.as_slice().to_vec();
traits::elaborate_predicates(tcx, predicates)
.filter_map(|predicate| {
// we're only interesting in `T : 'a` style predicates:
let outlives = match predicate {
ty::Predicate::TypeOutlives(data) => data,
_ => { return None; }
};
debug!("projection_bounds: outlives={} (1)",
outlives.repr(tcx));
// apply the substitutions (and normalize any projected types)
let outlives = fcx.instantiate_type_scheme(span,
projection_ty.trait_ref.substs,
&outlives);
debug!("projection_bounds: outlives={} (2)",
outlives.repr(tcx));
let region_result = infcx.try(|_| {
let (outlives, _) =
infcx.replace_late_bound_regions_with_fresh_var(
span,
infer::AssocTypeProjection(projection_ty.item_name),
&outlives);
debug!("projection_bounds: outlives={} (3)",
outlives.repr(tcx));
// check whether this predicate applies to our current projection
match infer::mk_eqty(infcx, false, infer::Misc(span), ty, outlives.0) {
Ok(()) => { Ok(outlives.1) }
Err(_) => { Err(()) }
}
});
debug!("projection_bounds: region_result={}",
region_result.repr(tcx));
region_result.ok()
})
.collect()
}

View file

@ -0,0 +1,31 @@
// Copyright 2015 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.
// Test that the compiler checks that arbitrary region bounds declared
// in the trait must be satisfied on the impl. Issue #20890.
trait Foo<'a> { type Value: 'a; }
impl<'a> Foo<'a> for &'a i16 {
// OK.
type Value = &'a i32;
}
impl<'a> Foo<'static> for &'a i32 {
//~^ ERROR cannot infer
type Value = &'a i32;
}
impl<'a,'b> Foo<'b> for &'a i64 {
//~^ ERROR cannot infer
type Value = &'a i32;
}
fn main() { }

View file

@ -0,0 +1,26 @@
// Copyright 2015 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.
// Test that the compiler checks that the 'static bound declared in
// the trait must be satisfied on the impl. Issue #20890.
trait Foo { type Value: 'static; }
impl<'a> Foo for &'a i32 {
//~^ ERROR cannot infer
type Value = &'a i32;
}
impl<'a> Foo for i32 {
// OK.
type Value = i32;
}
fn main() { }

View file

@ -0,0 +1,28 @@
// Copyright 2015 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.
// Test that the compiler considers the 'a bound declared in the
// trait. Issue #20890.
trait Foo<'a> {
type Value: 'a;
fn get(&self) -> &'a Self::Value;
}
fn takes_foo<'a,F: Foo<'a>>(f: &'a F) {
// This call would be illegal, because it results in &'a F::Value,
// and the only way we know that `F::Value : 'a` is because of the
// trait declaration.
f.get();
}
fn main() { }

View file

@ -0,0 +1,22 @@
// Copyright 2015 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.
// Test that the compiler considers the 'static bound declared in the
// trait. Issue #20890.
trait Foo { type Value: 'static; }
fn require_static<T: 'static>() {}
fn takes_foo<F: Foo>() {
require_static::<F::Value>()
}
fn main() { }