diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index e3829892f7b..59a6b08638e 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -576,7 +576,7 @@ impl<'a> Visitor<()> for Context<'a> {
         run_lints!(self, check_lifetime_ref, lt);
     }
 
-    fn visit_lifetime_decl(&mut self, lt: &ast::Lifetime, _: ()) {
+    fn visit_lifetime_decl(&mut self, lt: &ast::LifetimeDef, _: ()) {
         run_lints!(self, check_lifetime_decl, lt);
     }
 
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index 9224647bc79..1bf77b42bec 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -150,7 +150,7 @@ pub trait LintPass {
     fn check_variant(&mut self, _: &Context, _: &ast::Variant, _: &ast::Generics) { }
     fn check_opt_lifetime_ref(&mut self, _: &Context, _: Span, _: &Option<ast::Lifetime>) { }
     fn check_lifetime_ref(&mut self, _: &Context, _: &ast::Lifetime) { }
-    fn check_lifetime_decl(&mut self, _: &Context, _: &ast::Lifetime) { }
+    fn check_lifetime_decl(&mut self, _: &Context, _: &ast::LifetimeDef) { }
     fn check_explicit_self(&mut self, _: &Context, _: &ast::ExplicitSelf) { }
     fn check_mac(&mut self, _: &Context, _: &ast::Mac) { }
     fn check_path(&mut self, _: &Context, _: &ast::Path, _: ast::NodeId) { }
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 1c85413a8d8..5001e7a88bd 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -59,10 +59,10 @@ struct LifetimeContext<'a> {
 enum ScopeChain<'a> {
     /// EarlyScope(i, ['a, 'b, ...], s) extends s with early-bound
     /// lifetimes, assigning indexes 'a => i, 'b => i+1, ... etc.
-    EarlyScope(subst::ParamSpace, &'a Vec<ast::Lifetime>, Scope<'a>),
+    EarlyScope(subst::ParamSpace, &'a Vec<ast::LifetimeDef>, Scope<'a>),
     /// LateScope(binder_id, ['a, 'b, ...], s) extends s with late-bound
     /// lifetimes introduced by the declaration binder_id.
-    LateScope(ast::NodeId, &'a Vec<ast::Lifetime>, Scope<'a>),
+    LateScope(ast::NodeId, &'a Vec<ast::LifetimeDef>, Scope<'a>),
     /// lifetimes introduced by items within a code block are scoped
     /// to that block.
     BlockScope(ast::NodeId, Scope<'a>),
@@ -136,7 +136,7 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> {
         fn push_fn_scope(this: &mut LifetimeContext,
                          ty: &ast::Ty,
                          scope: Scope,
-                         lifetimes: &Vec<ast::Lifetime>) {
+                         lifetimes: &Vec<ast::LifetimeDef>) {
             let scope1 = LateScope(ty.id, lifetimes, scope);
             this.check_lifetime_names(lifetimes);
             debug!("pushing fn scope id={} due to type", ty.id);
@@ -216,7 +216,7 @@ impl<'a> LifetimeContext<'a> {
             walk(self, &scope1)
         } else {
             let (early, late) = generics.lifetimes.clone().partition(
-                |l| referenced_idents.iter().any(|&i| i == l.name));
+                |l| referenced_idents.iter().any(|&i| i == l.lifetime.name));
 
             let scope1 = EarlyScope(subst::FnSpace, &early, scope);
             let scope2 = LateScope(n, &late, &scope1);
@@ -334,29 +334,39 @@ impl<'a> LifetimeContext<'a> {
                     token::get_name(lifetime_ref.name)).as_slice());
     }
 
-    fn check_lifetime_names(&self, lifetimes: &Vec<ast::Lifetime>) {
+    fn check_lifetime_names(&self, lifetimes: &Vec<ast::LifetimeDef>) {
         for i in range(0, lifetimes.len()) {
             let lifetime_i = lifetimes.get(i);
 
             let special_idents = [special_idents::static_lifetime];
             for lifetime in lifetimes.iter() {
-                if special_idents.iter().any(|&i| i.name == lifetime.name) {
+                if special_idents.iter().any(|&i| i.name == lifetime.lifetime.name) {
                     self.sess.span_err(
-                        lifetime.span,
+                        lifetime.lifetime.span,
                         format!("illegal lifetime parameter name: `{}`",
-                                token::get_name(lifetime.name)).as_slice());
+                                token::get_name(lifetime.lifetime.name))
+                            .as_slice());
                 }
             }
 
             for j in range(i + 1, lifetimes.len()) {
                 let lifetime_j = lifetimes.get(j);
 
-                if lifetime_i.name == lifetime_j.name {
+                if lifetime_i.lifetime.name == lifetime_j.lifetime.name {
                     self.sess.span_err(
-                        lifetime_j.span,
+                        lifetime_j.lifetime.span,
                         format!("lifetime name `{}` declared twice in \
                                 the same scope",
-                                token::get_name(lifetime_j.name)).as_slice());
+                                token::get_name(lifetime_j.lifetime.name))
+                            .as_slice());
+                }
+            }
+
+            for bound in lifetime_i.bounds.iter() {
+                if !self.sess.features.issue_5723_bootstrap.get() {
+                    self.sess.span_err(
+                        bound.span,
+                        "region bounds require `issue_5723_bootstrap`");
                 }
             }
         }
@@ -379,12 +389,12 @@ impl<'a> LifetimeContext<'a> {
     }
 }
 
-fn search_lifetimes(lifetimes: &Vec<ast::Lifetime>,
+fn search_lifetimes(lifetimes: &Vec<ast::LifetimeDef>,
                     lifetime_ref: &ast::Lifetime)
                     -> Option<(uint, ast::NodeId)> {
     for (i, lifetime_decl) in lifetimes.iter().enumerate() {
-        if lifetime_decl.name == lifetime_ref.name {
-            return Some((i, lifetime_decl.id));
+        if lifetime_decl.lifetime.name == lifetime_ref.name {
+            return Some((i, lifetime_decl.lifetime.id));
         }
     }
     return None;
@@ -392,15 +402,15 @@ fn search_lifetimes(lifetimes: &Vec<ast::Lifetime>,
 
 ///////////////////////////////////////////////////////////////////////////
 
-pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::Lifetime> {
+pub fn early_bound_lifetimes<'a>(generics: &'a ast::Generics) -> Vec<ast::LifetimeDef> {
     let referenced_idents = free_lifetimes(&generics.ty_params);
     if referenced_idents.is_empty() {
         return Vec::new();
     }
 
     generics.lifetimes.iter()
-        .filter(|l| referenced_idents.iter().any(|&i| i == l.name))
-        .map(|l| *l)
+        .filter(|l| referenced_idents.iter().any(|&i| i == l.lifetime.name))
+        .map(|l| (*l).clone())
         .collect()
 }
 
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index 9a48045b369..fb0a9e32df9 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -804,9 +804,10 @@ pub fn trait_def_of_item(ccx: &CrateCtxt, it: &ast::Item) -> Rc<ty::TraitDef> {
             generics.lifetimes
                     .iter()
                     .enumerate()
-                    .map(|(i, def)| ty::ReEarlyBound(def.id,
+                    .map(|(i, def)| ty::ReEarlyBound(def.lifetime.id,
                                                      subst::TypeSpace,
-                                                     i, def.name))
+                                                     i,
+                                                     def.lifetime.name))
                     .collect();
 
         let types =
@@ -1073,7 +1074,7 @@ fn add_unsized_bound(ccx: &CrateCtxt,
 
 fn ty_generics(ccx: &CrateCtxt,
                space: subst::ParamSpace,
-               lifetimes: &Vec<ast::Lifetime>,
+               lifetimes: &Vec<ast::LifetimeDef>,
                types: &OwnedSlice<ast::TyParam>,
                base_generics: ty::Generics)
                -> ty::Generics
@@ -1081,10 +1082,10 @@ fn ty_generics(ccx: &CrateCtxt,
     let mut result = base_generics;
 
     for (i, l) in lifetimes.iter().enumerate() {
-        let def = ty::RegionParameterDef { name: l.name,
+        let def = ty::RegionParameterDef { name: l.lifetime.name,
                                            space: space,
                                            index: i,
-                                           def_id: local_def(l.id) };
+                                           def_id: local_def(l.lifetime.id) };
         debug!("ty_generics: def for region param: {}", def);
         result.regions.push(space, def);
     }
diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs
index 8be1700b635..e191fb343b5 100644
--- a/src/librustc/middle/typeck/infer/error_reporting.rs
+++ b/src/librustc/middle/typeck/infer/error_reporting.rs
@@ -976,10 +976,13 @@ impl<'a> Rebuilder<'a> {
                         -> ast::Generics {
         let mut lifetimes = Vec::new();
         for lt in add.iter() {
-            lifetimes.push(*lt);
+            lifetimes.push(ast::LifetimeDef { lifetime: *lt,
+                                              bounds: Vec::new() });
         }
         for lt in generics.lifetimes.iter() {
-            if keep.contains(&lt.name) || !remove.contains(&lt.name) {
+            if keep.contains(&lt.lifetime.name) ||
+                !remove.contains(&lt.lifetime.name)
+            {
                 lifetimes.push((*lt).clone());
             }
         }
@@ -1439,7 +1442,7 @@ impl Resolvable for Rc<ty::TraitRef> {
 
 fn lifetimes_in_scope(tcx: &ty::ctxt,
                       scope_id: ast::NodeId)
-                      -> Vec<ast::Lifetime> {
+                      -> Vec<ast::LifetimeDef> {
     let mut taken = Vec::new();
     let parent = tcx.map.get_parent(scope_id);
     let method_id_opt = match tcx.map.find(parent) {
@@ -1486,10 +1489,10 @@ struct LifeGiver {
 }
 
 impl LifeGiver {
-    fn with_taken(taken: &[ast::Lifetime]) -> LifeGiver {
+    fn with_taken(taken: &[ast::LifetimeDef]) -> LifeGiver {
         let mut taken_ = HashSet::new();
         for lt in taken.iter() {
-            let lt_name = token::get_name(lt.name).get().to_string();
+            let lt_name = token::get_name(lt.lifetime.name).get().to_string();
             taken_.insert(lt_name);
         }
         LifeGiver {
diff --git a/src/librustc/middle/typeck/variance.rs b/src/librustc/middle/typeck/variance.rs
index 4a8bc97183a..be9ae0cc719 100644
--- a/src/librustc/middle/typeck/variance.rs
+++ b/src/librustc/middle/typeck/variance.rs
@@ -358,7 +358,8 @@ impl<'a> Visitor<()> for TermsContext<'a> {
             ast::ItemStruct(_, ref generics) |
             ast::ItemTrait(ref generics, _, _, _) => {
                 for (i, p) in generics.lifetimes.iter().enumerate() {
-                    self.add_inferred(item.id, RegionParam, TypeSpace, i, p.id);
+                    let id = p.lifetime.id;
+                    self.add_inferred(item.id, RegionParam, TypeSpace, i, id);
                 }
                 for (i, p) in generics.ty_params.iter().enumerate() {
                     self.add_inferred(item.id, TypeParam, TypeSpace, i, p.id);
diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs
index 94dea6cb540..d45ebb3858e 100644
--- a/src/librustc_back/svh.rs
+++ b/src/librustc_back/svh.rs
@@ -410,8 +410,8 @@ mod svh_visitor {
             SawLifetimeRef(content(l.name)).hash(self.st);
         }
 
-        fn visit_lifetime_decl(&mut self, l: &Lifetime, _: E) {
-            SawLifetimeDecl(content(l.name)).hash(self.st);
+        fn visit_lifetime_decl(&mut self, l: &LifetimeDef, _: E) {
+            SawLifetimeDecl(content(l.lifetime.name)).hash(self.st);
         }
 
         // We do recursively walk the bodies of functions/methods
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index e9d5fb70281..8dc57d3e574 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -614,6 +614,12 @@ impl Clean<Lifetime> for ast::Lifetime {
     }
 }
 
+impl Clean<Lifetime> for ast::LifetimeDef {
+    fn clean(&self) -> Lifetime {
+        Lifetime(token::get_name(self.lifetime.name).get().to_string())
+    }
+}
+
 impl Clean<Lifetime> for ty::RegionParameterDef {
     fn clean(&self) -> Lifetime {
         Lifetime(token::get_name(self.name).get().to_string())
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 2d39a47a85e..fc000d2357e 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -160,6 +160,12 @@ pub struct Lifetime {
     pub name: Name
 }
 
+#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
+pub struct LifetimeDef {
+    pub lifetime: Lifetime,
+    pub bounds: Vec<Lifetime>
+}
+
 /// A "Path" is essentially Rust's notion of a name; for instance:
 /// std::cmp::PartialEq  .  It's represented as a sequence of identifiers,
 /// along with a bunch of supporting information.
@@ -231,7 +237,7 @@ pub struct TyParam {
 /// of a function, enum, trait, etc.
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
 pub struct Generics {
-    pub lifetimes: Vec<Lifetime>,
+    pub lifetimes: Vec<LifetimeDef>,
     pub ty_params: OwnedSlice<TyParam>,
 }
 
@@ -861,7 +867,7 @@ impl fmt::Show for Onceness {
 /// Represents the type of a closure
 #[deriving(PartialEq, Eq, Encodable, Decodable, Hash, Show)]
 pub struct ClosureTy {
-    pub lifetimes: Vec<Lifetime>,
+    pub lifetimes: Vec<LifetimeDef>,
     pub fn_style: FnStyle,
     pub onceness: Onceness,
     pub decl: P<FnDecl>,
@@ -876,7 +882,7 @@ pub struct ClosureTy {
 pub struct BareFnTy {
     pub fn_style: FnStyle,
     pub abi: Abi,
-    pub lifetimes: Vec<Lifetime>,
+    pub lifetimes: Vec<LifetimeDef>,
     pub decl: P<FnDecl>
 }
 
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index e58187fe30d..cf2b5bc4063 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -369,7 +369,7 @@ impl<'a, O: IdVisitingOperation> IdVisitor<'a, O> {
             self.operation.visit_id(type_parameter.id)
         }
         for lifetime in generics.lifetimes.iter() {
-            self.operation.visit_id(lifetime.id)
+            self.operation.visit_id(lifetime.lifetime.id)
         }
     }
 }
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index d500600e25d..0e687c02c1d 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -73,6 +73,11 @@ pub trait AstBuilder {
     fn trait_ref(&self, path: ast::Path) -> ast::TraitRef;
     fn typarambound(&self, path: ast::Path) -> ast::TyParamBound;
     fn lifetime(&self, span: Span, ident: ast::Name) -> ast::Lifetime;
+    fn lifetime_def(&self,
+                    span: Span,
+                    name: ast::Name,
+                    bounds: Vec<ast::Lifetime>)
+                    -> ast::LifetimeDef;
 
     // statements
     fn stmt_expr(&self, expr: Gc<ast::Expr>) -> Gc<ast::Stmt>;
@@ -456,6 +461,17 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         ast::Lifetime { id: ast::DUMMY_NODE_ID, span: span, name: name }
     }
 
+    fn lifetime_def(&self,
+                    span: Span,
+                    name: ast::Name,
+                    bounds: Vec<ast::Lifetime>)
+                    -> ast::LifetimeDef {
+        ast::LifetimeDef {
+            lifetime: self.lifetime(span, name),
+            bounds: bounds
+        }
+    }
+
     fn stmt_expr(&self, expr: Gc<ast::Expr>) -> Gc<ast::Stmt> {
         box(GC) respan(expr.span, ast::StmtSemi(expr, ast::DUMMY_NODE_ID))
     }
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 9225e4414c4..5842ca4a0d5 100644
--- a/src/libsyntax/ext/deriving/generic/mod.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
@@ -395,7 +395,7 @@ impl<'a> TraitDef<'a> {
         let mut ty_params = ty_params.into_vec();
 
         // Copy the lifetimes
-        lifetimes.extend(generics.lifetimes.iter().map(|l| *l));
+        lifetimes.extend(generics.lifetimes.iter().map(|l| (*l).clone()));
 
         // Create the type parameters.
         ty_params.extend(generics.ty_params.iter().map(|ty_param| {
@@ -429,7 +429,11 @@ impl<'a> TraitDef<'a> {
             cx.ty_ident(self.span, ty_param.ident)
         });
 
-        let self_lifetimes = generics.lifetimes.clone();
+        let self_lifetimes: Vec<ast::Lifetime> =
+            generics.lifetimes
+            .iter()
+            .map(|ld| ld.lifetime)
+            .collect();
 
         // Create the type of `self`.
         let self_type = cx.ty_path(
diff --git a/src/libsyntax/ext/deriving/generic/ty.rs b/src/libsyntax/ext/deriving/generic/ty.rs
index 5d8b5660698..2130b6f4e9d 100644
--- a/src/libsyntax/ext/deriving/generic/ty.rs
+++ b/src/libsyntax/ext/deriving/generic/ty.rs
@@ -174,7 +174,9 @@ impl<'a> Ty<'a> {
                 let self_params = self_generics.ty_params.map(|ty_param| {
                     cx.ty_ident(span, ty_param.ident)
                 });
-                let lifetimes = self_generics.lifetimes.clone();
+                let lifetimes = self_generics.lifetimes.iter()
+                                                       .map(|d| d.lifetime)
+                                                       .collect();
 
                 cx.path_all(span, false, vec!(self_ty), lifetimes,
                             self_params.into_vec())
@@ -200,7 +202,7 @@ fn mk_ty_param(cx: &ExtCtxt, span: Span, name: &str,
     cx.typaram(span, cx.ident_of(name), bounds, unbound, None)
 }
 
-fn mk_generics(lifetimes: Vec<ast::Lifetime>, ty_params: Vec<ast::TyParam> ) -> Generics {
+fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam> ) -> Generics {
     Generics {
         lifetimes: lifetimes,
         ty_params: OwnedSlice::from_vec(ty_params)
@@ -210,7 +212,7 @@ fn mk_generics(lifetimes: Vec<ast::Lifetime>, ty_params: Vec<ast::TyParam> ) ->
 /// Lifetimes and bounds on type parameters
 #[deriving(Clone)]
 pub struct LifetimeBounds<'a> {
-    pub lifetimes: Vec<&'a str>,
+    pub lifetimes: Vec<(&'a str, Vec<&'a str>)>,
     pub bounds: Vec<(&'a str, Option<ast::TyParamBound>, Vec<Path<'a>>)>,
 }
 
@@ -226,8 +228,11 @@ impl<'a> LifetimeBounds<'a> {
                        self_ty: Ident,
                        self_generics: &Generics)
                        -> Generics {
-        let lifetimes = self.lifetimes.iter().map(|lt| {
-            cx.lifetime(span, cx.ident_of(*lt).name)
+        let lifetimes = self.lifetimes.iter().map(|&(ref lt, ref bounds)| {
+            let bounds =
+                bounds.iter().map(
+                    |b| cx.lifetime(span, cx.ident_of(*b).name)).collect();
+            cx.lifetime_def(span, cx.ident_of(*lt).name, bounds)
         }).collect();
         let ty_params = self.bounds.iter().map(|t| {
             match t {
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 2f82702ece4..80325c64349 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -163,6 +163,10 @@ pub trait Folder {
         noop_fold_lifetime(l, self)
     }
 
+    fn fold_lifetime_def(&mut self, l: &LifetimeDef) -> LifetimeDef {
+        noop_fold_lifetime_def(l, self)
+    }
+
     fn fold_attribute(&mut self, at: Attribute) -> Attribute {
         noop_fold_attribute(at, self)
     }
@@ -187,6 +191,10 @@ pub trait Folder {
         noop_fold_lifetimes(lts, self)
     }
 
+    fn fold_lifetime_defs(&mut self, lts: &[LifetimeDef]) -> Vec<LifetimeDef> {
+        noop_fold_lifetime_defs(lts, self)
+    }
+
     fn fold_ty_param(&mut self, tp: &TyParam) -> TyParam {
         noop_fold_ty_param(tp, self)
     }
@@ -337,7 +345,7 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
                 onceness: f.onceness,
                 bounds: fld.fold_opt_bounds(&f.bounds),
                 decl: fld.fold_fn_decl(&*f.decl),
-                lifetimes: f.lifetimes.iter().map(|l| fld.fold_lifetime(l)).collect(),
+                lifetimes: fld.fold_lifetime_defs(f.lifetimes.as_slice()),
             }, fld.fold_opt_lifetime(region))
         }
         TyProc(ref f) => {
@@ -346,12 +354,12 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
                 onceness: f.onceness,
                 bounds: fld.fold_opt_bounds(&f.bounds),
                 decl: fld.fold_fn_decl(&*f.decl),
-                lifetimes: f.lifetimes.iter().map(|l| fld.fold_lifetime(l)).collect(),
+                lifetimes: fld.fold_lifetime_defs(f.lifetimes.as_slice()),
             })
         }
         TyBareFn(ref f) => {
             TyBareFn(box(GC) BareFnTy {
-                lifetimes: f.lifetimes.iter().map(|l| fld.fold_lifetime(l)).collect(),
+                lifetimes: fld.fold_lifetime_defs(f.lifetimes.as_slice()),
                 fn_style: f.fn_style,
                 abi: f.abi,
                 decl: fld.fold_fn_decl(&*f.decl)
@@ -665,10 +673,23 @@ pub fn noop_fold_lifetime<T: Folder>(l: &Lifetime, fld: &mut T) -> Lifetime {
     }
 }
 
+pub fn noop_fold_lifetime_def<T: Folder>(l: &LifetimeDef, fld: &mut T)
+                                         -> LifetimeDef
+{
+    LifetimeDef {
+        lifetime: fld.fold_lifetime(&l.lifetime),
+        bounds: fld.fold_lifetimes(l.bounds.as_slice()),
+    }
+}
+
 pub fn noop_fold_lifetimes<T: Folder>(lts: &[Lifetime], fld: &mut T) -> Vec<Lifetime> {
     lts.iter().map(|l| fld.fold_lifetime(l)).collect()
 }
 
+pub fn noop_fold_lifetime_defs<T: Folder>(lts: &[LifetimeDef], fld: &mut T) -> Vec<LifetimeDef> {
+    lts.iter().map(|l| fld.fold_lifetime_def(l)).collect()
+}
+
 pub fn noop_fold_opt_lifetime<T: Folder>(o_lt: &Option<Lifetime>, fld: &mut T)
                                       -> Option<Lifetime> {
     o_lt.as_ref().map(|lt| fld.fold_lifetime(lt))
@@ -676,7 +697,7 @@ pub fn noop_fold_opt_lifetime<T: Folder>(o_lt: &Option<Lifetime>, fld: &mut T)
 
 pub fn noop_fold_generics<T: Folder>(generics: &Generics, fld: &mut T) -> Generics {
     Generics {ty_params: fld.fold_ty_params(generics.ty_params.as_slice()),
-              lifetimes: fld.fold_lifetimes(generics.lifetimes.as_slice())}
+              lifetimes: fld.fold_lifetime_defs(generics.lifetimes.as_slice())}
 }
 
 pub fn noop_fold_struct_def<T: Folder>(struct_def: Gc<StructDef>,
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index c9fba355c4d..7ea000d3aac 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1053,10 +1053,10 @@ impl<'a> Parser<'a> {
 
         */
 
-        let lifetimes = if self.eat(&token::LT) {
-            let lifetimes = self.parse_lifetimes();
+        let lifetime_defs = if self.eat(&token::LT) {
+            let lifetime_defs = self.parse_lifetime_defs();
             self.expect_gt();
-            lifetimes
+            lifetime_defs
         } else {
             Vec::new()
         };
@@ -1082,7 +1082,7 @@ impl<'a> Parser<'a> {
             onceness: Once,
             bounds: bounds,
             decl: decl,
-            lifetimes: lifetimes,
+            lifetimes: lifetime_defs,
         })
     }
 
@@ -1096,7 +1096,7 @@ impl<'a> Parser<'a> {
           |        |      |    |      |      Return type
           |        |      |    |  Closure bounds
           |        |      |  Argument types
-          |        |    Lifetimes
+          |        |    Lifetime defs
           |     Once-ness (a.k.a., affine)
         Function Style
 
@@ -1105,11 +1105,11 @@ impl<'a> Parser<'a> {
         let fn_style = self.parse_unsafety();
         let onceness = if self.eat_keyword(keywords::Once) {Once} else {Many};
 
-        let lifetimes = if self.eat(&token::LT) {
-            let lifetimes = self.parse_lifetimes();
+        let lifetime_defs = if self.eat(&token::LT) {
+            let lifetime_defs = self.parse_lifetime_defs();
             self.expect_gt();
 
-            lifetimes
+            lifetime_defs
         } else {
             Vec::new()
         };
@@ -1164,7 +1164,7 @@ impl<'a> Parser<'a> {
                 onceness: onceness,
                 bounds: bounds,
                 decl: decl,
-                lifetimes: lifetimes,
+                lifetimes: lifetime_defs,
             }, region)
         }
     }
@@ -1179,7 +1179,7 @@ impl<'a> Parser<'a> {
 
     /// Parse a function type (following the 'fn')
     pub fn parse_ty_fn_decl(&mut self, allow_variadic: bool)
-                            -> (P<FnDecl>, Vec<ast::Lifetime>) {
+                            -> (P<FnDecl>, Vec<ast::LifetimeDef>) {
         /*
 
         (fn) <'lt> (S) -> T
@@ -1187,13 +1187,13 @@ impl<'a> Parser<'a> {
                |    |     |
                |    |   Return type
                |  Argument types
-           Lifetimes
+           Lifetime_defs
 
         */
-        let lifetimes = if self.eat(&token::LT) {
-            let lifetimes = self.parse_lifetimes();
+        let lifetime_defs = if self.eat(&token::LT) {
+            let lifetime_defs = self.parse_lifetime_defs();
             self.expect_gt();
-            lifetimes
+            lifetime_defs
         } else {
             Vec::new()
         };
@@ -1206,7 +1206,7 @@ impl<'a> Parser<'a> {
             cf: ret_style,
             variadic: variadic
         });
-        (decl, lifetimes)
+        (decl, lifetime_defs)
     }
 
     /// Parse the methods in a trait declaration
@@ -1770,12 +1770,51 @@ impl<'a> Parser<'a> {
         }
     }
 
+    pub fn parse_lifetime_defs(&mut self) -> Vec<ast::LifetimeDef> {
+        /*!
+         * Parses `lifetime_defs = [ lifetime_defs { ',' lifetime_defs } ]`
+         * where `lifetime_def  = lifetime [':' lifetimes]`
+         */
+
+        let mut res = Vec::new();
+        loop {
+            match self.token {
+                token::LIFETIME(_) => {
+                    let lifetime = self.parse_lifetime();
+                    let bounds =
+                        if self.eat(&token::COLON) {
+                            self.parse_lifetimes(token::BINOP(token::PLUS))
+                        } else {
+                            Vec::new()
+                        };
+                    res.push(ast::LifetimeDef { lifetime: lifetime,
+                                                bounds: bounds });
+                }
+
+                _ => {
+                    return res;
+                }
+            }
+
+            match self.token {
+                token::COMMA => { self.bump(); }
+                token::GT => { return res; }
+                token::BINOP(token::SHR) => { return res; }
+                _ => {
+                    let msg = format!("expected `,` or `>` after lifetime \
+                                      name, got: {:?}",
+                                      self.token);
+                    self.fatal(msg.as_slice());
+                }
+            }
+        }
+    }
+
     // matches lifetimes = ( lifetime ) | ( lifetime , lifetimes )
     // actually, it matches the empty one too, but putting that in there
     // messes up the grammar....
-    pub fn parse_lifetimes(&mut self) -> Vec<ast::Lifetime> {
+    pub fn parse_lifetimes(&mut self, sep: token::Token) -> Vec<ast::Lifetime> {
         /*!
-         *
          * Parses zero or more comma separated lifetimes.
          * Expects each lifetime to be followed by either
          * a comma or `>`.  Used when parsing type parameter
@@ -1793,17 +1832,11 @@ impl<'a> Parser<'a> {
                 }
             }
 
-            match self.token {
-                token::COMMA => { self.bump();}
-                token::GT => { return res; }
-                token::BINOP(token::SHR) => { return res; }
-                _ => {
-                    let msg = format!("expected `,` or `>` after lifetime \
-                                      name, got: {:?}",
-                                      self.token);
-                    self.fatal(msg.as_slice());
-                }
+            if self.token != sep {
+                return res;
             }
+
+            self.bump();
         }
     }
 
@@ -3664,7 +3697,7 @@ impl<'a> Parser<'a> {
     /// where   typaramseq = ( typaram ) | ( typaram , typaramseq )
     pub fn parse_generics(&mut self) -> ast::Generics {
         if self.eat(&token::LT) {
-            let lifetimes = self.parse_lifetimes();
+            let lifetime_defs = self.parse_lifetime_defs();
             let mut seen_default = false;
             let ty_params = self.parse_seq_to_gt(Some(token::COMMA), |p| {
                 p.forbid_lifetime();
@@ -3678,14 +3711,14 @@ impl<'a> Parser<'a> {
                 }
                 ty_param
             });
-            ast::Generics { lifetimes: lifetimes, ty_params: ty_params }
+            ast::Generics { lifetimes: lifetime_defs, ty_params: ty_params }
         } else {
             ast_util::empty_generics()
         }
     }
 
     fn parse_generic_values_after_lt(&mut self) -> (Vec<ast::Lifetime>, Vec<P<Ty>> ) {
-        let lifetimes = self.parse_lifetimes();
+        let lifetimes = self.parse_lifetimes(token::COMMA);
         let result = self.parse_seq_to_gt(
             Some(token::COMMA),
             |p| {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index ab0269f807a..d60e4cb3b54 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2082,10 +2082,26 @@ impl<'a> State<'a> {
     }
 
     pub fn print_lifetime(&mut self,
-                          lifetime: &ast::Lifetime) -> IoResult<()> {
+                          lifetime: &ast::Lifetime)
+                          -> IoResult<()>
+    {
         self.print_name(lifetime.name)
     }
 
+    pub fn print_lifetime_def(&mut self,
+                              lifetime: &ast::LifetimeDef)
+                              -> IoResult<()>
+    {
+        try!(self.print_lifetime(&lifetime.lifetime));
+        let mut sep = ":";
+        for v in lifetime.bounds.iter() {
+            try!(word(&mut self.s, sep));
+            try!(self.print_lifetime(v));
+            sep = "+";
+        }
+        Ok(())
+    }
+
     pub fn print_generics(&mut self,
                           generics: &ast::Generics) -> IoResult<()> {
         let total = generics.lifetimes.len() + generics.ty_params.len();
@@ -2102,7 +2118,7 @@ impl<'a> State<'a> {
                 |s, &idx| {
                     if idx < generics.lifetimes.len() {
                         let lifetime = generics.lifetimes.get(idx);
-                        s.print_lifetime(lifetime)
+                        s.print_lifetime_def(lifetime)
                     } else {
                         let idx = idx - generics.lifetimes.len();
                         let param = generics.ty_params.get(idx);
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 733c4bfc2b0..647e81db1f1 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -122,7 +122,7 @@ pub trait Visitor<E: Clone> {
     fn visit_lifetime_ref(&mut self, _lifetime: &Lifetime, _e: E) {
         /*! Visits a reference to a lifetime */
     }
-    fn visit_lifetime_decl(&mut self, _lifetime: &Lifetime, _e: E) {
+    fn visit_lifetime_decl(&mut self, _lifetime: &LifetimeDef, _e: E) {
         /*! Visits a declaration of a lifetime */
     }
     fn visit_explicit_self(&mut self, es: &ExplicitSelf, e: E) {
@@ -424,7 +424,7 @@ pub fn walk_ty<E: Clone, V: Visitor<E>>(visitor: &mut V, typ: &Ty, env: E) {
 }
 
 fn walk_lifetime_decls<E: Clone, V: Visitor<E>>(visitor: &mut V,
-                                                lifetimes: &Vec<Lifetime>,
+                                                lifetimes: &Vec<LifetimeDef>,
                                                 env: E) {
     for l in lifetimes.iter() {
         visitor.visit_lifetime_decl(l, env.clone());
diff --git a/src/test/compile-fail/regions-bound-lists-feature-gate-2.rs b/src/test/compile-fail/regions-bound-lists-feature-gate-2.rs
new file mode 100644
index 00000000000..0f79716f370
--- /dev/null
+++ b/src/test/compile-fail/regions-bound-lists-feature-gate-2.rs
@@ -0,0 +1,18 @@
+// 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.
+
+// ignore-pretty
+
+trait Foo { }
+
+fn foo<'a, 'b:'a>() { //~ ERROR region bounds require `issue_5723_bootstrap`
+}
+
+pub fn main() { }
diff --git a/src/test/run-pass/regions-bound-lists-feature-gate-2.rs b/src/test/run-pass/regions-bound-lists-feature-gate-2.rs
new file mode 100644
index 00000000000..0a95a89d57c
--- /dev/null
+++ b/src/test/run-pass/regions-bound-lists-feature-gate-2.rs
@@ -0,0 +1,20 @@
+// 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.
+
+// ignore-pretty
+
+#![feature(issue_5723_bootstrap)]
+
+trait Foo { }
+
+fn foo<'a, 'b, 'c:'a+'b, 'd>() {
+}
+
+pub fn main() { }