Generalize lifetime bounds on type parameters to support multiple

lifetime bounds. This doesn't really cause any difficulties, because
we already had to accommodate the fact that multiple implicit bounds
could accumulate. Object types still require precisely one lifetime
bound. This is a pre-step towards generalized where clauses (once you
have lifetime bounds in where clauses, it is harder to restrict them
to exactly one).
This commit is contained in:
Niko Matsakis 2014-08-14 18:05:27 -04:00
parent 946654a721
commit e86c87a81e
11 changed files with 81 additions and 18 deletions

View File

@ -680,14 +680,14 @@ fn parse_bounds(st: &mut PState, conv: conv_did) -> ty::ParamBounds {
let builtin_bounds = parse_builtin_bounds(st, |x,y| conv(x,y));
let mut param_bounds = ty::ParamBounds {
opt_region_bound: None,
region_bounds: Vec::new(),
builtin_bounds: builtin_bounds,
trait_bounds: Vec::new()
};
loop {
match next(st) {
'R' => {
param_bounds.opt_region_bound = Some(parse_region(st, |x, y| conv (x, y)));
param_bounds.region_bounds.push(parse_region(st, |x, y| conv (x, y)));
}
'I' => {
param_bounds.trait_bounds.push(Rc::new(parse_trait_ref(st, |x,y| conv(x,y))));

View File

@ -366,7 +366,7 @@ pub fn enc_existential_bounds(w: &mut SeekableMemWriter, cx: &ctxt, bs: &ty::Exi
pub fn enc_bounds(w: &mut SeekableMemWriter, cx: &ctxt, bs: &ty::ParamBounds) {
enc_builtin_bounds(w, cx, &bs.builtin_bounds);
for &r in bs.opt_region_bound.iter() {
for &r in bs.region_bounds.iter() {
mywrite!(w, "R");
enc_region(w, cx, r);
}

View File

@ -1008,7 +1008,7 @@ pub enum type_err {
/// as well as the existential type parameter in an object type.
#[deriving(PartialEq, Eq, Hash, Clone, Show)]
pub struct ParamBounds {
pub opt_region_bound: Option<ty::Region>,
pub region_bounds: Vec<ty::Region>,
pub builtin_bounds: BuiltinBounds,
pub trait_bounds: Vec<Rc<TraitRef>>
}
@ -1016,7 +1016,8 @@ pub struct ParamBounds {
/// Bounds suitable for an existentially quantified type parameter
/// such as those that appear in object types or closure types. The
/// major difference between this case and `ParamBounds` is that
/// general purpose trait bounds are omitted.
/// general purpose trait bounds are omitted and there must be
/// *exactly one* region.
#[deriving(PartialEq, Eq, Hash, Clone, Show)]
pub struct ExistentialBounds {
pub region_bound: ty::Region,
@ -4864,7 +4865,7 @@ pub fn required_region_bounds(tcx: &ctxt,
trait_bounds,
|trait_ref| {
let bounds = ty::bounds_for_trait_ref(tcx, &*trait_ref);
push_region_bounds(bounds.opt_region_bound.as_slice(),
push_region_bounds(bounds.region_bounds.as_slice(),
bounds.builtin_bounds,
&mut all_bounds);
debug!("from {}: bounds={} all_bounds={}",

View File

@ -287,7 +287,7 @@ impl TypeFoldable for ty::ExistentialBounds {
impl TypeFoldable for ty::ParamBounds {
fn fold_with<'tcx, F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::ParamBounds {
ty::ParamBounds {
opt_region_bound: self.opt_region_bound.fold_with(folder),
region_bounds: self.region_bounds.fold_with(folder),
builtin_bounds: self.builtin_bounds.fold_with(folder),
trait_bounds: self.trait_bounds.fold_with(folder),
}

View File

@ -2005,7 +2005,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let region_bounds =
ty::required_region_bounds(
self.tcx(),
param_bound.opt_region_bound.as_slice(),
param_bound.region_bounds.as_slice(),
param_bound.builtin_bounds,
param_bound.trait_bounds.as_slice());
for &r in region_bounds.iter() {

View File

@ -1880,7 +1880,7 @@ fn param_must_outlive(rcx: &Rcx,
let param_bound = param_env.bounds.get(param_ty.space, param_ty.idx);
param_bounds =
ty::required_region_bounds(rcx.tcx(),
param_bound.opt_region_bound.as_slice(),
param_bound.region_bounds.as_slice(),
param_bound.builtin_bounds,
param_bound.trait_bounds.as_slice());

View File

@ -327,7 +327,7 @@ impl<'a, 'tcx> Wf<'a, 'tcx> {
// Inspect bounds on this type parameter for any
// region bounds.
for &r in type_param_def.bounds.opt_region_bound.iter() {
for &r in type_param_def.bounds.region_bounds.iter() {
self.stack.push((r, Some(ty)));
self.accumulate_from_ty(type_param_ty);
self.stack.pop().unwrap();

View File

@ -1044,7 +1044,7 @@ fn ty_generics_for_trait(ccx: &CrateCtxt,
ident: special_idents::type_self,
def_id: local_def(param_id),
bounds: ty::ParamBounds {
opt_region_bound: None,
region_bounds: vec!(),
builtin_bounds: ty::empty_builtin_bounds(),
trait_bounds: vec!(self_trait_ref),
},
@ -1280,12 +1280,12 @@ fn conv_param_bounds(ccx: &CrateCtxt,
.map(|b| instantiate_trait_ref(ccx, b, param_ty.to_ty(ccx.tcx)))
.chain(unboxed_fn_ty_bounds)
.collect();
let opt_region_bound =
astconv::compute_opt_region_bound(
ccx.tcx, span, builtin_bounds, region_bounds.as_slice(),
trait_bounds.as_slice());
let region_bounds: Vec<ty::Region> =
region_bounds.move_iter()
.map(|r| ast_region_to_region(ccx.tcx, r))
.collect();
ty::ParamBounds {
opt_region_bound: opt_region_bound,
region_bounds: region_bounds,
builtin_bounds: builtin_bounds,
trait_bounds: trait_bounds,
}

View File

@ -35,10 +35,10 @@ fn test<
'a,
'b,
A:IsStatic,
B:Is<'a>+Is2<'b>, //~ ERROR ambiguous lifetime bound
B:Is<'a>+Is2<'b>, // OK in a parameter, but not an object type.
C:'b+Is<'a>+Is2<'b>,
D:Is<'a>+Is2<'static>,
E:'a+'b //~ ERROR only a single explicit lifetime bound is permitted
E:'a+'b // OK in a parameter, but not an object type.
>() { }
fn main() { }

View File

@ -0,0 +1,32 @@
// Copyright 2014 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.
// Various tests where we over type parameters with multiple lifetime
// bounds.
trait SomeTrait { fn get(&self) -> int; }
fn make_object_good1<'a,'b,A:SomeTrait+'a+'b>(v: A) -> Box<SomeTrait+'a> {
// A outlives 'a AND 'b...
box v as Box<SomeTrait+'a> // ...hence this type is safe.
}
fn make_object_good2<'a,'b,A:SomeTrait+'a+'b>(v: A) -> Box<SomeTrait+'b> {
// A outlives 'a AND 'b...
box v as Box<SomeTrait+'b> // ...hence this type is safe.
}
fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<SomeTrait+'c> {
// A outlives 'a AND 'b...but not 'c.
box v as Box<SomeTrait+'a> //~ ERROR mismatched types
}
fn main() {
}

View File

@ -0,0 +1,30 @@
// Copyright 2014 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.
// A test where we (successfully) close over a reference into
// an object.
trait SomeTrait { fn get(&self) -> int; }
impl<'a> SomeTrait for &'a int {
fn get(&self) -> int {
**self
}
}
fn make_object<'a,A:SomeTrait+'a>(v: A) -> Box<SomeTrait+'a> {
box v as Box<SomeTrait+'a>
}
fn main() {
let i: int = 22;
let obj = make_object(&i);
assert_eq!(22, obj.get());
}