about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-10-11 15:51:44 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-10-17 17:15:24 -0400
commit1f4d100472c7ea84091fc5a2b028d517cf6cdaf1 (patch)
tree5f870311b74e4826c7d60adf2829b187b0aed1c7
parent68da108d56577657135af0186826ddef93104d22 (diff)
move E0637 to lowering and improve output, add more tests
-rw-r--r--src/librustc/hir/intravisit.rs4
-rw-r--r--src/librustc/hir/lowering.rs106
-rw-r--r--src/librustc/hir/mod.rs17
-rw-r--r--src/librustc/ich/impls_hir.rs7
-rw-r--r--src/librustc/ich/impls_ty.rs5
-rw-r--r--src/librustc/middle/resolve_lifetime.rs57
-rw-r--r--src/librustc_lint/builtin.rs1
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs1
-rw-r--r--src/librustc_typeck/check/wfcheck.rs5
-rw-r--r--src/test/ui/error-codes/E0637.rs6
-rw-r--r--src/test/ui/error-codes/E0637.stderr12
-rw-r--r--src/test/ui/underscore-lifetime/in-binder.Rust2015.stderr46
-rw-r--r--src/test/ui/underscore-lifetime/in-binder.Rust2018.stderr39
-rw-r--r--src/test/ui/underscore-lifetime/in-binder.rs45
-rw-r--r--src/test/ui/underscore-lifetime/in-fn-return-illegal.rs17
-rw-r--r--src/test/ui/underscore-lifetime/in-fn-return-illegal.stderr11
-rw-r--r--src/test/ui/underscore-lifetime/in-struct.rs23
-rw-r--r--src/test/ui/underscore-lifetime/in-struct.stderr15
-rw-r--r--src/test/ui/underscore-lifetime/underscore-lifetime-binders.rs4
-rw-r--r--src/test/ui/underscore-lifetime/underscore-lifetime-binders.stderr28
-rw-r--r--src/test/ui/underscore-lifetime/underscore-outlives-bounds.rs8
-rw-r--r--src/test/ui/underscore-lifetime/underscore-outlives-bounds.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rs18
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2015.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-inherent-impl-ampersand.rust2018.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rs18
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2015.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-inherent-impl-underscore.rust2018.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rs16
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2015.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-trait-impl-region.rust2018.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rs16
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2015.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clause-trait-impl-underscore.rust2018.stderr9
-rw-r--r--src/test/ui/underscore-lifetime/where-clauses.rs3
-rw-r--r--src/test/ui/underscore-lifetime/where-clauses.stderr9
36 files changed, 539 insertions, 79 deletions
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 942f1cbd665..95b73d5f87b 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 16e8b8709a8..1fc677b0e29 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,17 +2303,15 @@ 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::Outlives(ref lifetime) => {
-                // We don't want to accept `'a: '_`:
-                self.with_anonymous_lifetime_mode(
-                    AnonymousLifetimeMode::PassThrough,
-                    |this| hir::GenericBound::Outlives(this.lower_lifetime(lifetime)),
+            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))
+            }
         }
     }
 
@@ -2323,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);
@@ -2361,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,
@@ -2494,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 {
@@ -4843,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
@@ -4861,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 => {}
         }
@@ -4891,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 c57c26434e3..01b68ae669b 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 da9604702df..fe663c68cd5 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 f51a3e71d07..0c8224710d3 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 2f3fdb7966f..671604918f7 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -43,20 +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
+                match kind {
+                    LifetimeParamKind::InBand => LifetimeDefOrigin::InBand,
+                    LifetimeParamKind::Explicit => LifetimeDefOrigin::ExplicitOrElided,
+                    LifetimeParamKind::Elided => LifetimeDefOrigin::ExplicitOrElided,
+                    LifetimeParamKind::Error => LifetimeDefOrigin::Error,
                 }
             }
             _ => bug!("expected a lifetime param"),
@@ -612,6 +615,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) => {
@@ -1631,6 +1635,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.
@@ -1650,16 +1660,17 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 }
 
                 Scope::Binder { ref lifetimes, s, .. } => {
-                    let name = match lifetime_ref.name {
-                        LifetimeName::Param(param_name) => param_name,
+                    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, .. } => {
@@ -1709,8 +1720,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     }
 
                     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(..) => {}
                 }
@@ -2339,14 +2352,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 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();
+                            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);
@@ -2364,6 +2370,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                         hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
                             self.resolve_lifetime_ref(lt);
                         }
+                        hir::LifetimeName::Error => {
+                            // No need to do anything, error already reported.
+                        }
                     }
                     _ => bug!(),
                 }
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index d18edf22dc1..bc5f688729c 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 65ba2f537bf..ab80eaba699 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 cc1906d91d4..713e2675432 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 ee6a978d169..b4f769a749f 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 245729376df..2a4545fc43d 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 00000000000..a851e6b2071
--- /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 00000000000..77da3038724
--- /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 00000000000..fc0602a0ba9
--- /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
+//[Rust2015]~| ERROR
+//[Rust2018]~^^^ ERROR
+
+struct Struct<'_> {
+//[Rust2015]~^ ERROR
+//[Rust2018]~^^ ERROR
+    v: Vec<&'static char>
+}
+
+enum Enum<'_> {
+//[Rust2015]~^ ERROR
+//[Rust2018]~^^ ERROR
+    Variant
+}
+
+union Union<'_> {
+//[Rust2015]~^ ERROR
+//[Rust2018]~^^ ERROR
+    a: u32
+}
+
+trait Trait<'_> {
+//[Rust2015]~^ ERROR
+//[Rust2018]~^^ ERROR
+}
+
+fn foo<'_>() {
+    //[Rust2015]~^ ERROR
+    //[Rust2018]~^^ ERROR
+}
+
+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 00000000000..959254b8bb8
--- /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
+
+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 00000000000..6536e6b278e
--- /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
+   |                              ^^ 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 00000000000..be605ed50a5
--- /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
+}
+
+struct Bar {
+    Variant(&'_ u32), //~ ERROR
+}
+
+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 00000000000..b60b3db408d
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/in-struct.stderr
@@ -0,0 +1,15 @@
+error: expected `:`, found `(`
+  --> $DIR/in-struct.rs:20:12
+   |
+LL |     Variant(&'_ u32), //~ ERROR
+   |            ^ expected `:`
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/in-struct.rs:16:9
+   |
+LL |     x: &'_ u32, //~ ERROR
+   |         ^^ 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 6a6698957d9..2652fc62bb6 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 4917c2795e3..fc9f3e642d4 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 00000000000..b514ff43860
--- /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 00000000000..4b38a26f957
--- /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 00000000000..ee6b35d878e
--- /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
+//[rust2018]~^^ ERROR
+{ }
+
+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 00000000000..fe726cb49c7
--- /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 00000000000..fe726cb49c7
--- /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 00000000000..55aecbc9fcf
--- /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
+//[rust2018]~^^ ERROR
+{ }
+
+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 00000000000..95939fd6b7e
--- /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 00000000000..95939fd6b7e
--- /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 00000000000..47de6a7af9c
--- /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
+//[rust2018]~^^ ERROR
+{ }
+
+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 00000000000..fbd14de2107
--- /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 00000000000..fbd14de2107
--- /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 00000000000..67db6be5361
--- /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
+//[rust2018]~^^ ERROR
+{ }
+
+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 00000000000..92caff0dcde
--- /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 00000000000..92caff0dcde
--- /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 00000000000..da91718fec3
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clauses.rs
@@ -0,0 +1,3 @@
+trait Foo<'a> {}
+impl<'b: '_> Foo<'b> for i32 {}
+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 00000000000..98ac5b5b3dc
--- /dev/null
+++ b/src/test/ui/underscore-lifetime/where-clauses.stderr
@@ -0,0 +1,9 @@
+error[E0637]: `'_` cannot be used here
+  --> $DIR/where-clauses.rs:2: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`.