about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTaylor Cramer <cramertj@google.com>2017-11-16 22:59:45 -0800
committerNiko Matsakis <niko@alum.mit.edu>2017-11-22 12:44:09 -0500
commit91b7920c09004ed4dcbad0ab4128a664ca794848 (patch)
treebb18e5558bcef5aeeadfa70dbac668a980c0e6af
parent1dc0b573e7ce4314eb196b21b7e0ea4a1bf1f673 (diff)
downloadrust-91b7920c09004ed4dcbad0ab4128a664ca794848.tar.gz
rust-91b7920c09004ed4dcbad0ab4128a664ca794848.zip
Implement in-band lifetime bindings
-rw-r--r--src/librustc/diagnostics.rs2
-rw-r--r--src/librustc/hir/lowering.rs442
-rw-r--r--src/librustc/hir/mod.rs4
-rw-r--r--src/librustc/ich/impls_hir.rs3
-rw-r--r--src/librustc/ich/impls_ty.rs9
-rw-r--r--src/librustc/infer/error_reporting/different_lifetimes.rs19
-rw-r--r--src/librustc/middle/resolve_lifetime.rs105
-rw-r--r--src/librustc_typeck/astconv.rs4
-rw-r--r--src/librustc_typeck/collect.rs2
-rw-r--r--src/librustdoc/clean/mod.rs4
-rw-r--r--src/libsyntax/feature_gate.rs3
-rw-r--r--src/test/compile-fail/feature-gate-in_band_lifetimes.rs72
-rw-r--r--src/test/run-pass/in-band-lifetimes.rs84
-rw-r--r--src/test/ui/in-band-lifetimes/E0687.rs26
-rw-r--r--src/test/ui/in-band-lifetimes/E0687.stderr26
-rw-r--r--src/test/ui/in-band-lifetimes/E0687_where.rs18
-rw-r--r--src/test/ui/in-band-lifetimes/E0687_where.stderr14
-rw-r--r--src/test/ui/in-band-lifetimes/E0688.rs26
-rw-r--r--src/test/ui/in-band-lifetimes/E0688.stderr26
-rw-r--r--src/test/ui/in-band-lifetimes/mismatched.rs18
-rw-r--r--src/test/ui/in-band-lifetimes/mismatched.stderr18
-rw-r--r--src/test/ui/in-band-lifetimes/mismatched_trait.rs20
-rw-r--r--src/test/ui/in-band-lifetimes/mismatched_trait.stderr10
-rw-r--r--src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs24
-rw-r--r--src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr39
-rw-r--r--src/test/ui/in-band-lifetimes/mut_while_borrow.rs21
-rw-r--r--src/test/ui/in-band-lifetimes/mut_while_borrow.stderr10
-rw-r--r--src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs22
-rw-r--r--src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr14
-rw-r--r--src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs23
-rw-r--r--src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr14
-rw-r--r--src/test/ui/in-band-lifetimes/shadow.rs21
-rw-r--r--src/test/ui/in-band-lifetimes/shadow.stderr19
33 files changed, 1026 insertions, 136 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index cba1a66c17c..8089a88a9e8 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -2051,4 +2051,6 @@ register_diagnostics! {
     E0631, // type mismatch in closure arguments
     E0637, // "'_" is not a valid lifetime bound
     E0657, // `impl Trait` can only capture lifetimes bound at the fn level
+    E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax
+    E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders
 }
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 3e527f43fec..7fd6f4a8b42 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -106,6 +106,26 @@ pub struct LoweringContext<'a> {
     is_in_loop_condition: bool,
     is_in_trait_impl: bool,
 
+    // Used to create lifetime definitions from in-band lifetime usages.
+    // e.g. `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8`
+    // When a named lifetime is encountered in a function or impl header and
+    // has not been defined
+    // (i.e. it doesn't appear in the in_scope_lifetimes list), it is added
+    // to this list. The results of this list are then added to the list of
+    // lifetime definitions in the corresponding impl or function generics.
+    lifetimes_to_define: Vec<(Span, Name)>,
+    // Whether or not in-band lifetimes are being collected. This is used to
+    // indicate whether or not we're in a place where new lifetimes will result
+    // in in-band lifetime definitions, such a function or an impl header.
+    // This will always be false unless the `in_band_lifetimes` feature is
+    // enabled.
+    is_collecting_in_band_lifetimes: bool,
+    // Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
+    // When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
+    // against this list to see if it is already in-scope, or if a definition
+    // needs to be created for it.
+    in_scope_lifetimes: Vec<Name>,
+
     type_def_lifetime_params: DefIdMap<usize>,
 
     current_hir_id_owner: Vec<(DefIndex, u32)>,
@@ -177,6 +197,9 @@ pub fn lower_crate(sess: &Session,
         node_id_to_hir_id: IndexVec::new(),
         is_generator: false,
         is_in_trait_impl: false,
+        lifetimes_to_define: Vec::new(),
+        is_collecting_in_band_lifetimes: false,
+        in_scope_lifetimes: Vec::new(),
     }.lower_crate(krate)
 }
 
@@ -271,13 +294,23 @@ impl<'a> LoweringContext<'a> {
                 });
 
                 if item_lowered {
-                    if let ItemKind::Impl(_,_,_,_,ref opt_trait_ref,_,_) = item.node {
-                        self.with_trait_impl_ref(opt_trait_ref, |this| {
-                            visit::walk_item(this, item)
-                        });
-                    } else {
-                        visit::walk_item(self, item);
-                    }
+                    let item_lifetimes = match self.lctx.items.get(&item.id).unwrap().node {
+                        hir::Item_::ItemImpl(_,_,_,ref generics,..) |
+                        hir::Item_::ItemTrait(_,_,ref generics,..) =>
+                            generics.lifetimes.clone(),
+                        _ => Vec::new().into(),
+                    };
+
+                    self.lctx.with_parent_impl_lifetime_defs(&item_lifetimes, |this| {
+                        let this = &mut ItemLowerer { lctx: this };
+                        if let ItemKind::Impl(_,_,_,_,ref opt_trait_ref,_,_) = item.node {
+                            this.with_trait_impl_ref(opt_trait_ref, |this| {
+                                visit::walk_item(this, item)
+                            });
+                        } else {
+                            visit::walk_item(this, item);
+                        }
+                    });
                 }
             }
 
@@ -490,6 +523,124 @@ impl<'a> LoweringContext<'a> {
         span.with_ctxt(SyntaxContext::empty().apply_mark(mark))
     }
 
+    // Creates a new hir::LifetimeDef for every new lifetime encountered
+    // while evaluating `f`. Definitions are created with the parent provided.
+    // If no `parent_id` is provided, no definitions will be returned.
+    fn collect_in_band_lifetime_defs<T, F>(
+        &mut self,
+        parent_id: Option<DefId>,
+        f: F
+    ) -> (Vec<hir::LifetimeDef>, T) where F: FnOnce(&mut LoweringContext) -> T
+    {
+        assert!(!self.is_collecting_in_band_lifetimes);
+        assert!(self.lifetimes_to_define.is_empty());
+        self.is_collecting_in_band_lifetimes = self.sess.features.borrow().in_band_lifetimes;
+
+        let res = f(self);
+
+        self.is_collecting_in_band_lifetimes = false;
+
+        let lifetimes_to_define = self.lifetimes_to_define.split_off(0);
+
+        let lifetime_defs = match parent_id {
+            Some(parent_id) => lifetimes_to_define.into_iter().map(|(span, name)| {
+                    let def_node_id = self.next_id().node_id;
+
+                    // Add a definition for the in-band lifetime def
+                    self.resolver.definitions().create_def_with_parent(
+                        parent_id.index,
+                        def_node_id,
+                        DefPathData::LifetimeDef(name.as_str()),
+                        DefIndexAddressSpace::High,
+                        Mark::root()
+                    );
+
+                    hir::LifetimeDef {
+                        lifetime: hir::Lifetime {
+                            id: def_node_id,
+                            span,
+                            name: hir::LifetimeName::Name(name),
+                        },
+                        bounds: Vec::new().into(),
+                        pure_wrt_drop: false,
+                        in_band: true,
+                    }
+                }).collect(),
+            None => Vec::new(),
+        };
+
+        (lifetime_defs, res)
+    }
+
+    // Evaluates `f` with the lifetimes in `lt_defs` in-scope.
+    // This is used to track which lifetimes have already been defined, and
+    // which are new in-band lifetimes that need to have a definition created
+    // for them.
+    fn with_in_scope_lifetime_defs<T, F>(
+        &mut self,
+        lt_defs: &[LifetimeDef],
+        f: F
+    ) -> T where F: FnOnce(&mut LoweringContext) -> T
+    {
+        let old_len = self.in_scope_lifetimes.len();
+        let lt_def_names = lt_defs.iter().map(|lt_def| lt_def.lifetime.ident.name);
+        self.in_scope_lifetimes.extend(lt_def_names);
+
+        let res = f(self);
+
+        self.in_scope_lifetimes.truncate(old_len);
+        res
+    }
+
+    // Same as the method above, but accepts `hir::LifetimeDef`s
+    // instead of `ast::LifetimeDef`s.
+    // This should only be used with generics that have already had their
+    // in-band lifetimes added. In practice, this means that this function is
+    // only used when lowering a child item of a trait or impl.
+    fn with_parent_impl_lifetime_defs<T, F>(
+        &mut self,
+        lt_defs: &[hir::LifetimeDef],
+        f: F
+    ) -> T where F: FnOnce(&mut LoweringContext) -> T
+    {
+        let old_len = self.in_scope_lifetimes.len();
+        let lt_def_names = lt_defs.iter().map(|lt_def| lt_def.lifetime.name.name());
+        self.in_scope_lifetimes.extend(lt_def_names);
+
+        let res = f(self);
+
+        self.in_scope_lifetimes.truncate(old_len);
+        res
+    }
+
+    // Appends in-band lifetime defs to the existing set of out-of-band lifetime defs.
+    // Evaluates all within the context of the out-of-band defs.
+    // If provided, `impl_item_id` is used to find the parent impls of impl items so
+    // that their generics are not duplicated.
+    fn add_in_band_lifetime_defs<F, T>(
+        &mut self,
+        generics: &Generics,
+        parent_id: Option<DefId>,
+        f: F
+    ) -> (hir::Generics, T)
+        where F: FnOnce(&mut LoweringContext) -> T
+    {
+        let (in_band_defs, (mut lowered_generics, res)) =
+            self.with_in_scope_lifetime_defs(&generics.lifetimes, |this| {
+                this.collect_in_band_lifetime_defs(parent_id, |this| {
+                    (this.lower_generics(generics), f(this))
+                })
+            });
+
+        lowered_generics.lifetimes =
+            lowered_generics.lifetimes
+                .iter().cloned()
+               .chain(in_band_defs.into_iter())
+               .collect();
+
+        (lowered_generics, res)
+    }
+
     fn with_catch_scope<T, F>(&mut self, catch_id: NodeId, f: F) -> T
         where F: FnOnce(&mut LoweringContext) -> T
     {
@@ -710,13 +861,14 @@ impl<'a> LoweringContext<'a> {
                 hir::TyRptr(lifetime, self.lower_mt(mt, itctx))
             }
             TyKind::BareFn(ref f) => {
-                hir::TyBareFn(P(hir::BareFnTy {
-                    lifetimes: self.lower_lifetime_defs(&f.lifetimes),
-                    unsafety: self.lower_unsafety(f.unsafety),
-                    abi: f.abi,
-                    decl: self.lower_fn_decl(&f.decl, None, false),
-                    arg_names: self.lower_fn_args_to_names(&f.decl),
-                }))
+                self.with_in_scope_lifetime_defs(&f.lifetimes, |this|
+                    hir::TyBareFn(P(hir::BareFnTy {
+                        lifetimes: this.lower_lifetime_defs(&f.lifetimes),
+                        unsafety: this.lower_unsafety(f.unsafety),
+                        abi: f.abi,
+                        decl: this.lower_fn_decl(&f.decl, None, false),
+                        arg_names: this.lower_fn_args_to_names(&f.decl),
+                    })))
             }
             TyKind::Never => hir::TyNever,
             TyKind::Tup(ref tys) => {
@@ -906,6 +1058,7 @@ impl<'a> LoweringContext<'a> {
                             lifetime: def_lifetime,
                             bounds: Vec::new().into(),
                             pure_wrt_drop: false,
+                            in_band: false,
                         });
                     }
                 }
@@ -1344,23 +1497,44 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
+        let name = match self.lower_ident(l.ident) {
+            x if x == "'_" => hir::LifetimeName::Underscore,
+            x if x == "'static" => hir::LifetimeName::Static,
+            name => {
+                if  self.is_collecting_in_band_lifetimes &&
+                    !self.in_scope_lifetimes.contains(&name) &&
+                    self.lifetimes_to_define.iter()
+                        .find(|&&(_, lt_name)| lt_name == name)
+                        .is_none()
+                {
+                    self.lifetimes_to_define.push((l.span, name));
+                }
+
+                hir::LifetimeName::Name(name)
+            }
+        };
+
         hir::Lifetime {
             id: self.lower_node_id(l.id).node_id,
-            name: match self.lower_ident(l.ident) {
-                x if x == "'_" => hir::LifetimeName::Underscore,
-                x if x == "'static" => hir::LifetimeName::Static,
-                name => hir::LifetimeName::Name(name),
-            },
+            name,
             span: l.span,
         }
     }
 
     fn lower_lifetime_def(&mut self, l: &LifetimeDef) -> hir::LifetimeDef {
-        hir::LifetimeDef {
+        let was_collecting_in_band = self.is_collecting_in_band_lifetimes;
+        self.is_collecting_in_band_lifetimes = false;
+
+        let def = hir::LifetimeDef {
             lifetime: self.lower_lifetime(&l.lifetime),
             bounds: self.lower_lifetimes(&l.bounds),
             pure_wrt_drop: l.attrs.iter().any(|attr| attr.check_name("may_dangle")),
-        }
+            in_band: false,
+        };
+
+        self.is_collecting_in_band_lifetimes = was_collecting_in_band;
+
+        def
     }
 
     fn lower_lifetimes(&mut self, lts: &Vec<Lifetime>) -> hir::HirVec<hir::Lifetime> {
@@ -1435,15 +1609,19 @@ impl<'a> LoweringContext<'a> {
                                                                 ref bounded_ty,
                                                                 ref bounds,
                                                                 span}) => {
-                hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
-                    bound_lifetimes: self.lower_lifetime_defs(bound_lifetimes),
-                    bounded_ty: self.lower_ty(bounded_ty, ImplTraitContext::Disallowed),
-                    bounds: bounds.iter().filter_map(|bound| match *bound {
-                        // Ignore `?Trait` bounds, they were copied into type parameters already.
-                        TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
-                        _ => Some(self.lower_ty_param_bound(bound, ImplTraitContext::Disallowed))
-                    }).collect(),
-                    span,
+                self.with_in_scope_lifetime_defs(bound_lifetimes, |this| {
+                    hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+                        bound_lifetimes: this.lower_lifetime_defs(bound_lifetimes),
+                        bounded_ty: this.lower_ty(bounded_ty, ImplTraitContext::Disallowed),
+                        bounds: bounds.iter().filter_map(|bound| match *bound {
+                            // Ignore `?Trait` bounds.
+                            // Tthey were copied into type parameters already.
+                            TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
+                            _ => Some(this.lower_ty_param_bound(
+                                    bound, ImplTraitContext::Disallowed))
+                        }).collect(),
+                        span,
+                    })
                 })
             }
             WherePredicate::RegionPredicate(WhereRegionPredicate{ ref lifetime,
@@ -1504,9 +1682,13 @@ impl<'a> LoweringContext<'a> {
                             p: &PolyTraitRef,
                             itctx: ImplTraitContext)
                             -> hir::PolyTraitRef {
+        let bound_lifetimes = self.lower_lifetime_defs(&p.bound_lifetimes);
+        let trait_ref = self.with_parent_impl_lifetime_defs(&bound_lifetimes,
+                                |this| this.lower_trait_ref(&p.trait_ref, itctx));
+
         hir::PolyTraitRef {
-            bound_lifetimes: self.lower_lifetime_defs(&p.bound_lifetimes),
-            trait_ref: self.lower_trait_ref(&p.trait_ref, itctx),
+            bound_lifetimes,
+            trait_ref,
             span: p.span,
         }
     }
@@ -1678,11 +1860,15 @@ impl<'a> LoweringContext<'a> {
                         let body = this.lower_block(body, false);
                         this.expr_block(body, ThinVec::new())
                     });
-                    hir::ItemFn(this.lower_fn_decl(decl, fn_def_id, true),
+                    let (generics, fn_decl) =
+                        this.add_in_band_lifetime_defs(generics, fn_def_id, |this|
+                            this.lower_fn_decl(decl, fn_def_id, true));
+
+                    hir::ItemFn(fn_decl,
                                 this.lower_unsafety(unsafety),
                                 this.lower_constness(constness),
                                 abi,
-                                this.lower_generics(generics),
+                                generics,
                                 body_id)
                 })
             }
@@ -1723,29 +1909,42 @@ impl<'a> LoweringContext<'a> {
             ItemKind::Impl(unsafety,
                            polarity,
                            defaultness,
-                           ref generics,
+                           ref ast_generics,
                            ref ifce,
                            ref ty,
                            ref impl_items) => {
-                let new_impl_items = impl_items.iter()
-                                               .map(|item| self.lower_impl_item_ref(item))
-                                               .collect();
-                let ifce = ifce.as_ref().map(|trait_ref| {
-                    self.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed)
+                let def_id = self.resolver.definitions().opt_local_def_id(id);
+                let (generics, (ifce, lowered_ty)) =
+                    self.add_in_band_lifetime_defs(ast_generics, def_id, |this| {
+                        let ifce = ifce.as_ref().map(|trait_ref| {
+                            this.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed)
+                        });
+
+                        if let Some(ref trait_ref) = ifce {
+                            if let Def::Trait(def_id) = trait_ref.path.def {
+                                this.trait_impls.entry(def_id).or_insert(vec![]).push(id);
+                            }
+                        }
+
+                        let lowered_ty = this.lower_ty(ty, ImplTraitContext::Disallowed);
+
+                        (ifce, lowered_ty)
+                    });
+
+                let new_impl_items = self.with_in_scope_lifetime_defs(
+                                            &ast_generics.lifetimes, |this| {
+                    impl_items.iter()
+                              .map(|item| this.lower_impl_item_ref(item))
+                              .collect()
                 });
 
-                if let Some(ref trait_ref) = ifce {
-                    if let Def::Trait(def_id) = trait_ref.path.def {
-                        self.trait_impls.entry(def_id).or_insert(vec![]).push(id);
-                    }
-                }
 
                 hir::ItemImpl(self.lower_unsafety(unsafety),
                               self.lower_impl_polarity(polarity),
                               self.lower_defaultness(defaultness, true /* [1] */),
-                              self.lower_generics(generics),
+                              generics,
                               ifce,
-                              self.lower_ty(ty, ImplTraitContext::Disallowed),
+                              lowered_ty,
                               new_impl_items)
             }
             ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => {
@@ -1769,41 +1968,55 @@ impl<'a> LoweringContext<'a> {
             let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id);
             let fn_def_id = this.resolver.definitions().opt_local_def_id(node_id);
 
+            let (generics, node) = match i.node {
+                TraitItemKind::Const(ref ty, ref default) => {
+                    (
+                        this.lower_generics(&i.generics),
+                        hir::TraitItemKind::Const(
+                            this.lower_ty(ty, ImplTraitContext::Disallowed),
+                            default.as_ref().map(|x| {
+                                this.lower_body(None, |this| this.lower_expr(x))
+                            }))
+                    )
+                }
+                TraitItemKind::Method(ref sig, None) => {
+                    let names = this.lower_fn_args_to_names(&sig.decl);
+                    this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this|
+                        hir::TraitItemKind::Method(
+                            this.lower_method_sig(sig, fn_def_id, false),
+                            hir::TraitMethod::Required(names)))
+                }
+                TraitItemKind::Method(ref sig, Some(ref body)) => {
+                    let body_id = this.lower_body(Some(&sig.decl), |this| {
+                        let body = this.lower_block(body, false);
+                        this.expr_block(body, ThinVec::new())
+                    });
+
+                    this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this|
+                        hir::TraitItemKind::Method(
+                            this.lower_method_sig(sig, fn_def_id, false),
+                           hir::TraitMethod::Provided(body_id)))
+                }
+                TraitItemKind::Type(ref bounds, ref default) => {
+                    (
+                        this.lower_generics(&i.generics),
+                        hir::TraitItemKind::Type(
+                            this.lower_bounds(bounds, ImplTraitContext::Disallowed),
+                            default.as_ref().map(|x| {
+                                this.lower_ty(x, ImplTraitContext::Disallowed)
+                            }))
+                    )
+                }
+                TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"),
+            };
+
             hir::TraitItem {
                 id: node_id,
                 hir_id,
                 name: this.lower_ident(i.ident),
                 attrs: this.lower_attrs(&i.attrs),
-                generics: this.lower_generics(&i.generics),
-                node: match i.node {
-                    TraitItemKind::Const(ref ty, ref default) => {
-                        hir::TraitItemKind::Const(this.lower_ty(ty, ImplTraitContext::Disallowed),
-                                                  default.as_ref().map(|x| {
-                            this.lower_body(None, |this| this.lower_expr(x))
-                        }))
-                    }
-                    TraitItemKind::Method(ref sig, None) => {
-                        let names = this.lower_fn_args_to_names(&sig.decl);
-                        hir::TraitItemKind::Method(this.lower_method_sig(sig, fn_def_id, false),
-                                                   hir::TraitMethod::Required(names))
-                    }
-                    TraitItemKind::Method(ref sig, Some(ref body)) => {
-                        let body_id = this.lower_body(Some(&sig.decl), |this| {
-                            let body = this.lower_block(body, false);
-                            this.expr_block(body, ThinVec::new())
-                        });
-                        hir::TraitItemKind::Method(this.lower_method_sig(sig, fn_def_id, false),
-                                                   hir::TraitMethod::Provided(body_id))
-                    }
-                    TraitItemKind::Type(ref bounds, ref default) => {
-                        hir::TraitItemKind::Type(this.lower_bounds(bounds,
-                                                                   ImplTraitContext::Disallowed),
-                                                 default.as_ref().map(|x| {
-                                                     this.lower_ty(x, ImplTraitContext::Disallowed)
-                                                 }))
-                    }
-                    TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"),
-                },
+                generics,
+                node,
                 span: i.span,
             }
         })
@@ -1838,37 +2051,46 @@ impl<'a> LoweringContext<'a> {
             let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id);
             let fn_def_id = this.resolver.definitions().opt_local_def_id(node_id);
 
+            let (generics, node) = match i.node {
+                ImplItemKind::Const(ref ty, ref expr) => {
+                    let body_id = this.lower_body(None, |this| this.lower_expr(expr));
+                    (
+                        this.lower_generics(&i.generics),
+                        hir::ImplItemKind::Const(
+                            this.lower_ty(ty, ImplTraitContext::Disallowed),
+                            body_id
+                        )
+                    )
+                }
+                ImplItemKind::Method(ref sig, ref body) => {
+                    let body_id = this.lower_body(Some(&sig.decl), |this| {
+                        let body = this.lower_block(body, false);
+                        this.expr_block(body, ThinVec::new())
+                    });
+                    let impl_trait_return_allow = !this.is_in_trait_impl;
+
+                    this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this|
+                        hir::ImplItemKind::Method(
+                            this.lower_method_sig(sig, fn_def_id, impl_trait_return_allow),
+                            body_id))
+                }
+                ImplItemKind::Type(ref ty) => (
+                    this.lower_generics(&i.generics),
+                    hir::ImplItemKind::Type(
+                        this.lower_ty(ty, ImplTraitContext::Disallowed)),
+                ),
+                ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"),
+            };
+
             hir::ImplItem {
                 id: node_id,
                 hir_id,
                 name: this.lower_ident(i.ident),
                 attrs: this.lower_attrs(&i.attrs),
-                generics: this.lower_generics(&i.generics),
+                generics,
                 vis: this.lower_visibility(&i.vis, None),
                 defaultness: this.lower_defaultness(i.defaultness, true /* [1] */),
-                node: match i.node {
-                    ImplItemKind::Const(ref ty, ref expr) => {
-                        let body_id = this.lower_body(None, |this| this.lower_expr(expr));
-                        hir::ImplItemKind::Const(
-                            this.lower_ty(ty, ImplTraitContext::Disallowed),
-                            body_id
-                        )
-                    }
-                    ImplItemKind::Method(ref sig, ref body) => {
-                        let body_id = this.lower_body(Some(&sig.decl), |this| {
-                            let body = this.lower_block(body, false);
-                            this.expr_block(body, ThinVec::new())
-                        });
-                        let impl_trait_return_allow = !this.is_in_trait_impl;
-                        hir::ImplItemKind::Method(this.lower_method_sig(sig,
-                                                                        fn_def_id,
-                                                                        impl_trait_return_allow),
-                                                  body_id)
-                    }
-                    ImplItemKind::Type(ref ty) =>
-                        hir::ImplItemKind::Type(this.lower_ty(ty, ImplTraitContext::Disallowed)),
-                    ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"),
-                },
+                node,
                 span: i.span,
             }
         })
@@ -1958,16 +2180,26 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem {
         self.with_parent_def(i.id, |this| {
+            let node_id = this.lower_node_id(i.id).node_id;
+            let def_id = this.resolver.definitions().local_def_id(node_id);
             hir::ForeignItem {
-                id: this.lower_node_id(i.id).node_id,
+                id: node_id,
                 name: i.ident.name,
                 attrs: this.lower_attrs(&i.attrs),
                 node: match i.node {
                     ForeignItemKind::Fn(ref fdec, ref generics) => {
                         // Disallow impl Trait in foreign items
-                        hir::ForeignItemFn(this.lower_fn_decl(fdec, None, false),
-                                           this.lower_fn_args_to_names(fdec),
-                                           this.lower_generics(generics))
+                        let (generics, (fn_dec, fn_args)) =
+                            this.add_in_band_lifetime_defs(
+                                generics,
+                                Some(def_id),
+                                |this| (
+                                    this.lower_fn_decl(fdec, None, false),
+                                    this.lower_fn_args_to_names(fdec)
+                                )
+                            );
+
+                        hir::ForeignItemFn(fn_dec, fn_args, generics)
                     }
                     ForeignItemKind::Static(ref t, m) => {
                         hir::ForeignItemStatic(this.lower_ty(t, ImplTraitContext::Disallowed), m)
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 9ef82d09380..39ec33eef1f 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -222,6 +222,10 @@ pub struct LifetimeDef {
     pub lifetime: Lifetime,
     pub bounds: HirVec<Lifetime>,
     pub pure_wrt_drop: bool,
+    // Indicates that the lifetime definition was synthetically added
+    // as a result of an in-band lifetime usage like
+    // `fn foo(x: &'a u8) -> &'a u8 { x }`
+    pub in_band: bool,
 }
 
 /// A "Path" is essentially Rust's notion of a name; for instance:
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index 87ee28a7258..bccef6dc91b 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -157,7 +157,8 @@ impl_stable_hash_for!(struct hir::Lifetime {
 impl_stable_hash_for!(struct hir::LifetimeDef {
     lifetime,
     bounds,
-    pure_wrt_drop
+    pure_wrt_drop,
+    in_band
 });
 
 impl_stable_hash_for!(struct hir::Path {
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index 8f2ad98f858..45e80b58a4f 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -493,10 +493,15 @@ for ::middle::resolve_lifetime::Set1<T>
     }
 }
 
+impl_stable_hash_for!(enum ::middle::resolve_lifetime::LifetimeDefOrigin {
+    Explicit,
+    InBand
+});
+
 impl_stable_hash_for!(enum ::middle::resolve_lifetime::Region {
     Static,
-    EarlyBound(index, decl),
-    LateBound(db_index, decl),
+    EarlyBound(index, decl, is_in_band),
+    LateBound(db_index, decl, is_in_band),
     LateBoundAnon(db_index, anon_index),
     Free(call_site_scope_data, decl)
 });
diff --git a/src/librustc/infer/error_reporting/different_lifetimes.rs b/src/librustc/infer/error_reporting/different_lifetimes.rs
index c64bd610962..cade67a44bb 100644
--- a/src/librustc/infer/error_reporting/different_lifetimes.rs
+++ b/src/librustc/infer/error_reporting/different_lifetimes.rs
@@ -281,7 +281,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
                     // Find the index of the named region that was part of the
                     // error. We will then search the function parameters for a bound
                     // region at the right depth with the same index
-                    (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
+                    (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => {
                         debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \
                                         def_id={:?}", id, def_id);
                         if id == def_id {
@@ -293,7 +293,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
                     // Find the index of the named region that was part of the
                     // error. We will then search the function parameters for a bound
                     // region at the right depth with the same index
-                    (Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => {
+                    (
+                     Some(rl::Region::LateBound(debruijn_index, id, _)),
+                     ty::BrNamed(def_id, _)
+                    ) => {
                         debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
                                debruijn_index.depth);
                         debug!("self.infcx.tcx.hir.local_def_id(id)={:?}", id);
@@ -306,8 +309,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
 
                     (Some(rl::Region::Static), _) |
                     (Some(rl::Region::Free(_, _)), _) |
-                    (Some(rl::Region::EarlyBound(_, _)), _) |
-                    (Some(rl::Region::LateBound(_, _)), _) |
+                    (Some(rl::Region::EarlyBound(_, _, _)), _) |
+                    (Some(rl::Region::LateBound(_, _, _)), _) |
                     (Some(rl::Region::LateBoundAnon(_, _)), _) |
                     (None, _) => {
                         debug!("no arg found");
@@ -368,7 +371,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
                 }
             }
 
-            (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
+            (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => {
                 debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \
                                         def_id={:?}", id, def_id);
                 if id == def_id {
@@ -377,7 +380,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
                 }
             }
 
-            (Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => {
+            (Some(rl::Region::LateBound(debruijn_index, id, _)), ty::BrNamed(def_id, _)) => {
                 debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
                        debruijn_index.depth);
                 debug!("id={:?}", id);
@@ -389,8 +392,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
             }
 
             (Some(rl::Region::Static), _) |
-            (Some(rl::Region::EarlyBound(_, _)), _) |
-            (Some(rl::Region::LateBound(_, _)), _) |
+            (Some(rl::Region::EarlyBound(_, _, _)), _) |
+            (Some(rl::Region::LateBound(_, _, _)), _) |
             (Some(rl::Region::LateBoundAnon(_, _)), _) |
             (Some(rl::Region::Free(_, _)), _) |
             (None, _) => {
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 7ed646bb9dd..b39975d3ff9 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -36,11 +36,32 @@ use rustc_back::slice;
 use hir;
 use hir::intravisit::{self, Visitor, NestedVisitorMap};
 
+/// The origin of a named lifetime definition.
+///
+/// This is used to prevent the usage of in-band lifetimes in `Fn`/`fn` syntax.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
+pub enum LifetimeDefOrigin {
+    // Explicit binders like `fn foo<'a>(x: &'a u8)`
+    Explicit,
+    // In-band declarations like `fn foo(x: &'a u8)`
+    InBand,
+}
+
+impl LifetimeDefOrigin {
+    fn from_is_in_band(is_in_band: bool) -> Self {
+        if is_in_band {
+            LifetimeDefOrigin::InBand
+        } else {
+            LifetimeDefOrigin::Explicit
+        }
+    }
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)]
 pub enum Region {
     Static,
-    EarlyBound(/* index */ u32, /* lifetime decl */ DefId),
-    LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId),
+    EarlyBound(/* index */ u32, /* lifetime decl */ DefId, LifetimeDefOrigin),
+    LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId, LifetimeDefOrigin),
     LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32),
     Free(DefId, /* lifetime decl */ DefId),
 }
@@ -52,14 +73,16 @@ impl Region {
         let i = *index;
         *index += 1;
         let def_id = hir_map.local_def_id(def.lifetime.id);
+        let origin = LifetimeDefOrigin::from_is_in_band(def.in_band);
         debug!("Region::early: index={} def_id={:?}", i, def_id);
-        (def.lifetime.name, Region::EarlyBound(i, def_id))
+        (def.lifetime.name, Region::EarlyBound(i, def_id, origin))
     }
 
     fn late(hir_map: &Map, def: &hir::LifetimeDef) -> (hir::LifetimeName, Region) {
         let depth = ty::DebruijnIndex::new(1);
         let def_id = hir_map.local_def_id(def.lifetime.id);
-        (def.lifetime.name, Region::LateBound(depth, def_id))
+        let origin = LifetimeDefOrigin::from_is_in_band(def.in_band);
+        (def.lifetime.name, Region::LateBound(depth, def_id, origin))
     }
 
     fn late_anon(index: &Cell<u32>) -> Region {
@@ -74,16 +97,16 @@ impl Region {
             Region::Static |
             Region::LateBoundAnon(..) => None,
 
-            Region::EarlyBound(_, id) |
-            Region::LateBound(_, id) |
+            Region::EarlyBound(_, id, _) |
+            Region::LateBound(_, id, _) |
             Region::Free(_, id) => Some(id)
         }
     }
 
     fn shifted(self, amount: u32) -> Region {
         match self {
-            Region::LateBound(depth, id) => {
-                Region::LateBound(depth.shifted(amount), id)
+            Region::LateBound(depth, id, origin) => {
+                Region::LateBound(depth.shifted(amount), id, origin)
             }
             Region::LateBoundAnon(depth, index) => {
                 Region::LateBoundAnon(depth.shifted(amount), index)
@@ -94,10 +117,10 @@ impl Region {
 
     fn from_depth(self, depth: u32) -> Region {
         match self {
-            Region::LateBound(debruijn, id) => {
+            Region::LateBound(debruijn, id, origin) => {
                 Region::LateBound(ty::DebruijnIndex {
                     depth: debruijn.depth - (depth - 1)
-                }, id)
+                }, id, origin)
             }
             Region::LateBoundAnon(debruijn, index) => {
                 Region::LateBoundAnon(ty::DebruijnIndex {
@@ -110,7 +133,7 @@ impl Region {
 
     fn subst(self, params: &[hir::Lifetime], map: &NamedRegionMap)
              -> Option<Region> {
-        if let Region::EarlyBound(index, _) = self {
+        if let Region::EarlyBound(index, _, _) = self {
             params.get(index as usize).and_then(|lifetime| {
                 map.defs.get(&lifetime.id).cloned()
             })
@@ -187,6 +210,9 @@ struct LifetimeContext<'a, 'tcx: 'a> {
     // I'm sorry.
     trait_ref_hack: bool,
 
+    // Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax.
+    is_in_fn_syntax: bool,
+
     // List of labels in the function/method currently under analysis.
     labels_in_fn: Vec<(ast::Name, Span)>,
 
@@ -280,6 +306,7 @@ pub fn krate(sess: &Session,
             map: &mut map,
             scope: ROOT_SCOPE,
             trait_ref_hack: false,
+            is_in_fn_syntax: false,
             labels_in_fn: vec![],
             xcrate_object_lifetime_defaults: DefIdMap(),
         };
@@ -384,6 +411,8 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         match ty.node {
             hir::TyBareFn(ref c) => {
                 let next_early_index = self.next_early_index();
+                let was_in_fn_syntax = self.is_in_fn_syntax;
+                self.is_in_fn_syntax = true;
                 let scope = Scope::Binder {
                     lifetimes: c.lifetimes.iter().map(|def| {
                         Region::late(self.hir_map, def)
@@ -397,6 +426,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     this.check_lifetime_defs(old_scope, &c.lifetimes);
                     intravisit::walk_ty(this, ty);
                 });
+                self.is_in_fn_syntax = was_in_fn_syntax;
             }
             hir::TyTraitObject(ref bounds, ref lifetime) => {
                 for bound in bounds {
@@ -430,7 +460,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     // well-supported at the moment, so this doesn't work.
                     // In the future, this should be fixed and this error should be removed.
                     let def = self.map.defs.get(&lifetime.id);
-                    if let Some(&Region::LateBound(_, def_id)) = def {
+                    if let Some(&Region::LateBound(_, def_id, _)) = def {
                         if let Some(node_id) = self.hir_map.as_local_node_id(def_id) {
                             // Ensure that the parent of the def is an item, not HRTB
                             let parent_id = self.hir_map.get_parent_node(node_id);
@@ -528,6 +558,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     }
 
     fn visit_generics(&mut self, generics: &'tcx hir::Generics) {
+        check_mixed_explicit_and_in_band_defs(&self.sess, &generics.lifetimes);
         for ty_param in generics.ty_params.iter() {
             walk_list!(self, visit_ty_param_bound, &ty_param.bounds);
             if let Some(ref ty) = ty_param.default {
@@ -639,6 +670,22 @@ impl ShadowKind {
     }
 }
 
+fn check_mixed_explicit_and_in_band_defs(
+    sess: &Session,
+    lifetime_defs: &[hir::LifetimeDef],
+) {
+    let oob_def = lifetime_defs.iter().find(|lt| !lt.in_band);
+    let in_band_def = lifetime_defs.iter().find(|lt| lt.in_band);
+
+    if let (Some(oob_def), Some(in_band_def)) = (oob_def, in_band_def) {
+        struct_span_err!(sess, in_band_def.lifetime.span, E0688,
+                         "cannot mix in-band and explicit lifetime definitions")
+            .span_label(in_band_def.lifetime.span, "in-band lifetime definition here")
+            .span_label(oob_def.lifetime.span, "explicit lifetime definition here")
+            .emit();
+    }
+}
+
 fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) {
     let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) {
         // lifetime/lifetime shadowing is an error
@@ -767,7 +814,7 @@ fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map)
                             match *set {
                                 Set1::Empty => "BaseDefault".to_string(),
                                 Set1::One(Region::Static) => "'static".to_string(),
-                                Set1::One(Region::EarlyBound(i, _)) => {
+                                Set1::One(Region::EarlyBound(i, _, _)) => {
                                     generics.lifetimes[i as usize].lifetime.name.name().to_string()
                                 }
                                 Set1::One(_) => bug!(),
@@ -837,7 +884,8 @@ fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics)
                         def.lifetime.name == name
                     }).map_or(Set1::Many, |(i, def)| {
                         let def_id = hir_map.local_def_id(def.lifetime.id);
-                        Set1::One(Region::EarlyBound(i as u32, def_id))
+                        let origin = LifetimeDefOrigin::from_is_in_band(def.in_band);
+                        Set1::One(Region::EarlyBound(i as u32, def_id, origin))
                     })
                 }
             }
@@ -868,6 +916,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             map: *map,
             scope: &wrap_scope,
             trait_ref_hack: self.trait_ref_hack,
+            is_in_fn_syntax: self.is_in_fn_syntax,
             labels_in_fn,
             xcrate_object_lifetime_defaults,
         };
@@ -1020,6 +1069,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     _ => {}
                 }
             }
+
+            // Check for fn-syntax conflicts with in-band lifetime definitions
+            if self.is_in_fn_syntax {
+                match def {
+                    Region::EarlyBound(_, _, LifetimeDefOrigin::InBand) |
+                    Region::LateBound(_, _, LifetimeDefOrigin::InBand) => {
+                        struct_span_err!(self.sess, lifetime_ref.span, E0687,
+                            "lifetimes used in `fn` or `Fn` syntax must be \
+                            explicitly declared using `<...>` binders")
+                            .span_label(lifetime_ref.span,
+                                        "in-band lifetime definition")
+                            .emit();
+                    },
+
+                    Region::Static |
+                    Region::EarlyBound(_, _, LifetimeDefOrigin::Explicit) |
+                    Region::LateBound(_, _, LifetimeDefOrigin::Explicit) |
+                    Region::LateBoundAnon(..) |
+                    Region::Free(..) => {}
+                }
+            }
+
             self.insert_lifetime(lifetime_ref, def);
         } else {
             struct_span_err!(self.sess, lifetime_ref.span, E0261,
@@ -1033,8 +1104,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                                 def: Def,
                                 depth: usize,
                                 params: &'tcx hir::PathParameters) {
+
         if params.parenthesized {
+            let was_in_fn_syntax = self.is_in_fn_syntax;
+            self.is_in_fn_syntax = true;
             self.visit_fn_like_elision(params.inputs(), Some(&params.bindings[0].ty));
+            self.is_in_fn_syntax = was_in_fn_syntax;
             return;
         }
 
@@ -1355,7 +1430,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
                 if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.id) {
                     match lifetime {
-                        Region::LateBound(debruijn, _) |
+                        Region::LateBound(debruijn, _, _) |
                         Region::LateBoundAnon(debruijn, _)
                                 if debruijn.depth < self.binder_depth => {
                             self.have_bound_regions = true;
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 2405461741d..8b849a9e52f 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -110,7 +110,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                 tcx.types.re_static
             }
 
-            Some(rl::Region::LateBound(debruijn, id)) => {
+            Some(rl::Region::LateBound(debruijn, id, _)) => {
                 let name = lifetime_name(id);
                 tcx.mk_region(ty::ReLateBound(debruijn,
                     ty::BrNamed(id, name)))
@@ -120,7 +120,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
                 tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index)))
             }
 
-            Some(rl::Region::EarlyBound(index, id)) => {
+            Some(rl::Region::EarlyBound(index, id, _)) => {
                 let name = lifetime_name(id);
                 tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
                     def_id: id,
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 4c6bad61b19..7de29868d43 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -784,7 +784,7 @@ fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             let hir_id = self.tcx.hir.node_to_hir_id(lt.id);
             match self.tcx.named_region(hir_id) {
                 Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {}
-                Some(rl::Region::LateBound(debruijn, _)) |
+                Some(rl::Region::LateBound(debruijn, _, _)) |
                 Some(rl::Region::LateBoundAnon(debruijn, _))
                     if debruijn.depth < self.binder_depth => {}
                 _ => self.has_late_bound_regions = Some(lt.span),
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 42d7a31713c..e20cae036a3 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1008,8 +1008,8 @@ impl Clean<Lifetime> for hir::Lifetime {
         let hir_id = cx.tcx.hir.node_to_hir_id(self.id);
         let def = cx.tcx.named_region(hir_id);
         match def {
-            Some(rl::Region::EarlyBound(_, node_id)) |
-            Some(rl::Region::LateBound(_, node_id)) |
+            Some(rl::Region::EarlyBound(_, node_id, _)) |
+            Some(rl::Region::LateBound(_, node_id, _)) |
             Some(rl::Region::Free(_, node_id)) => {
                 if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() {
                     return lt;
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 383fe0092be..89d1a3699e8 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -425,6 +425,9 @@ declare_features! (
 
     // `crate` in paths
     (active, crate_in_paths, "1.23.0", Some(45477)),
+
+    // In-band lifetime bindings (e.g. `fn foo(x: &'a u8) -> &'a u8`)
+    (active, in_band_lifetimes, "1.23.0", Some(44524)),
 );
 
 declare_features! (
diff --git a/src/test/compile-fail/feature-gate-in_band_lifetimes.rs b/src/test/compile-fail/feature-gate-in_band_lifetimes.rs
new file mode 100644
index 00000000000..ae1f81c2f57
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-in_band_lifetimes.rs
@@ -0,0 +1,72 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+
+fn foo(x: &'x u8) -> &'x u8 { x }
+//~^ ERROR use of undeclared lifetime name
+//~^^ ERROR use of undeclared lifetime name
+
+struct X<'a>(&'a u8);
+
+impl<'a> X<'a> {
+    fn inner(&self) -> &'a u8 {
+        self.0
+    }
+}
+
+impl<'a> X<'b> {
+//~^ ERROR use of undeclared lifetime name
+    fn inner_2(&self) -> &'b u8 {
+    //~^ ERROR use of undeclared lifetime name
+        self.0
+    }
+}
+
+impl X<'b> {
+//~^ ERROR use of undeclared lifetime name
+    fn inner_3(&self) -> &'b u8 {
+    //~^ ERROR use of undeclared lifetime name
+        self.0
+    }
+}
+
+struct Y<T>(T);
+
+impl Y<&'a u8> {
+    //~^ ERROR use of undeclared lifetime name
+    fn inner(&self) -> &'a u8 {
+    //~^ ERROR use of undeclared lifetime name
+        self.0
+    }
+}
+
+trait MyTrait<'a> {
+    fn my_lifetime(&self) -> &'a u8;
+    fn any_lifetime() -> &'b u8;
+    //~^ ERROR use of undeclared lifetime name
+    fn borrowed_lifetime(&'b self) -> &'b u8;
+    //~^ ERROR use of undeclared lifetime name
+    //~^^ ERROR use of undeclared lifetime name
+}
+
+impl MyTrait<'a> for Y<&'a u8> {
+//~^ ERROR use of undeclared lifetime name
+//~^^ ERROR use of undeclared lifetime name
+    fn my_lifetime(&self) -> &'a u8 { self.0 }
+    //~^ ERROR use of undeclared lifetime name
+    fn any_lifetime() -> &'b u8 { &0 }
+    //~^ ERROR use of undeclared lifetime name
+    fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 }
+    //~^ ERROR use of undeclared lifetime name
+    //~^^ ERROR use of undeclared lifetime name
+}
+
+fn main() {}
diff --git a/src/test/run-pass/in-band-lifetimes.rs b/src/test/run-pass/in-band-lifetimes.rs
new file mode 100644
index 00000000000..d7a837f23b1
--- /dev/null
+++ b/src/test/run-pass/in-band-lifetimes.rs
@@ -0,0 +1,84 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes, universal_impl_trait)]
+
+fn foo(x: &'x u8) -> &'x u8 { x }
+fn foo2(x: &'a u8, y: &u8) -> &'a u8 { x }
+
+fn check_in_band_can_be_late_bound() {
+    let _: for<'x> fn(&'x u8, &u8) -> &'x u8 = foo2;
+}
+
+struct ForInherentNoParams;
+
+impl ForInherentNoParams {
+    fn foo(x: &'a u32, y: &u32) -> &'a u32 { x }
+}
+
+struct X<'a>(&'a u8);
+
+impl<'a> X<'a> {
+    fn inner(&self) -> &'a u8 {
+        self.0
+    }
+
+    fn same_lifetime_as_parameter(&mut self, x: &'a u8) {
+        self.0 = x;
+    }
+}
+
+impl X<'b> {
+    fn inner_2(&self) -> &'b u8 {
+        self.0
+    }
+
+    fn reference_already_introduced_in_band_from_method_with_explicit_binders<'a>(
+        &'b self, x: &'a u32
+    ) {}
+}
+
+struct Y<T>(T);
+
+impl Y<&'a u8> {
+    fn inner(&self) -> &'a u8 {
+        self.0
+    }
+}
+
+trait MyTrait<'a> {
+    fn my_lifetime(&self) -> &'a u8;
+    fn any_lifetime() -> &'b u8;
+    fn borrowed_lifetime(&'b self) -> &'b u8;
+    fn default_impl(&self, x: &'b u32, y: &u32) -> &'b u32 { x }
+    fn in_band_def_explicit_impl(&self, x: &'b u8);
+}
+
+impl MyTrait<'a> for Y<&'a u8> {
+    fn my_lifetime(&self) -> &'a u8 { self.0 }
+    fn any_lifetime() -> &'b u8 { &0 }
+    fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 }
+    fn in_band_def_explicit_impl<'b>(&self, x: &'b u8) {}
+}
+
+fn test_hrtb_defined_lifetime_where<F>(_: F) where for<'a> F: Fn(&'a u8) {}
+fn test_hrtb_defined_lifetime_polytraitref<F>(_: F) where F: for<'a> Fn(&'a u8) {}
+
+fn reference_in_band_from_locals(x: &'test u32) -> &'test u32 {
+    let y: &'test u32 = x;
+    y
+}
+
+fn in_generics_in_band<T: MyTrait<'a>>(x: &T) {}
+fn where_clause_in_band<T>(x: &T) where T: MyTrait<'a> {}
+fn impl_trait_in_band(x: &impl MyTrait<'a>) {}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/E0687.rs b/src/test/ui/in-band-lifetimes/E0687.rs
new file mode 100644
index 00000000000..57b355cbb6a
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/E0687.rs
@@ -0,0 +1,26 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+fn foo(x: fn(&'a u32)) {}
+
+fn bar(x: &Fn(&'a u32)) {}
+
+fn baz(x: fn(&'a u32), y: &'a u32) {}
+
+struct Foo<'a> { x: &'a u32 }
+
+impl Foo<'a> {
+    fn bar(&self, x: fn(&'a u32)) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/E0687.stderr b/src/test/ui/in-band-lifetimes/E0687.stderr
new file mode 100644
index 00000000000..14b513fc52a
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/E0687.stderr
@@ -0,0 +1,26 @@
+error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders
+  --> $DIR/E0687.rs:14:15
+   |
+14 | fn foo(x: fn(&'a u32)) {}
+   |               ^^ in-band lifetime definition
+
+error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders
+  --> $DIR/E0687.rs:16:16
+   |
+16 | fn bar(x: &Fn(&'a u32)) {}
+   |                ^^ in-band lifetime definition
+
+error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders
+  --> $DIR/E0687.rs:18:15
+   |
+18 | fn baz(x: fn(&'a u32), y: &'a u32) {}
+   |               ^^ in-band lifetime definition
+
+error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders
+  --> $DIR/E0687.rs:23:26
+   |
+23 |     fn bar(&self, x: fn(&'a u32)) {}
+   |                          ^^ in-band lifetime definition
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/in-band-lifetimes/E0687_where.rs b/src/test/ui/in-band-lifetimes/E0687_where.rs
new file mode 100644
index 00000000000..6f04720dd4a
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/E0687_where.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes, universal_impl_trait)]
+
+fn bar<F>(x: &F) where F: Fn(&'a u32) {}
+
+fn baz(x: &impl Fn(&'a u32)) {}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/E0687_where.stderr b/src/test/ui/in-band-lifetimes/E0687_where.stderr
new file mode 100644
index 00000000000..db50076fe7c
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/E0687_where.stderr
@@ -0,0 +1,14 @@
+error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders
+  --> $DIR/E0687_where.rs:14:31
+   |
+14 | fn bar<F>(x: &F) where F: Fn(&'a u32) {}
+   |                               ^^ in-band lifetime definition
+
+error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders
+  --> $DIR/E0687_where.rs:16:21
+   |
+16 | fn baz(x: &impl Fn(&'a u32)) {}
+   |                     ^^ in-band lifetime definition
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/in-band-lifetimes/E0688.rs b/src/test/ui/in-band-lifetimes/E0688.rs
new file mode 100644
index 00000000000..e6fe4e138e4
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/E0688.rs
@@ -0,0 +1,26 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+fn foo<'a>(x: &'a u32, y: &'b u32) {}
+
+struct Foo<'a> { x: &'a u32 }
+
+impl Foo<'a> {
+    fn bar<'b>(x: &'a u32, y: &'b u32, z: &'c u32) {}
+}
+
+impl<'b> Foo<'a> {
+    fn baz() {}
+}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/E0688.stderr b/src/test/ui/in-band-lifetimes/E0688.stderr
new file mode 100644
index 00000000000..1289e7fd010
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/E0688.stderr
@@ -0,0 +1,26 @@
+error[E0688]: cannot mix in-band and explicit lifetime definitions
+  --> $DIR/E0688.rs:14:28
+   |
+14 | fn foo<'a>(x: &'a u32, y: &'b u32) {}
+   |        --                  ^^ in-band lifetime definition here
+   |        |
+   |        explicit lifetime definition here
+
+error[E0688]: cannot mix in-band and explicit lifetime definitions
+  --> $DIR/E0688.rs:19:44
+   |
+19 |     fn bar<'b>(x: &'a u32, y: &'b u32, z: &'c u32) {}
+   |            --                              ^^ in-band lifetime definition here
+   |            |
+   |            explicit lifetime definition here
+
+error[E0688]: cannot mix in-band and explicit lifetime definitions
+  --> $DIR/E0688.rs:22:14
+   |
+22 | impl<'b> Foo<'a> {
+   |      --      ^^ in-band lifetime definition here
+   |      |
+   |      explicit lifetime definition here
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/in-band-lifetimes/mismatched.rs b/src/test/ui/in-band-lifetimes/mismatched.rs
new file mode 100644
index 00000000000..6b0a3fedfc5
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mismatched.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+fn foo(x: &'a u32, y: &u32) -> &'a u32 { y }
+
+fn foo2(x: &'a u32, y: &'b u32) -> &'a u32 { y }
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/mismatched.stderr b/src/test/ui/in-band-lifetimes/mismatched.stderr
new file mode 100644
index 00000000000..bbc25869f46
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mismatched.stderr
@@ -0,0 +1,18 @@
+error[E0621]: explicit lifetime required in the type of `y`
+  --> $DIR/mismatched.rs:14:42
+   |
+14 | fn foo(x: &'a u32, y: &u32) -> &'a u32 { y }
+   |                    -                     ^ lifetime `'a` required
+   |                    |
+   |                    consider changing the type of `y` to `&'a u32`
+
+error[E0623]: lifetime mismatch
+  --> $DIR/mismatched.rs:16:46
+   |
+16 | fn foo2(x: &'a u32, y: &'b u32) -> &'a u32 { y }
+   |                        -------     -------   ^ ...but data from `y` is returned here
+   |                        |
+   |                        this parameter and the return type are declared with different lifetimes...
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait.rs b/src/test/ui/in-band-lifetimes/mismatched_trait.rs
new file mode 100644
index 00000000000..2e316770404
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mismatched_trait.rs
@@ -0,0 +1,20 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+trait Get {
+    fn baz(&self, x: &'a u32, y: &u32) -> &'a u32 {
+        y
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait.stderr
new file mode 100644
index 00000000000..315e881bf07
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mismatched_trait.stderr
@@ -0,0 +1,10 @@
+error[E0621]: explicit lifetime required in the type of `y`
+  --> $DIR/mismatched_trait.rs:16:9
+   |
+15 |     fn baz(&self, x: &'a u32, y: &u32) -> &'a u32 {
+   |                               - consider changing the type of `y` to `&'a u32`
+16 |         y
+   |         ^ lifetime `'a` required
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs
new file mode 100644
index 00000000000..1be32e5c51e
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs
@@ -0,0 +1,24 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+trait Get {
+    fn foo(&self, x: &'a u32, y: &u32) -> &'a u32;
+}
+
+impl Get for i32 {
+    fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 {
+        x
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr
new file mode 100644
index 00000000000..d1f3ae450f8
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr
@@ -0,0 +1,39 @@
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'a in generic type due to conflicting requirements
+  --> $DIR/mismatched_trait_impl.rs:19:5
+   |
+19 | /     fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 {
+20 | |         x
+21 | |     }
+   | |_____^
+   |
+note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the method body at 19:5...
+  --> $DIR/mismatched_trait_impl.rs:19:5
+   |
+19 | /     fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 {
+20 | |         x
+21 | |     }
+   | |_____^
+note: ...so that method type is compatible with trait (expected fn(&i32, &'a u32, &u32) -> &'a u32, found fn(&i32, &u32, &u32) -> &u32)
+  --> $DIR/mismatched_trait_impl.rs:19:5
+   |
+19 | /     fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 {
+20 | |         x
+21 | |     }
+   | |_____^
+note: but, the lifetime must be valid for the lifetime 'a as defined on the method body at 19:5...
+  --> $DIR/mismatched_trait_impl.rs:19:5
+   |
+19 | /     fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 {
+20 | |         x
+21 | |     }
+   | |_____^
+note: ...so that method type is compatible with trait (expected fn(&i32, &'a u32, &u32) -> &'a u32, found fn(&i32, &u32, &u32) -> &u32)
+  --> $DIR/mismatched_trait_impl.rs:19:5
+   |
+19 | /     fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 {
+20 | |         x
+21 | |     }
+   | |_____^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/in-band-lifetimes/mut_while_borrow.rs b/src/test/ui/in-band-lifetimes/mut_while_borrow.rs
new file mode 100644
index 00000000000..fabf531968a
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mut_while_borrow.rs
@@ -0,0 +1,21 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+fn foo(x: &'a u32) -> &'a u32 { x }
+
+fn main() {
+    let mut p = 3;
+    let r = foo(&p);
+    p += 1;
+    println!("{}", r);
+}
diff --git a/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr b/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr
new file mode 100644
index 00000000000..4800e39269f
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr
@@ -0,0 +1,10 @@
+error[E0506]: cannot assign to `p` because it is borrowed
+  --> $DIR/mut_while_borrow.rs:19:5
+   |
+18 |     let r = foo(&p);
+   |                  - borrow of `p` occurs here
+19 |     p += 1;
+   |     ^^^^^^ assignment to borrowed `p` occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs
new file mode 100644
index 00000000000..b421e16092c
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs
@@ -0,0 +1,22 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+struct Foo {
+    x: &'test u32,
+}
+
+enum Bar {
+    Baz(&'test u32),
+}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr
new file mode 100644
index 00000000000..7ffbfbd89b6
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr
@@ -0,0 +1,14 @@
+error[E0261]: use of undeclared lifetime name `'test`
+  --> $DIR/no_in_band_in_struct.rs:15:9
+   |
+15 |     x: &'test u32,
+   |         ^^^^^ undeclared lifetime
+
+error[E0261]: use of undeclared lifetime name `'test`
+  --> $DIR/no_in_band_in_struct.rs:19:10
+   |
+19 |     Baz(&'test u32),
+   |          ^^^^^ undeclared lifetime
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs
new file mode 100644
index 00000000000..a7c9d4ece95
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs
@@ -0,0 +1,23 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+fn foo(x: &u32) {
+    let y: &'test u32 = x;
+}
+
+fn foo2(x: &u32) {}
+fn bar() {
+    let y: fn(&'test u32) = foo2;
+}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr
new file mode 100644
index 00000000000..a0c2e819969
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr
@@ -0,0 +1,14 @@
+error[E0261]: use of undeclared lifetime name `'test`
+  --> $DIR/no_introducing_in_band_in_locals.rs:15:13
+   |
+15 |     let y: &'test u32 = x;
+   |             ^^^^^ undeclared lifetime
+
+error[E0261]: use of undeclared lifetime name `'test`
+  --> $DIR/no_introducing_in_band_in_locals.rs:20:16
+   |
+20 |     let y: fn(&'test u32) = foo2;
+   |                ^^^^^ undeclared lifetime
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/in-band-lifetimes/shadow.rs b/src/test/ui/in-band-lifetimes/shadow.rs
new file mode 100644
index 00000000000..a9053d09f6c
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/shadow.rs
@@ -0,0 +1,21 @@
+// Copyright 2017 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.
+
+#![allow(warnings)]
+#![feature(in_band_lifetimes)]
+
+struct Foo<T>(T);
+
+impl Foo<&'s u8> {
+    fn bar<'s>(&self, x: &'s u8) {}
+    fn baz(x: for<'s> fn(&'s u32)) {}
+}
+
+fn main() {}
diff --git a/src/test/ui/in-band-lifetimes/shadow.stderr b/src/test/ui/in-band-lifetimes/shadow.stderr
new file mode 100644
index 00000000000..f05b76fbea4
--- /dev/null
+++ b/src/test/ui/in-band-lifetimes/shadow.stderr
@@ -0,0 +1,19 @@
+error[E0496]: lifetime name `'s` shadows a lifetime name that is already in scope
+  --> $DIR/shadow.rs:17:12
+   |
+16 | impl Foo<&'s u8> {
+   |           -- first declared here
+17 |     fn bar<'s>(&self, x: &'s u8) {}
+   |            ^^ lifetime 's already in scope
+
+error[E0496]: lifetime name `'s` shadows a lifetime name that is already in scope
+  --> $DIR/shadow.rs:18:19
+   |
+16 | impl Foo<&'s u8> {
+   |           -- first declared here
+17 |     fn bar<'s>(&self, x: &'s u8) {}
+18 |     fn baz(x: for<'s> fn(&'s u32)) {}
+   |                   ^^ lifetime 's already in scope
+
+error: aborting due to 2 previous errors
+