diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 942f1cbd6651b..95b73d5f87b39 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -436,7 +436,9 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
             visitor.visit_ident(ident);
         }
         LifetimeName::Param(ParamName::Fresh(_)) |
+        LifetimeName::Param(ParamName::Error) |
         LifetimeName::Static |
+        LifetimeName::Error |
         LifetimeName::Implicit |
         LifetimeName::Underscore => {}
     }
@@ -747,7 +749,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi
     walk_list!(visitor, visit_attribute, &param.attrs);
     match param.name {
         ParamName::Plain(ident) => visitor.visit_ident(ident),
-        ParamName::Fresh(_) => {}
+        ParamName::Error | ParamName::Fresh(_) => {}
     }
     match param.kind {
         GenericParamKind::Lifetime { .. } => {}
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 7309358091056..1fc677b0e2926 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -316,6 +316,11 @@ enum AnonymousLifetimeMode {
     /// For **Deprecated** cases, report an error.
     CreateParameter,
 
+    /// Give a hard error when either `&` or `'_` is written. Used to
+    /// rule out things like `where T: Foo<'_>`. Does not imply an
+    /// error on default object bounds (e.g., `Box<dyn Foo>`).
+    ReportError,
+
     /// Pass responsibility to `resolve_lifetime` code for all cases.
     PassThrough,
 }
@@ -736,6 +741,10 @@ impl<'a> LoweringContext<'a> {
                         keywords::UnderscoreLifetime.name().as_interned_str(),
                         hir::LifetimeParamKind::Elided,
                     ),
+                    ParamName::Error => (
+                        keywords::UnderscoreLifetime.name().as_interned_str(),
+                        hir::LifetimeParamKind::Error,
+                    ),
                 };
 
                 // Add a definition for the in-band lifetime def
@@ -792,7 +801,7 @@ impl<'a> LoweringContext<'a> {
     }
 
     /// When we have either an elided or `'_` lifetime in an impl
-    /// header, we convert it to
+    /// header, we convert it to an in-band lifetime.
     fn collect_fresh_in_band_lifetime(&mut self, span: Span) -> ParamName {
         assert!(self.is_collecting_in_band_lifetimes);
         let index = self.lifetimes_to_define.len();
@@ -1475,7 +1484,7 @@ impl<'a> LoweringContext<'a> {
                         }
                     }
                     hir::LifetimeName::Param(_) => lifetime.name,
-                    hir::LifetimeName::Static => return,
+                    hir::LifetimeName::Error | hir::LifetimeName::Static => return,
                 };
 
                 if !self.currently_bound_lifetimes.contains(&name)
@@ -2163,7 +2172,7 @@ impl<'a> LoweringContext<'a> {
                         }
                     }
                     hir::LifetimeName::Param(_) => lifetime.name,
-                    hir::LifetimeName::Static => return,
+                    hir::LifetimeName::Error | hir::LifetimeName::Static => return,
                 };
 
                 if !self.currently_bound_lifetimes.contains(&name) {
@@ -2294,10 +2303,12 @@ impl<'a> LoweringContext<'a> {
         itctx: ImplTraitContext<'_>,
     ) -> hir::GenericBound {
         match *tpb {
-            GenericBound::Trait(ref ty, modifier) => hir::GenericBound::Trait(
-                self.lower_poly_trait_ref(ty, itctx),
-                self.lower_trait_bound_modifier(modifier),
-            ),
+            GenericBound::Trait(ref ty, modifier) => {
+                hir::GenericBound::Trait(
+                    self.lower_poly_trait_ref(ty, itctx),
+                    self.lower_trait_bound_modifier(modifier),
+                )
+            }
             GenericBound::Outlives(ref lifetime) => {
                 hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
             }
@@ -2319,6 +2330,8 @@ impl<'a> LoweringContext<'a> {
                     AnonymousLifetimeMode::PassThrough => {
                         self.new_named_lifetime(l.id, span, hir::LifetimeName::Underscore)
                     }
+
+                    AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span),
                 },
             ident => {
                 self.maybe_collect_in_band_lifetime(ident);
@@ -2357,16 +2370,26 @@ impl<'a> LoweringContext<'a> {
                            add_bounds: &NodeMap<Vec<GenericBound>>,
                            mut itctx: ImplTraitContext<'_>)
                            -> hir::GenericParam {
-        let mut bounds = self.lower_param_bounds(&param.bounds, itctx.reborrow());
+        let mut bounds = self.with_anonymous_lifetime_mode(
+            AnonymousLifetimeMode::ReportError,
+            |this| this.lower_param_bounds(&param.bounds, itctx.reborrow()),
+        );
+
         match param.kind {
             GenericParamKind::Lifetime => {
                 let was_collecting_in_band = self.is_collecting_in_band_lifetimes;
                 self.is_collecting_in_band_lifetimes = false;
 
-                let lt = self.lower_lifetime(&Lifetime { id: param.id, ident: param.ident });
+                let lt = self.with_anonymous_lifetime_mode(
+                    AnonymousLifetimeMode::ReportError,
+                    |this| this.lower_lifetime(&Lifetime { id: param.id, ident: param.ident }),
+                );
                 let param_name = match lt.name {
                     hir::LifetimeName::Param(param_name) => param_name,
-                    _ => hir::ParamName::Plain(lt.name.ident()),
+                    hir::LifetimeName::Implicit
+                        | hir::LifetimeName::Underscore
+                        | hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
+                    hir::LifetimeName::Error => ParamName::Error,
                 };
                 let param = hir::GenericParam {
                     id: lt.id,
@@ -2490,13 +2513,18 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause {
-        hir::WhereClause {
-            id: self.lower_node_id(wc.id).node_id,
-            predicates: wc.predicates
-                .iter()
-                .map(|predicate| self.lower_where_predicate(predicate))
-                .collect(),
-        }
+        self.with_anonymous_lifetime_mode(
+            AnonymousLifetimeMode::ReportError,
+            |this| {
+                hir::WhereClause {
+                    id: this.lower_node_id(wc.id).node_id,
+                    predicates: wc.predicates
+                        .iter()
+                        .map(|predicate| this.lower_where_predicate(predicate))
+                        .collect(),
+                }
+            },
+        )
     }
 
     fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate {
@@ -4839,10 +4867,38 @@ impl<'a> LoweringContext<'a> {
                 }
             }
 
+            AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
+
             AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
         }
     }
 
+    /// Report an error on illegal use of `'_` or a `&T` with no explicit lifetime;
+    /// return a "error lifetime".
+    fn new_error_lifetime(&mut self, id: Option<NodeId>, span: Span) -> hir::Lifetime {
+        let (id, msg, label) = match id {
+            Some(id) => (id, "`'_` cannot be used here", "`'_` is a reserved lifetime name"),
+
+            None => (
+                self.next_id().node_id,
+                "`&` without an explicit lifetime name cannot be used here",
+                "explicit lifetime name needed here",
+            ),
+        };
+
+        let mut err = struct_span_err!(
+            self.sess,
+            span,
+            E0637,
+            "{}",
+            msg,
+        );
+        err.span_label(span, label);
+        err.emit();
+
+        self.new_named_lifetime(id, span, hir::LifetimeName::Error)
+    }
+
     /// Invoked to create the lifetime argument(s) for a path like
     /// `std::cell::Ref<T>`; note that implicit lifetimes in these
     /// sorts of cases are deprecated. This may therefore report a warning or an
@@ -4857,6 +4913,12 @@ impl<'a> LoweringContext<'a> {
             //     impl Foo for std::cell::Ref<u32> // note lack of '_
             AnonymousLifetimeMode::CreateParameter => {}
 
+            AnonymousLifetimeMode::ReportError => {
+                return (0..count)
+                    .map(|_| self.new_error_lifetime(None, span))
+                    .collect();
+            }
+
             // This is the normal case.
             AnonymousLifetimeMode::PassThrough => {}
         }
@@ -4887,6 +4949,10 @@ impl<'a> LoweringContext<'a> {
             // `resolve_lifetime` has the code to make that happen.
             AnonymousLifetimeMode::CreateParameter => {}
 
+            AnonymousLifetimeMode::ReportError => {
+                // ReportError applies to explicit use of `'_`.
+            }
+
             // This is the normal case.
             AnonymousLifetimeMode::PassThrough => {}
         }
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index c57c26434e32a..01b68ae669b67 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -208,13 +208,18 @@ pub enum ParamName {
     /// where `'f` is something like `Fresh(0)`. The indices are
     /// unique per impl, but not necessarily continuous.
     Fresh(usize),
+
+    /// Indicates an illegal name was given and an error has been
+    /// repored (so we should squelch other derived errors). Occurs
+    /// when e.g. `'_` is used in the wrong place.
+    Error,
 }
 
 impl ParamName {
     pub fn ident(&self) -> Ident {
         match *self {
             ParamName::Plain(ident) => ident,
-            ParamName::Fresh(_) => keywords::UnderscoreLifetime.ident(),
+            ParamName::Error | ParamName::Fresh(_) => keywords::UnderscoreLifetime.ident(),
         }
     }
 
@@ -234,6 +239,10 @@ pub enum LifetimeName {
     /// User typed nothing. e.g. the lifetime in `&u32`.
     Implicit,
 
+    /// Indicates an error during lowering (usually `'_` in wrong place)
+    /// that was already reported.
+    Error,
+
     /// User typed `'_`.
     Underscore,
 
@@ -245,6 +254,7 @@ impl LifetimeName {
     pub fn ident(&self) -> Ident {
         match *self {
             LifetimeName::Implicit => keywords::Invalid.ident(),
+            LifetimeName::Error => keywords::Invalid.ident(),
             LifetimeName::Underscore => keywords::UnderscoreLifetime.ident(),
             LifetimeName::Static => keywords::StaticLifetime.ident(),
             LifetimeName::Param(param_name) => param_name.ident(),
@@ -260,7 +270,7 @@ impl LifetimeName {
             // in the compiler is concerned -- `Fresh(_)` variants act
             // equivalently to "some fresh name". They correspond to
             // early-bound regions on an impl, in other words.
-            LifetimeName::Param(_) | LifetimeName::Static => false,
+            LifetimeName::Error | LifetimeName::Param(_) | LifetimeName::Static => false,
         }
     }
 
@@ -513,6 +523,9 @@ pub enum LifetimeParamKind {
     // Indication that the lifetime was elided like both cases here:
     // `fn foo(x: &u8) -> &'_ u8 { x }`
     Elided,
+
+    // Indication that the lifetime name was somehow in error.
+    Error,
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index da9604702dfa3..fe663c68cd50b 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -144,7 +144,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for hir::ImplItemId {
 
 impl_stable_hash_for!(enum hir::ParamName {
     Plain(name),
-    Fresh(index)
+    Fresh(index),
+    Error,
 });
 
 impl_stable_hash_for!(enum hir::LifetimeName {
@@ -152,6 +153,7 @@ impl_stable_hash_for!(enum hir::LifetimeName {
     Implicit,
     Underscore,
     Static,
+    Error,
 });
 
 impl_stable_hash_for!(struct hir::Label {
@@ -210,7 +212,8 @@ impl_stable_hash_for!(struct hir::GenericParam {
 impl_stable_hash_for!(enum hir::LifetimeParamKind {
     Explicit,
     InBand,
-    Elided
+    Elided,
+    Error,
 });
 
 impl<'a> HashStable<StableHashingContext<'a>> for hir::GenericParamKind {
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index f51a3e71d0741..0c8224710d396 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -755,8 +755,9 @@ for ::middle::resolve_lifetime::Set1<T>
 }
 
 impl_stable_hash_for!(enum ::middle::resolve_lifetime::LifetimeDefOrigin {
-    Explicit,
-    InBand
+    ExplicitOrElided,
+    InBand,
+    Error,
 });
 
 impl_stable_hash_for!(enum ::middle::resolve_lifetime::Region {
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 2f3fdb7966f25..b22ea3dceb367 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -18,8 +18,8 @@
 use hir::def::Def;
 use hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use hir::map::Map;
-use hir::{GenericArg, GenericParam, ItemLocalId, LifetimeName, ParamName, Node};
-use ty::{self, TyCtxt, DefIdTree, GenericParamDefKind};
+use hir::{GenericArg, GenericParam, ItemLocalId, LifetimeName, Node, ParamName};
+use ty::{self, DefIdTree, GenericParamDefKind, TyCtxt};
 
 use errors::{Applicability, DiagnosticBuilder};
 use rustc::lint;
@@ -43,22 +43,23 @@ use hir::{self, GenericParamKind, LifetimeParamKind};
 /// 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,
+    // Explicit binders like `fn foo<'a>(x: &'a u8)` or elided like `impl Foo<&u32>`
+    ExplicitOrElided,
     // In-band declarations like `fn foo(x: &'a u8)`
     InBand,
+    // Some kind of erroneous origin
+    Error,
 }
 
 impl LifetimeDefOrigin {
     fn from_param(param: &GenericParam) -> Self {
         match param.kind {
-            GenericParamKind::Lifetime { kind } => {
-                if kind == LifetimeParamKind::InBand {
-                    LifetimeDefOrigin::InBand
-                } else {
-                    LifetimeDefOrigin::Explicit
-                }
-            }
+            GenericParamKind::Lifetime { kind } => match kind {
+                LifetimeParamKind::InBand => LifetimeDefOrigin::InBand,
+                LifetimeParamKind::Explicit => LifetimeDefOrigin::ExplicitOrElided,
+                LifetimeParamKind::Elided => LifetimeDefOrigin::ExplicitOrElided,
+                LifetimeParamKind::Error => LifetimeDefOrigin::Error,
+            },
             _ => bug!("expected a lifetime param"),
         }
     }
@@ -104,12 +105,12 @@ impl Region {
         let origin = LifetimeDefOrigin::from_param(param);
         debug!(
             "Region::late: param={:?} depth={:?} def_id={:?} origin={:?}",
-            param,
-            depth,
-            def_id,
-            origin,
+            param, depth, def_id, origin,
         );
-        (param.name.modern(), Region::LateBound(depth, def_id, origin))
+        (
+            param.name.modern(),
+            Region::LateBound(depth, def_id, origin),
+        )
     }
 
     fn late_anon(index: &Cell<u32>) -> Region {
@@ -143,23 +144,24 @@ impl Region {
 
     fn shifted_out_to_binder(self, binder: ty::DebruijnIndex) -> Region {
         match self {
-            Region::LateBound(debruijn, id, origin) => Region::LateBound(
-                debruijn.shifted_out_to_binder(binder),
-                id,
-                origin,
-            ),
-            Region::LateBoundAnon(debruijn, index) => Region::LateBoundAnon(
-                debruijn.shifted_out_to_binder(binder),
-                index,
-            ),
+            Region::LateBound(debruijn, id, origin) => {
+                Region::LateBound(debruijn.shifted_out_to_binder(binder), id, origin)
+            }
+            Region::LateBoundAnon(debruijn, index) => {
+                Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index)
+            }
             _ => self,
         }
     }
 
     fn subst<'a, L>(self, mut params: L, map: &NamedRegionMap) -> Option<Region>
-            where L: Iterator<Item = &'a hir::Lifetime>  {
+    where
+        L: Iterator<Item = &'a hir::Lifetime>,
+    {
         if let Region::EarlyBound(index, _, _) = self {
-            params.nth(index as usize).and_then(|lifetime| map.defs.get(&lifetime.id).cloned())
+            params
+                .nth(index as usize)
+                .and_then(|lifetime| map.defs.get(&lifetime.id).cloned())
         } else {
             Some(self)
         }
@@ -361,17 +363,17 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
         is_late_bound_map: |tcx, id| {
             let id = LocalDefId::from_def_id(DefId::local(id)); // (*)
             tcx.resolve_lifetimes(LOCAL_CRATE)
-               .late_bound
-               .get(&id)
-               .cloned()
+                .late_bound
+                .get(&id)
+                .cloned()
         },
 
         object_lifetime_defaults_map: |tcx, id| {
             let id = LocalDefId::from_def_id(DefId::local(id)); // (*)
             tcx.resolve_lifetimes(LOCAL_CRATE)
-               .object_lifetime_defaults
-               .get(&id)
-               .cloned()
+                .object_lifetime_defaults
+                .get(&id)
+                .cloned()
         },
 
         ..*providers
@@ -405,14 +407,16 @@ fn resolve_lifetimes<'tcx>(
     }
     for k in named_region_map.late_bound {
         let hir_id = tcx.hir.node_to_hir_id(k);
-        let map = rl.late_bound.entry(hir_id.owner_local_def_id()).or_default();
+        let map = rl.late_bound
+            .entry(hir_id.owner_local_def_id())
+            .or_default();
         Lrc::get_mut(map).unwrap().insert(hir_id.local_id);
     }
     for (k, v) in named_region_map.object_lifetime_defaults {
         let hir_id = tcx.hir.node_to_hir_id(k);
         let map = rl.object_lifetime_defaults
-                    .entry(hir_id.owner_local_def_id())
-                    .or_default();
+            .entry(hir_id.owner_local_def_id())
+            .or_default();
         Lrc::get_mut(map)
             .unwrap()
             .insert(hir_id.local_id, Lrc::new(v));
@@ -495,13 +499,20 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 };
                 self.with(scope, |_, this| intravisit::walk_item(this, item));
             }
-            hir::ItemKind::Existential(hir::ExistTy { impl_trait_fn: Some(_), .. }) => {
+            hir::ItemKind::Existential(hir::ExistTy {
+                impl_trait_fn: Some(_),
+                ..
+            }) => {
                 // currently existential type declarations are just generated from impl Trait
                 // items. doing anything on this node is irrelevant, as we currently don't need
                 // it.
             }
             hir::ItemKind::Ty(_, ref generics)
-            | hir::ItemKind::Existential(hir::ExistTy { impl_trait_fn: None, ref generics, .. })
+            | hir::ItemKind::Existential(hir::ExistTy {
+                impl_trait_fn: None,
+                ref generics,
+                ..
+            })
             | hir::ItemKind::Enum(_, ref generics)
             | hir::ItemKind::Struct(_, ref generics)
             | hir::ItemKind::Union(_, ref generics)
@@ -521,15 +532,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     0
                 };
                 let mut type_count = 0;
-                let lifetimes = generics.params.iter().filter_map(|param| match param.kind {
-                    GenericParamKind::Lifetime { .. } => {
-                        Some(Region::early(&self.tcx.hir, &mut index, param))
-                    }
-                    GenericParamKind::Type { .. } => {
-                        type_count += 1;
-                        None
-                    }
-                }).collect();
+                let lifetimes = generics
+                    .params
+                    .iter()
+                    .filter_map(|param| match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            Some(Region::early(&self.tcx.hir, &mut index, param))
+                        }
+                        GenericParamKind::Type { .. } => {
+                            type_count += 1;
+                            None
+                        }
+                    })
+                    .collect();
                 let scope = Scope::Binder {
                     lifetimes,
                     next_early_index: index + type_count,
@@ -569,12 +584,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let was_in_fn_syntax = self.is_in_fn_syntax;
                 self.is_in_fn_syntax = true;
                 let scope = Scope::Binder {
-                    lifetimes: c.generic_params.iter().filter_map(|param| match param.kind {
-                        GenericParamKind::Lifetime { .. } => {
-                            Some(Region::late(&self.tcx.hir, param))
-                        }
-                        _ => None,
-                    }).collect(),
+                    lifetimes: c.generic_params
+                        .iter()
+                        .filter_map(|param| match param.kind {
+                            GenericParamKind::Lifetime { .. } => {
+                                Some(Region::late(&self.tcx.hir, param))
+                            }
+                            _ => None,
+                        })
+                        .collect(),
                     s: self.scope,
                     next_early_index,
                     track_lifetime_uses: true,
@@ -612,6 +630,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         // If the user wrote an explicit name, use that.
                         self.visit_lifetime(lifetime);
                     }
+                    LifetimeName::Error => {}
                 }
             }
             hir::TyKind::Rptr(ref lifetime_ref, ref mt) => {
@@ -631,19 +650,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let (generics, bounds) = match self.tcx.hir.expect_item(item_id.id).node {
                     // named existential types are reached via TyKind::Path
                     // this arm is for `impl Trait` in the types of statics, constants and locals
-                    hir::ItemKind::Existential(hir::ExistTy{ impl_trait_fn: None, .. }) => {
+                    hir::ItemKind::Existential(hir::ExistTy {
+                        impl_trait_fn: None,
+                        ..
+                    }) => {
                         intravisit::walk_ty(self, ty);
                         return;
-                    },
+                    }
                     // RPIT (return position impl trait)
-                    hir::ItemKind::Existential(hir::ExistTy{
+                    hir::ItemKind::Existential(hir::ExistTy {
                         ref generics,
                         ref bounds,
                         ..
-                    }) => (
-                        generics,
-                        bounds,
-                    ),
+                    }) => (generics, bounds),
                     ref i => bug!("impl Trait pointed to non-existential type?? {:#?}", i),
                 };
 
@@ -774,15 +793,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let mut index = self.next_early_index();
                 debug!("visit_ty: index = {}", index);
                 let mut type_count = 0;
-                let lifetimes = generics.params.iter().filter_map(|param| match param.kind {
-                    GenericParamKind::Lifetime { .. } => {
-                        Some(Region::early(&self.tcx.hir, &mut index, param))
-                    }
-                    GenericParamKind::Type { .. } => {
-                        type_count += 1;
-                        None
-                    }
-                }).collect();
+                let lifetimes = generics
+                    .params
+                    .iter()
+                    .filter_map(|param| match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            Some(Region::early(&self.tcx.hir, &mut index, param))
+                        }
+                        GenericParamKind::Type { .. } => {
+                            type_count += 1;
+                            None
+                        }
+                    })
+                    .collect();
                 let scope = Scope::Binder {
                     lifetimes,
                     next_early_index: index + type_count,
@@ -825,15 +848,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let mut index = self.next_early_index();
                 let mut next_early_index = index;
                 debug!("visit_ty: index = {}", index);
-                let lifetimes = generics.params.iter().filter_map(|param| match param.kind {
-                    GenericParamKind::Lifetime { .. } => {
-                        Some(Region::early(&self.tcx.hir, &mut index, param))
-                    }
-                    GenericParamKind::Type { .. } => {
-                        next_early_index += 1;
-                        None
-                    }
-                }).collect();
+                let lifetimes = generics
+                    .params
+                    .iter()
+                    .filter_map(|param| match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            Some(Region::early(&self.tcx.hir, &mut index, param))
+                        }
+                        GenericParamKind::Type { .. } => {
+                            next_early_index += 1;
+                            None
+                        }
+                    })
+                    .collect();
                 let scope = Scope::Binder {
                     lifetimes,
                     next_early_index,
@@ -851,15 +878,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let mut index = self.next_early_index();
                 let mut next_early_index = index;
                 debug!("visit_ty: index = {}", index);
-                let lifetimes = generics.params.iter().filter_map(|param| match param.kind {
-                    GenericParamKind::Lifetime { .. } => {
-                        Some(Region::early(&self.tcx.hir, &mut index, param))
-                    }
-                    GenericParamKind::Type { .. } => {
-                        next_early_index += 1;
-                        None
-                    }
-                }).collect();
+                let lifetimes = generics
+                    .params
+                    .iter()
+                    .filter_map(|param| match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            Some(Region::early(&self.tcx.hir, &mut index, param))
+                        }
+                        GenericParamKind::Type { .. } => {
+                            next_early_index += 1;
+                            None
+                        }
+                    })
+                    .collect();
 
                 let scope = Scope::Binder {
                     lifetimes,
@@ -933,13 +964,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     ref bound_generic_params,
                     ..
                 }) => {
-                    let lifetimes: FxHashMap<_, _> = bound_generic_params.iter()
+                    let lifetimes: FxHashMap<_, _> = bound_generic_params
+                        .iter()
                         .filter_map(|param| match param.kind {
                             GenericParamKind::Lifetime { .. } => {
                                 Some(Region::late(&self.tcx.hir, param))
                             }
                             _ => None,
-                        }).collect();
+                        })
+                        .collect();
                     if !lifetimes.is_empty() {
                         self.trait_ref_hack = true;
                         let next_early_index = self.next_early_index();
@@ -989,15 +1022,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     ) {
         debug!("visit_poly_trait_ref trait_ref={:?}", trait_ref);
 
-        if !self.trait_ref_hack
-            || trait_ref
-                .bound_generic_params
-                .iter()
-                .any(|param| match param.kind {
-                    GenericParamKind::Lifetime { .. } => true,
-                    _ => false,
-                })
-        {
+        if !self.trait_ref_hack || trait_ref.bound_generic_params.iter().any(|param| {
+            match param.kind {
+                GenericParamKind::Lifetime { .. } => true,
+                _ => false,
+            }
+        }) {
             if self.trait_ref_hack {
                 span_err!(
                     self.tcx.sess,
@@ -1008,13 +1038,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             }
             let next_early_index = self.next_early_index();
             let scope = Scope::Binder {
-                lifetimes: trait_ref.bound_generic_params.iter()
+                lifetimes: trait_ref
+                    .bound_generic_params
+                    .iter()
                     .filter_map(|param| match param.kind {
                         GenericParamKind::Lifetime { .. } => {
                             Some(Region::late(&self.tcx.hir, param))
                         }
                         _ => None,
-                    }).collect(),
+                    })
+                    .collect(),
                 s: self.scope,
                 next_early_index,
                 track_lifetime_uses: true,
@@ -1079,29 +1112,30 @@ impl ShadowKind {
     }
 }
 
-fn check_mixed_explicit_and_in_band_defs(
-    tcx: TyCtxt<'_, '_, '_>,
-    params: &P<[hir::GenericParam]>,
-) {
-    let lifetime_params: Vec<_> = params.iter().filter_map(|param| match param.kind {
-        GenericParamKind::Lifetime { kind, .. } => Some((kind, param.span)),
-        _ => None,
-    }).collect();
-    let explicit = lifetime_params.iter().find(|(kind, _)| *kind == LifetimeParamKind::Explicit);
-    let in_band = lifetime_params.iter().find(|(kind, _)| *kind == LifetimeParamKind::InBand);
-
-    if let (Some((_, explicit_span)), Some((_, in_band_span)))
-        = (explicit, in_band) {
+fn check_mixed_explicit_and_in_band_defs(tcx: TyCtxt<'_, '_, '_>, params: &P<[hir::GenericParam]>) {
+    let lifetime_params: Vec<_> = params
+        .iter()
+        .filter_map(|param| match param.kind {
+            GenericParamKind::Lifetime { kind, .. } => Some((kind, param.span)),
+            _ => None,
+        })
+        .collect();
+    let explicit = lifetime_params
+        .iter()
+        .find(|(kind, _)| *kind == LifetimeParamKind::Explicit);
+    let in_band = lifetime_params
+        .iter()
+        .find(|(kind, _)| *kind == LifetimeParamKind::InBand);
+
+    if let (Some((_, explicit_span)), Some((_, in_band_span))) = (explicit, in_band) {
         struct_span_err!(
             tcx.sess,
             *in_band_span,
             E0688,
             "cannot mix in-band and explicit lifetime definitions"
-        ).span_label(
-            *in_band_span,
-            "in-band lifetime definition here",
-        ).span_label(*explicit_span, "explicit lifetime definition here")
-        .emit();
+        ).span_label(*in_band_span, "in-band lifetime definition here")
+            .span_label(*explicit_span, "explicit lifetime definition here")
+            .emit();
     }
 }
 
@@ -1187,8 +1221,9 @@ fn extract_labels(ctxt: &mut LifetimeContext<'_, '_>, body: &hir::Body) {
 
     fn expression_label(ex: &hir::Expr) -> Option<ast::Ident> {
         match ex.node {
-            hir::ExprKind::While(.., Some(label)) |
-            hir::ExprKind::Loop(_, Some(label), _) => Some(label.ident),
+            hir::ExprKind::While(.., Some(label)) | hir::ExprKind::Loop(_, Some(label), _) => {
+                Some(label.ident)
+            }
             _ => None,
         }
     }
@@ -1241,7 +1276,11 @@ fn compute_object_lifetime_defaults(
             hir::ItemKind::Struct(_, ref generics)
             | hir::ItemKind::Union(_, ref generics)
             | hir::ItemKind::Enum(_, ref generics)
-            | hir::ItemKind::Existential(hir::ExistTy { ref generics, impl_trait_fn: None, .. })
+            | hir::ItemKind::Existential(hir::ExistTy {
+                ref generics,
+                impl_trait_fn: None,
+                ..
+            })
             | hir::ItemKind::Ty(_, ref generics)
             | hir::ItemKind::Trait(_, _, ref generics, ..) => {
                 let result = object_lifetime_defaults_for_item(tcx, generics);
@@ -1253,8 +1292,10 @@ fn compute_object_lifetime_defaults(
                         .map(|set| match *set {
                             Set1::Empty => "BaseDefault".into(),
                             Set1::One(Region::Static) => "'static".into(),
-                            Set1::One(Region::EarlyBound(mut i, _, _)) => {
-                                generics.params.iter().find_map(|param| match param.kind {
+                            Set1::One(Region::EarlyBound(mut i, _, _)) => generics
+                                .params
+                                .iter()
+                                .find_map(|param| match param.kind {
                                     GenericParamKind::Lifetime { .. } => {
                                         if i == 0 {
                                             return Some(param.name.ident().to_string().into());
@@ -1263,8 +1304,8 @@ fn compute_object_lifetime_defaults(
                                         None
                                     }
                                     _ => None,
-                                }).unwrap()
-                            }
+                                })
+                                .unwrap(),
                             Set1::One(_) => bug!(),
                             Set1::Many => "Ambiguous".into(),
                         })
@@ -1296,66 +1337,70 @@ fn object_lifetime_defaults_for_item(
         }
     }
 
-    generics.params.iter().filter_map(|param| match param.kind {
-        GenericParamKind::Lifetime { .. } => None,
-        GenericParamKind::Type { .. } => {
-            let mut set = Set1::Empty;
+    generics
+        .params
+        .iter()
+        .filter_map(|param| match param.kind {
+            GenericParamKind::Lifetime { .. } => None,
+            GenericParamKind::Type { .. } => {
+                let mut set = Set1::Empty;
 
-            add_bounds(&mut set, &param.bounds);
+                add_bounds(&mut set, &param.bounds);
 
-            let param_def_id = tcx.hir.local_def_id(param.id);
-            for predicate in &generics.where_clause.predicates {
-                // Look for `type: ...` where clauses.
-                let data = match *predicate {
-                    hir::WherePredicate::BoundPredicate(ref data) => data,
-                    _ => continue,
-                };
+                let param_def_id = tcx.hir.local_def_id(param.id);
+                for predicate in &generics.where_clause.predicates {
+                    // Look for `type: ...` where clauses.
+                    let data = match *predicate {
+                        hir::WherePredicate::BoundPredicate(ref data) => data,
+                        _ => continue,
+                    };
 
-                // Ignore `for<'a> type: ...` as they can change what
-                // lifetimes mean (although we could "just" handle it).
-                if !data.bound_generic_params.is_empty() {
-                    continue;
-                }
+                    // Ignore `for<'a> type: ...` as they can change what
+                    // lifetimes mean (although we could "just" handle it).
+                    if !data.bound_generic_params.is_empty() {
+                        continue;
+                    }
 
-                let def = match data.bounded_ty.node {
-                    hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.def,
-                    _ => continue,
-                };
+                    let def = match data.bounded_ty.node {
+                        hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.def,
+                        _ => continue,
+                    };
 
-                if def == Def::TyParam(param_def_id) {
-                    add_bounds(&mut set, &data.bounds);
+                    if def == Def::TyParam(param_def_id) {
+                        add_bounds(&mut set, &data.bounds);
+                    }
                 }
-            }
 
-            Some(match set {
-                Set1::Empty => Set1::Empty,
-                Set1::One(name) => {
-                    if name == hir::LifetimeName::Static {
-                        Set1::One(Region::Static)
-                    } else {
-                        generics.params.iter().filter_map(|param| match param.kind {
-                            GenericParamKind::Lifetime { .. } => {
-                                Some((
-                                    param.id,
-                                    hir::LifetimeName::Param(param.name),
-                                    LifetimeDefOrigin::from_param(param),
-                                ))
-                            }
-                            _ => None,
-                        })
-                        .enumerate()
-                        .find(|&(_, (_, lt_name, _))| lt_name == name)
-                        .map_or(Set1::Many, |(i, (id, _, origin))| {
-                            let def_id = tcx.hir.local_def_id(id);
-                            Set1::One(Region::EarlyBound(i as u32, def_id, origin))
-                        })
+                Some(match set {
+                    Set1::Empty => Set1::Empty,
+                    Set1::One(name) => {
+                        if name == hir::LifetimeName::Static {
+                            Set1::One(Region::Static)
+                        } else {
+                            generics
+                                .params
+                                .iter()
+                                .filter_map(|param| match param.kind {
+                                    GenericParamKind::Lifetime { .. } => Some((
+                                        param.id,
+                                        hir::LifetimeName::Param(param.name),
+                                        LifetimeDefOrigin::from_param(param),
+                                    )),
+                                    _ => None,
+                                })
+                                .enumerate()
+                                .find(|&(_, (_, lt_name, _))| lt_name == name)
+                                .map_or(Set1::Many, |(i, (id, _, origin))| {
+                                    let def_id = tcx.hir.local_def_id(id);
+                                    Set1::One(Region::EarlyBound(i as u32, def_id, origin))
+                                })
+                        }
                     }
-                }
-                Set1::Many => Set1::Many,
-            })
-        }
-    })
-    .collect()
+                    Set1::Many => Set1::Many,
+                })
+            }
+        })
+        .collect()
 }
 
 impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
@@ -1405,20 +1450,19 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             // if sole lifetime, remove the `<>` brackets
             Some(generics.span)
         } else {
-            generics.params.iter().enumerate()
-                .find_map(|(i, param)| {
-                    if param.name.ident() == name {
-                        // We also want to delete a leading or trailing comma
-                        // as appropriate
-                        if i >= generics.params.len() - 1 {
-                            Some(generics.params[i-1].span.shrink_to_hi().to(param.span))
-                        } else {
-                            Some(param.span.to(generics.params[i+1].span.shrink_to_lo()))
-                        }
+            generics.params.iter().enumerate().find_map(|(i, param)| {
+                if param.name.ident() == name {
+                    // We also want to delete a leading or trailing comma
+                    // as appropriate
+                    if i >= generics.params.len() - 1 {
+                        Some(generics.params[i - 1].span.shrink_to_hi().to(param.span))
                     } else {
-                        None
+                        Some(param.span.to(generics.params[i + 1].span.shrink_to_lo()))
                     }
-                })
+                } else {
+                    None
+                }
+            })
         }
     }
 
@@ -1431,7 +1475,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             }
         };
 
-        let mut def_ids: Vec<_> = defined_by.values()
+        let mut def_ids: Vec<_> = defined_by
+            .values()
             .flat_map(|region| match region {
                 Region::EarlyBound(_, def_id, _)
                 | Region::LateBound(_, def_id, _)
@@ -1445,21 +1490,28 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         def_ids.sort_by_key(|&def_id| self.tcx.def_path_hash(def_id));
 
         for def_id in def_ids {
-            debug!("check_uses_for_lifetimes_defined_by_scope: def_id = {:?}", def_id);
+            debug!(
+                "check_uses_for_lifetimes_defined_by_scope: def_id = {:?}",
+                def_id
+            );
 
             let lifetimeuseset = self.lifetime_uses.remove(&def_id);
 
-            debug!("check_uses_for_lifetimes_defined_by_scope: lifetimeuseset = {:?}",
-                   lifetimeuseset);
+            debug!(
+                "check_uses_for_lifetimes_defined_by_scope: lifetimeuseset = {:?}",
+                lifetimeuseset
+            );
 
             match lifetimeuseset {
                 Some(LifetimeUseSet::One(lifetime)) => {
                     let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
                     debug!("node id first={:?}", node_id);
                     if let Some((id, span, name)) = match self.tcx.hir.get(node_id) {
-                        Node::Lifetime(hir_lifetime) => {
-                            Some((hir_lifetime.id, hir_lifetime.span, hir_lifetime.name.ident()))
-                        }
+                        Node::Lifetime(hir_lifetime) => Some((
+                            hir_lifetime.id,
+                            hir_lifetime.span,
+                            hir_lifetime.name.ident(),
+                        )),
                         Node::GenericParam(param) => {
                             Some((param.id, param.span, param.name.ident()))
                         }
@@ -1483,9 +1535,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 None => {
                     let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
                     if let Some((id, span, name)) = match self.tcx.hir.get(node_id) {
-                        Node::Lifetime(hir_lifetime) => {
-                            Some((hir_lifetime.id, hir_lifetime.span, hir_lifetime.name.ident()))
-                        }
+                        Node::Lifetime(hir_lifetime) => Some((
+                            hir_lifetime.id,
+                            hir_lifetime.span,
+                            hir_lifetime.name.ident(),
+                        )),
                         Node::GenericParam(param) => {
                             Some((param.id, param.span, param.name.ident()))
                         }
@@ -1496,7 +1550,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                             lint::builtin::UNUSED_LIFETIMES,
                             id,
                             span,
-                            &format!("lifetime parameter `{}` never used", name)
+                            &format!("lifetime parameter `{}` never used", name),
                         );
                         if let Some(parent_def_id) = self.tcx.parent(def_id) {
                             if let Some(generics) = self.tcx.hir.get_generics(parent_def_id) {
@@ -1506,7 +1560,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                                         span,
                                         "remove it",
                                         String::new(),
-                                        Applicability::MachineApplicable
+                                        Applicability::MachineApplicable,
                                     );
                                 }
                             }
@@ -1564,19 +1618,23 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         }
 
         let mut type_count = 0;
-        let lifetimes = generics.params.iter().filter_map(|param| match param.kind {
-            GenericParamKind::Lifetime { .. } => {
-                if self.map.late_bound.contains(&param.id) {
-                    Some(Region::late(&self.tcx.hir, param))
-                } else {
-                    Some(Region::early(&self.tcx.hir, &mut index, param))
+        let lifetimes = generics
+            .params
+            .iter()
+            .filter_map(|param| match param.kind {
+                GenericParamKind::Lifetime { .. } => {
+                    if self.map.late_bound.contains(&param.id) {
+                        Some(Region::late(&self.tcx.hir, param))
+                    } else {
+                        Some(Region::early(&self.tcx.hir, &mut index, param))
+                    }
                 }
-            }
-            GenericParamKind::Type { .. } => {
-                type_count += 1;
-                None
-            }
-        }).collect();
+                GenericParamKind::Type { .. } => {
+                    type_count += 1;
+                    None
+                }
+            })
+            .collect();
         let next_early_index = index + type_count;
 
         let scope = Scope::Binder {
@@ -1631,6 +1689,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
 
     fn resolve_lifetime_ref(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
         debug!("resolve_lifetime_ref(lifetime_ref={:?})", lifetime_ref);
+
+        // If we've already reported an error, just ignore `lifetime_ref`.
+        if let LifetimeName::Error = lifetime_ref.name {
+            return;
+        }
+
         // Walk up the scope chain, tracking the number of fn scopes
         // that we pass through, until we find a lifetime with the
         // given name or we run out of scopes.
@@ -1649,17 +1713,20 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     break None;
                 }
 
-                Scope::Binder { ref lifetimes, s, .. } => {
-                    let name = match lifetime_ref.name {
-                        LifetimeName::Param(param_name) => param_name,
+                Scope::Binder {
+                    ref lifetimes, s, ..
+                } => {
+                    match lifetime_ref.name {
+                        LifetimeName::Param(param_name) => {
+                            if let Some(&def) = lifetimes.get(&param_name.modern()) {
+                                break Some(def.shifted(late_depth));
+                            }
+                        }
                         _ => bug!("expected LifetimeName::Param"),
-                    };
-                    if let Some(&def) = lifetimes.get(&name.modern()) {
-                        break Some(def.shifted(late_depth));
-                    } else {
-                        late_depth += 1;
-                        scope = s;
                     }
+
+                    late_depth += 1;
+                    scope = s;
                 }
 
                 Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
@@ -1705,12 +1772,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                             "lifetimes used in `fn` or `Fn` syntax must be \
                              explicitly declared using `<...>` binders"
                         ).span_label(lifetime_ref.span, "in-band lifetime definition")
-                         .emit();
+                            .emit();
                     }
 
                     Region::Static
-                    | Region::EarlyBound(_, _, LifetimeDefOrigin::Explicit)
-                    | Region::LateBound(_, _, LifetimeDefOrigin::Explicit)
+                    | Region::EarlyBound(_, _, LifetimeDefOrigin::ExplicitOrElided)
+                    | Region::LateBound(_, _, LifetimeDefOrigin::ExplicitOrElided)
+                    | Region::EarlyBound(_, _, LifetimeDefOrigin::Error)
+                    | Region::LateBound(_, _, LifetimeDefOrigin::Error)
                     | Region::LateBoundAnon(..)
                     | Region::Free(..) => {}
                 }
@@ -1725,35 +1794,33 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 "use of undeclared lifetime name `{}`",
                 lifetime_ref
             ).span_label(lifetime_ref.span, "undeclared lifetime")
-             .emit();
+                .emit();
         }
     }
 
-    fn visit_segment_args(
-        &mut self,
-        def: Def,
-        depth: usize,
-        generic_args: &'tcx hir::GenericArgs,
-    ) {
+    fn visit_segment_args(&mut self, def: Def, depth: usize, generic_args: &'tcx hir::GenericArgs) {
         if generic_args.parenthesized {
             let was_in_fn_syntax = self.is_in_fn_syntax;
             self.is_in_fn_syntax = true;
-            self.visit_fn_like_elision(generic_args.inputs(),
-                                       Some(&generic_args.bindings[0].ty));
+            self.visit_fn_like_elision(generic_args.inputs(), Some(&generic_args.bindings[0].ty));
             self.is_in_fn_syntax = was_in_fn_syntax;
             return;
         }
 
         let mut elide_lifetimes = true;
-        let lifetimes = generic_args.args.iter().filter_map(|arg| match arg {
-            hir::GenericArg::Lifetime(lt) => {
-                if !lt.is_elided() {
-                    elide_lifetimes = false;
+        let lifetimes = generic_args
+            .args
+            .iter()
+            .filter_map(|arg| match arg {
+                hir::GenericArg::Lifetime(lt) => {
+                    if !lt.is_elided() {
+                        elide_lifetimes = false;
+                    }
+                    Some(lt)
                 }
-                Some(lt)
-            }
-            _ => None,
-        }).collect();
+                _ => None,
+            })
+            .collect();
         if elide_lifetimes {
             self.resolve_elided_lifetimes(lifetimes);
         } else {
@@ -1809,33 +1876,37 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 self.xcrate_object_lifetime_defaults
                     .entry(def_id)
                     .or_insert_with(|| {
-                        tcx.generics_of(def_id).params.iter().filter_map(|param| {
-                            match param.kind {
-                                GenericParamDefKind::Type { object_lifetime_default, .. } => {
-                                    Some(object_lifetime_default)
-                                }
+                        tcx.generics_of(def_id)
+                            .params
+                            .iter()
+                            .filter_map(|param| match param.kind {
+                                GenericParamDefKind::Type {
+                                    object_lifetime_default,
+                                    ..
+                                } => Some(object_lifetime_default),
                                 GenericParamDefKind::Lifetime => None,
-                            }
-                        }).collect()
+                            })
+                            .collect()
                     })
             };
-            unsubst.iter()
-                   .map(|set| match *set {
-                       Set1::Empty => if in_body {
-                           None
-                       } else {
-                           Some(Region::Static)
-                       },
-                       Set1::One(r) => {
-                           let lifetimes = generic_args.args.iter().filter_map(|arg| match arg {
-                               GenericArg::Lifetime(lt) => Some(lt),
-                               _ => None,
-                           });
-                           r.subst(lifetimes, map)
-                       }
-                       Set1::Many => None,
-                   })
-                   .collect()
+            unsubst
+                .iter()
+                .map(|set| match *set {
+                    Set1::Empty => if in_body {
+                        None
+                    } else {
+                        Some(Region::Static)
+                    },
+                    Set1::One(r) => {
+                        let lifetimes = generic_args.args.iter().filter_map(|arg| match arg {
+                            GenericArg::Lifetime(lt) => Some(lt),
+                            _ => None,
+                        });
+                        r.subst(lifetimes, map)
+                    }
+                    Set1::Many => None,
+                })
+                .collect()
         });
 
         let mut i = 0;
@@ -1862,11 +1933,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         }
     }
 
-    fn visit_fn_like_elision(
-        &mut self,
-        inputs: &'tcx [hir::Ty],
-        output: Option<&'tcx P<hir::Ty>>,
-    ) {
+    fn visit_fn_like_elision(&mut self, inputs: &'tcx [hir::Ty], output: Option<&'tcx P<hir::Ty>>) {
         debug!("visit_fn_like_elision: enter");
         let mut arg_elide = Elide::FreshLateAnon(Cell::new(0));
         let arg_scope = Scope::Elision {
@@ -2126,8 +2193,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         }
     }
 
-    fn resolve_elided_lifetimes(&mut self,
-                                lifetime_refs: Vec<&'tcx hir::Lifetime>) {
+    fn resolve_elided_lifetimes(&mut self, lifetime_refs: Vec<&'tcx hir::Lifetime>) {
         if lifetime_refs.is_empty() {
             return;
         }
@@ -2285,23 +2351,32 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
 
                 Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return,
 
-                Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l,
+                Scope::ObjectLifetimeDefault {
+                    lifetime: Some(l), ..
+                } => break l,
             }
         };
         self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
     }
 
-    fn check_lifetime_params(&mut self, old_scope: ScopeRef<'_>,
-                             params: &'tcx [hir::GenericParam]) {
-        let lifetimes: Vec<_> = params.iter().filter_map(|param| match param.kind {
-            GenericParamKind::Lifetime { .. } => Some((param, param.name)),
-            _ => None,
-        }).collect();
+    fn check_lifetime_params(
+        &mut self,
+        old_scope: ScopeRef<'_>,
+        params: &'tcx [hir::GenericParam],
+    ) {
+        let lifetimes: Vec<_> = params
+            .iter()
+            .filter_map(|param| match param.kind {
+                GenericParamKind::Lifetime { .. } => Some((param, param.name)),
+                _ => None,
+            })
+            .collect();
         for (i, (lifetime_i, lifetime_i_name)) in lifetimes.iter().enumerate() {
             if let hir::ParamName::Plain(_) = lifetime_i_name {
                 let name = lifetime_i_name.ident().name;
-                if name == keywords::UnderscoreLifetime.name() ||
-                   name == keywords::StaticLifetime.name() {
+                if name == keywords::UnderscoreLifetime.name()
+                    || name == keywords::StaticLifetime.name()
+                {
                     let mut err = struct_span_err!(
                         self.tcx.sess,
                         lifetime_i.span,
@@ -2327,8 +2402,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                         "lifetime name `{}` declared twice in the same scope",
                         lifetime_j.name.ident()
                     ).span_label(lifetime_j.span, "declared twice")
-                     .span_label(lifetime_i.span, "previous declaration here")
-                     .emit();
+                        .span_label(lifetime_i.span, "previous declaration here")
+                        .emit();
                 }
             }
 
@@ -2338,33 +2413,34 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             for bound in &lifetime_i.bounds {
                 match bound {
                     hir::GenericBound::Outlives(lt) => match lt.name {
-                        hir::LifetimeName::Underscore => {
-                            let mut err = struct_span_err!(
-                                self.tcx.sess,
-                                lt.span,
-                                E0637,
-                                "invalid lifetime bound name: `'_`"
-                            );
-                            err.span_label(lt.span, "`'_` is a reserved lifetime name");
-                            err.emit();
-                        }
+                        hir::LifetimeName::Underscore => self.tcx.sess.delay_span_bug(
+                            lt.span,
+                            "use of `'_` in illegal place, but not caught by lowering",
+                        ),
                         hir::LifetimeName::Static => {
                             self.insert_lifetime(lt, Region::Static);
-                            self.tcx.sess.struct_span_warn(
-                                lifetime_i.span.to(lt.span),
-                                &format!(
-                                    "unnecessary lifetime parameter `{}`",
+                            self.tcx
+                                .sess
+                                .struct_span_warn(
+                                    lifetime_i.span.to(lt.span),
+                                    &format!(
+                                        "unnecessary lifetime parameter `{}`",
+                                        lifetime_i.name.ident(),
+                                    ),
+                                )
+                                .help(&format!(
+                                    "you can use the `'static` lifetime directly, in place of `{}`",
                                     lifetime_i.name.ident(),
-                                ),
-                            ).help(&format!(
-                                "you can use the `'static` lifetime directly, in place of `{}`",
-                                lifetime_i.name.ident(),
-                            )).emit();
+                                ))
+                                .emit();
                         }
                         hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
                             self.resolve_lifetime_ref(lt);
                         }
-                    }
+                        hir::LifetimeName::Error => {
+                            // No need to do anything, error already reported.
+                        }
+                    },
                     _ => bug!(),
                 }
             }
@@ -2533,8 +2609,10 @@ fn insert_late_bound_lifetimes(
     decl: &hir::FnDecl,
     generics: &hir::Generics,
 ) {
-    debug!("insert_late_bound_lifetimes(decl={:?}, generics={:?})",
-           decl, generics);
+    debug!(
+        "insert_late_bound_lifetimes(decl={:?}, generics={:?})",
+        decl, generics
+    );
 
     let mut constrained_by_input = ConstrainedCollector {
         regions: FxHashSet(),
@@ -2548,8 +2626,10 @@ fn insert_late_bound_lifetimes(
     };
     intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
 
-    debug!("insert_late_bound_lifetimes: constrained_by_input={:?}",
-           constrained_by_input.regions);
+    debug!(
+        "insert_late_bound_lifetimes: constrained_by_input={:?}",
+        constrained_by_input.regions
+    );
 
     // Walk the lifetimes that appear in where clauses.
     //
@@ -2565,7 +2645,8 @@ fn insert_late_bound_lifetimes(
             if !param.bounds.is_empty() {
                 // `'a: 'b` means both `'a` and `'b` are referenced
                 appears_in_where_clause
-                    .regions.insert(hir::LifetimeName::Param(param.name.modern()));
+                    .regions
+                    .insert(hir::LifetimeName::Param(param.name.modern()));
             }
         }
     }
@@ -2600,9 +2681,11 @@ fn insert_late_bound_lifetimes(
             continue;
         }
 
-        debug!("insert_late_bound_lifetimes: lifetime {:?} with id {:?} is late-bound",
-               param.name.ident(),
-               param.id);
+        debug!(
+            "insert_late_bound_lifetimes: lifetime {:?} with id {:?} is late-bound",
+            param.name.ident(),
+            param.id
+        );
 
         let inserted = map.late_bound.insert(param.id);
         assert!(inserted, "visited lifetime {:?} twice", param.id);
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index d18edf22dc10c..bc5f688729c36 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -2074,6 +2074,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
                     hir::GenericParamKind::Type { .. } => {
                         match param.name {
                             hir::ParamName::Fresh(_) => { continue; },
+                            hir::ParamName::Error => { continue; },
                             hir::ParamName::Plain(name) => name.to_string()
                         }
                     }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
index 65ba2f537bf21..ab80eaba69924 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
@@ -563,6 +563,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let lifetime = self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?;
         match lifetime.name {
             hir::LifetimeName::Param(_)
+            | hir::LifetimeName::Error
             | hir::LifetimeName::Static
             | hir::LifetimeName::Underscore => {
                 let region_name = self.synthesize_region_name(counter);
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index cc1906d91d4c9..713e267543269 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -816,7 +816,10 @@ fn check_variances_for_type_defn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
 
         let param = &hir_generics.params[index];
-        report_bivariance(tcx, param.span, param.name.ident().name);
+        match param.name {
+            hir::ParamName::Error => { }
+            _ => report_bivariance(tcx, param.span, param.name.ident().name),
+        }
     }
 }
 
diff --git a/src/test/ui/error-codes/E0637.rs b/src/test/ui/error-codes/E0637.rs
index ee6a978d169a3..b4f769a749f80 100644
--- a/src/test/ui/error-codes/E0637.rs
+++ b/src/test/ui/error-codes/E0637.rs
@@ -8,11 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-struct Foo<'a: '_>(&'a u8); //~ ERROR invalid lifetime bound name: `'_`
-fn foo<'a: '_>(_: &'a u8) {} //~ ERROR invalid lifetime bound name: `'_`
+struct Foo<'a: '_>(&'a u8); //~ ERROR cannot be used here
+fn foo<'a: '_>(_: &'a u8) {} //~ ERROR cannot be used here
 
 struct Bar<'a>(&'a u8);
-impl<'a: '_> Bar<'a> { //~ ERROR invalid lifetime bound name: `'_`
+impl<'a: '_> Bar<'a> { //~ ERROR cannot be used here
   fn bar() {}
 }
 
diff --git a/src/test/ui/error-codes/E0637.stderr b/src/test/ui/error-codes/E0637.stderr
index 245729376df36..2a4545fc43d08 100644
--- a/src/test/ui/error-codes/E0637.stderr
+++ b/src/test/ui/error-codes/E0637.stderr
@@ -1,19 +1,19 @@
-error[E0637]: invalid lifetime bound name: `'_`
+error[E0637]: `'_` cannot be used here
   --> $DIR/E0637.rs:11:16
    |
-LL | struct Foo<'a: '_>(&'a u8); //~ ERROR invalid lifetime bound name: `'_`
+LL | struct Foo<'a: '_>(&'a u8); //~ ERROR cannot be used here
    |                ^^ `'_` is a reserved lifetime name
 
-error[E0637]: invalid lifetime bound name: `'_`
+error[E0637]: `'_` cannot be used here
   --> $DIR/E0637.rs:12:12
    |
-LL | fn foo<'a: '_>(_: &'a u8) {} //~ ERROR invalid lifetime bound name: `'_`
+LL | fn foo<'a: '_>(_: &'a u8) {} //~ ERROR cannot be used here
    |            ^^ `'_` is a reserved lifetime name
 
-error[E0637]: invalid lifetime bound name: `'_`
+error[E0637]: `'_` cannot be used here
   --> $DIR/E0637.rs:15:10
    |
-LL | impl<'a: '_> Bar<'a> { //~ ERROR invalid lifetime bound name: `'_`
+LL | impl<'a: '_> Bar<'a> { //~ ERROR cannot be used here
    |          ^^ `'_` is a reserved lifetime name
 
 error: aborting due to 3 previous errors
diff --git a/src/test/ui/underscore-lifetime/in-binder.Rust2015.stderr b/src/test/ui/underscore-lifetime/in-binder.Rust2015.stderr
new file mode 100644
index 0000000000000..a851e6b2071b2
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-binder.Rust2015.stderr
@@ -0,0 +1,46 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:12:6
+   |
+LL | impl<'_> IceCube<'_> {}
+   |      ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:17:15
+   |
+LL | struct Struct<'_> {
+   |               ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:23:11
+   |
+LL | enum Enum<'_> {
+   |           ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:29:13
+   |
+LL | union Union<'_> {
+   |             ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:35:13
+   |
+LL | trait Trait<'_> {
+   |             ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:40:8
+   |
+LL | fn foo<'_>() {
+   |        ^^ `'_` is a reserved lifetime name
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/in-binder.rs:12:18
+   |
+LL | impl<'_> IceCube<'_> {}
+   |                  ^^ expected lifetime parameter
+
+error: aborting due to 7 previous errors
+
+Some errors occurred: E0106, E0637.
+For more information about an error, try `rustc --explain E0106`.
diff --git a/src/test/ui/underscore-lifetime/in-binder.Rust2018.stderr b/src/test/ui/underscore-lifetime/in-binder.Rust2018.stderr
new file mode 100644
index 0000000000000..77da3038724b8
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-binder.Rust2018.stderr
@@ -0,0 +1,39 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:12:6
+   |
+LL | impl<'_> IceCube<'_> {}
+   |      ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:17:15
+   |
+LL | struct Struct<'_> {
+   |               ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:23:11
+   |
+LL | enum Enum<'_> {
+   |           ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:29:13
+   |
+LL | union Union<'_> {
+   |             ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:35:13
+   |
+LL | trait Trait<'_> {
+   |             ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/in-binder.rs:40:8
+   |
+LL | fn foo<'_>() {
+   |        ^^ `'_` is a reserved lifetime name
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/in-binder.rs b/src/test/ui/underscore-lifetime/in-binder.rs
new file mode 100644
index 0000000000000..c94947d1eab1c
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-binder.rs
@@ -0,0 +1,45 @@
+// Check that we error when `'_` appears as the name of a lifetime parameter.
+//
+// Regression test for #52098.
+
+// revisions: Rust2015 Rust2018
+//[Rust2018] edition:2018
+
+struct IceCube<'a> {
+    v: Vec<&'a char>
+}
+
+impl<'_> IceCube<'_> {}
+//[Rust2015]~^ ERROR `'_` cannot be used here
+//[Rust2015]~| ERROR missing lifetime specifier
+//[Rust2018]~^^^ ERROR `'_` cannot be used here
+
+struct Struct<'_> {
+//[Rust2015]~^ ERROR `'_` cannot be used here
+//[Rust2018]~^^ ERROR `'_` cannot be used here
+    v: Vec<&'static char>
+}
+
+enum Enum<'_> {
+//[Rust2015]~^ ERROR `'_` cannot be used here
+//[Rust2018]~^^ ERROR `'_` cannot be used here
+    Variant
+}
+
+union Union<'_> {
+//[Rust2015]~^ ERROR `'_` cannot be used here
+//[Rust2018]~^^ ERROR `'_` cannot be used here
+    a: u32
+}
+
+trait Trait<'_> {
+//[Rust2015]~^ ERROR `'_` cannot be used here
+//[Rust2018]~^^ ERROR `'_` cannot be used here
+}
+
+fn foo<'_>() {
+    //[Rust2015]~^ ERROR `'_` cannot be used here
+    //[Rust2018]~^^ ERROR `'_` cannot be used here
+}
+
+fn main() {}
diff --git a/src/test/ui/underscore-lifetime/in-fn-return-illegal.rs b/src/test/ui/underscore-lifetime/in-fn-return-illegal.rs
new file mode 100644
index 0000000000000..09f3abd9135cb
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-fn-return-illegal.rs
@@ -0,0 +1,17 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that the `'_` used in structs/enums gives an error.
+
+use std::fmt::Debug;
+
+fn foo(x: &u32, y: &u32) -> &'_ u32 { loop { } } //~ ERROR missing lifetime specifier
+
+fn main() { }
diff --git a/src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr b/src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr
new file mode 100644
index 0000000000000..f3ba3e5292426
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/in-fn-return-illegal.rs:15:30
+   |
+LL | fn foo(x: &u32, y: &u32) -> &'_ u32 { loop { } } //~ ERROR missing lifetime specifier
+   |                              ^^ expected lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/underscore-lifetime/in-struct.rs b/src/test/ui/underscore-lifetime/in-struct.rs
new file mode 100644
index 0000000000000..1c1a9ef62d594
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-struct.rs
@@ -0,0 +1,23 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that the `'_` used in structs/enums gives an error.
+
+use std::fmt::Debug;
+
+struct Foo {
+    x: &'_ u32, //~ ERROR missing lifetime specifier
+}
+
+enum Bar {
+    Variant(&'_ u32), //~ ERROR missing lifetime specifier
+}
+
+fn main() { }
diff --git a/src/test/ui/underscore-lifetime/in-struct.stderr b/src/test/ui/underscore-lifetime/in-struct.stderr
new file mode 100644
index 0000000000000..d288995d4bb82
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-struct.stderr
@@ -0,0 +1,15 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/in-struct.rs:16:9
+   |
+LL |     x: &'_ u32, //~ ERROR missing lifetime specifier
+   |         ^^ expected lifetime parameter
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/in-struct.rs:20:14
+   |
+LL |     Variant(&'_ u32), //~ ERROR missing lifetime specifier
+   |              ^^ expected lifetime parameter
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.rs b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.rs
index 6a6698957d9d1..2652fc62bb622 100644
--- a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.rs
+++ b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.rs
@@ -15,13 +15,13 @@ impl Foo<'_> { //~ ERROR missing lifetime specifier
     fn x() {}
 }
 
-fn foo<'_> //~ ERROR invalid lifetime parameter name: `'_`
+fn foo<'_> //~ ERROR cannot be used here
 (_: Foo<'_>) {}
 
 trait Meh<'a> {}
 impl<'a> Meh<'a> for u8 {}
 
-fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR invalid lifetime parameter name: `'_`
+fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR cannot be used here
 //~^ ERROR missing lifetime specifier
 {
   Box::new(5u8)
diff --git a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
index 4917c2795e39d..fc9f3e642d402 100644
--- a/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
+++ b/src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr
@@ -1,3 +1,15 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/underscore-lifetime-binders.rs:18:8
+   |
+LL | fn foo<'_> //~ ERROR cannot be used here
+   |        ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/underscore-lifetime-binders.rs:24:21
+   |
+LL | fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR cannot be used here
+   |                     ^^ `'_` is a reserved lifetime name
+
 error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:12:17
    |
@@ -10,22 +22,10 @@ error[E0106]: missing lifetime specifier
 LL | impl Foo<'_> { //~ ERROR missing lifetime specifier
    |          ^^ expected lifetime parameter
 
-error[E0262]: invalid lifetime parameter name: `'_`
-  --> $DIR/underscore-lifetime-binders.rs:18:8
-   |
-LL | fn foo<'_> //~ ERROR invalid lifetime parameter name: `'_`
-   |        ^^ '_ is a reserved lifetime name
-
-error[E0262]: invalid lifetime parameter name: `'_`
-  --> $DIR/underscore-lifetime-binders.rs:24:21
-   |
-LL | fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR invalid lifetime parameter name: `'_`
-   |                     ^^ '_ is a reserved lifetime name
-
 error[E0106]: missing lifetime specifier
   --> $DIR/underscore-lifetime-binders.rs:24:29
    |
-LL | fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR invalid lifetime parameter name: `'_`
+LL | fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR cannot be used here
    |                             ^^ expected lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
@@ -41,5 +41,5 @@ LL | fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y } //~ ERROR missing lifetime sp
 
 error: aborting due to 6 previous errors
 
-Some errors occurred: E0106, E0262.
+Some errors occurred: E0106, E0637.
 For more information about an error, try `rustc --explain E0106`.
diff --git a/src/test/ui/underscore-lifetime/underscore-outlives-bounds.rs b/src/test/ui/underscore-lifetime/underscore-outlives-bounds.rs
new file mode 100644
index 0000000000000..b514ff4386075
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/underscore-outlives-bounds.rs
@@ -0,0 +1,8 @@
+// Regression test to check that `'b: '_` gets an error, because it's
+// basically useless.
+//
+// #54902
+
+trait Foo<'a> {}
+impl<'b: '_> Foo<'b> for i32 {}
+fn main() { }
diff --git a/src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr b/src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr
new file mode 100644
index 0000000000000..4b38a26f957f9
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/underscore-outlives-bounds.rs:7:10
+   |
+LL | impl<'b: '_> Foo<'b> for i32 {}
+   |          ^^ `'_` is a reserved lifetime name
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rs b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rs
new file mode 100644
index 0000000000000..43de30944cacb
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rs
@@ -0,0 +1,18 @@
+// revisions: rust2015 rust2018
+//[rust2018] edition:2018
+
+trait WithType<T> {}
+trait WithRegion<'a> { }
+
+struct Foo<T> {
+    t: T
+}
+
+impl<T> Foo<T>
+where
+    T: WithType<&u32>
+//[rust2015]~^ ERROR `&` without an explicit lifetime name cannot be used here
+//[rust2018]~^^ ERROR `&` without an explicit lifetime name cannot be used here
+{ }
+
+fn main() {}
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr
new file mode 100644
index 0000000000000..fe726cb49c737
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/where-clause-inherent-impl-ampersand.rs:13:17
+   |
+LL |     T: WithType<&u32>
+   |                 ^ explicit lifetime name needed here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr
new file mode 100644
index 0000000000000..fe726cb49c737
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/where-clause-inherent-impl-ampersand.rs:13:17
+   |
+LL |     T: WithType<&u32>
+   |                 ^ explicit lifetime name needed here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rs b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rs
new file mode 100644
index 0000000000000..b50cce335bd8c
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rs
@@ -0,0 +1,18 @@
+// revisions: rust2015 rust2018
+//[rust2018] edition:2018
+
+trait WithType<T> {}
+trait WithRegion<'a> { }
+
+struct Foo<T> {
+    t: T
+}
+
+impl<T> Foo<T>
+where
+    T: WithRegion<'_>
+//[rust2015]~^ ERROR `'_` cannot be used here
+//[rust2018]~^^ ERROR `'_` cannot be used here
+{ }
+
+fn main() {}
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr
new file mode 100644
index 0000000000000..95939fd6b7e03
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/where-clause-inherent-impl-underscore.rs:13:19
+   |
+LL |     T: WithRegion<'_>
+   |                   ^^ `'_` is a reserved lifetime name
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr
new file mode 100644
index 0000000000000..95939fd6b7e03
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/where-clause-inherent-impl-underscore.rs:13:19
+   |
+LL |     T: WithRegion<'_>
+   |                   ^^ `'_` is a reserved lifetime name
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rs b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rs
new file mode 100644
index 0000000000000..f2d483e66e013
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rs
@@ -0,0 +1,16 @@
+// revisions: rust2015 rust2018
+//[rust2018] edition:2018
+
+trait WithType<T> {}
+trait WithRegion<'a> { }
+
+trait Foo { }
+
+impl<T> Foo for Vec<T>
+where
+    T: WithType<&u32>
+//[rust2015]~^ ERROR `&` without an explicit lifetime name cannot be used here
+//[rust2018]~^^ ERROR `&` without an explicit lifetime name cannot be used here
+{ }
+
+fn main() {}
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr
new file mode 100644
index 0000000000000..fbd14de21078b
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/where-clause-trait-impl-region.rs:11:17
+   |
+LL |     T: WithType<&u32>
+   |                 ^ explicit lifetime name needed here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr
new file mode 100644
index 0000000000000..fbd14de21078b
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/where-clause-trait-impl-region.rs:11:17
+   |
+LL |     T: WithType<&u32>
+   |                 ^ explicit lifetime name needed here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rs b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rs
new file mode 100644
index 0000000000000..94e4426e822d9
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rs
@@ -0,0 +1,16 @@
+// revisions: rust2015 rust2018
+//[rust2018] edition:2018
+
+trait WithType<T> {}
+trait WithRegion<'a> { }
+
+trait Foo { }
+
+impl<T> Foo for Vec<T>
+where
+    T: WithRegion<'_>
+//[rust2015]~^ ERROR `'_` cannot be used here
+//[rust2018]~^^ ERROR `'_` cannot be used here
+{ }
+
+fn main() {}
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr
new file mode 100644
index 0000000000000..92caff0dcde99
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/where-clause-trait-impl-underscore.rs:11:19
+   |
+LL |     T: WithRegion<'_>
+   |                   ^^ `'_` is a reserved lifetime name
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr
new file mode 100644
index 0000000000000..92caff0dcde99
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/where-clause-trait-impl-underscore.rs:11:19
+   |
+LL |     T: WithRegion<'_>
+   |                   ^^ `'_` is a reserved lifetime name
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/src/test/ui/underscore-lifetime/where-clauses.rs b/src/test/ui/underscore-lifetime/where-clauses.rs
new file mode 100644
index 0000000000000..ee6823b804782
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clauses.rs
@@ -0,0 +1,7 @@
+trait Foo<'a> {}
+
+impl<'b: '_> Foo<'b> for i32 {} //~ ERROR `'_` cannot be used here
+
+impl<T: '_> Foo<'static> for Vec<T> {} //~ ERROR `'_` cannot be used here
+
+fn main() { }
diff --git a/src/test/ui/underscore-lifetime/where-clauses.stderr b/src/test/ui/underscore-lifetime/where-clauses.stderr
new file mode 100644
index 0000000000000..57fe2456f4cc5
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clauses.stderr
@@ -0,0 +1,15 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/where-clauses.rs:3:10
+   |
+LL | impl<'b: '_> Foo<'b> for i32 {} //~ ERROR `'_` cannot be used here
+   |          ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/where-clauses.rs:5:9
+   |
+LL | impl<T: '_> Foo<'static> for Vec<T> {} //~ ERROR `'_` cannot be used here
+   |         ^^ `'_` is a reserved lifetime name
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0637`.