about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-05-01 01:03:43 +0000
committerMichael Goulet <michael@errs.io>2025-05-07 18:12:54 +0000
commitf03d246db98e0b67f38b015bcc86a23d9c1e9adf (patch)
treed217c958100d2576ea19b09ee43c2079892bfbe7
parentb27d630f8958374bc774bc183105ee23f5320a3a (diff)
downloadrust-f03d246db98e0b67f38b015bcc86a23d9c1e9adf.tar.gz
rust-f03d246db98e0b67f38b015bcc86a23d9c1e9adf.zip
Better error message for late/early lifetime param mismatch
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs237
-rw-r--r--tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs6
-rw-r--r--tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr90
-rw-r--r--tests/ui/error-codes/E0195.rs19
-rw-r--r--tests/ui/error-codes/E0195.stderr42
-rw-r--r--tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr53
6 files changed, 398 insertions, 49 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 553d981aa38..fb67f2fd223 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -5,7 +5,7 @@ use std::iter;
 use hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::codes::*;
-use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
+use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::VisitorExt;
 use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit};
@@ -14,10 +14,10 @@ use rustc_infer::traits::util;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{
     self, BottomUpFolder, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder,
-    TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast,
+    TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
 };
 use rustc_middle::{bug, span_bug};
-use rustc_span::Span;
+use rustc_span::{DUMMY_SP, Span};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::regions::InferCtxtRegionExt;
@@ -1141,6 +1141,10 @@ fn check_region_bounds_on_impl_item<'tcx>(
         return Ok(());
     }
 
+    if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
+        return Err(guar);
+    }
+
     let span = tcx
         .hir_get_generics(impl_m.def_id.expect_local())
         .expect("expected impl item to have generics or else we can't compare them")
@@ -1221,6 +1225,233 @@ fn check_region_bounds_on_impl_item<'tcx>(
     Err(reported)
 }
 
+#[allow(unused)]
+enum LateEarlyMismatch<'tcx> {
+    EarlyInImpl(DefId, DefId, ty::Region<'tcx>),
+    LateInImpl(DefId, DefId, ty::Region<'tcx>),
+}
+
+fn check_region_late_boundedness<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    impl_m: ty::AssocItem,
+    trait_m: ty::AssocItem,
+) -> Option<ErrorGuaranteed> {
+    if !impl_m.is_fn() {
+        return None;
+    }
+
+    let (infcx, param_env) = tcx
+        .infer_ctxt()
+        .build_with_typing_env(ty::TypingEnv::non_body_analysis(tcx, impl_m.def_id));
+
+    let impl_m_args = infcx.fresh_args_for_item(DUMMY_SP, impl_m.def_id);
+    let impl_m_sig = tcx.fn_sig(impl_m.def_id).instantiate(tcx, impl_m_args);
+    let impl_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, impl_m_sig);
+
+    let trait_m_args = infcx.fresh_args_for_item(DUMMY_SP, trait_m.def_id);
+    let trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_args);
+    let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_m_sig);
+
+    let ocx = ObligationCtxt::new(&infcx);
+
+    // Equate the signatures so that we can infer whether a late-bound param was present where
+    // an early-bound param was expected, since we replace the late-bound lifetimes with
+    // `ReLateParam`, and early-bound lifetimes with infer vars, so the early-bound args will
+    // resolve to `ReLateParam` if there is a mismatch.
+    let Ok(()) = ocx.eq(
+        &ObligationCause::dummy(),
+        param_env,
+        ty::Binder::dummy(trait_m_sig),
+        ty::Binder::dummy(impl_m_sig),
+    ) else {
+        return None;
+    };
+
+    let errors = ocx.select_where_possible();
+    if !errors.is_empty() {
+        return None;
+    }
+
+    let mut mismatched = vec![];
+
+    let impl_generics = tcx.generics_of(impl_m.def_id);
+    for (id_arg, arg) in
+        std::iter::zip(ty::GenericArgs::identity_for_item(tcx, impl_m.def_id), impl_m_args)
+    {
+        if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
+            && let ty::ReVar(vid) = r.kind()
+            && let r = infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_var(tcx, vid)
+            && let ty::ReLateParam(ty::LateParamRegion {
+                kind: ty::LateParamRegionKind::Named(trait_param_def_id, _),
+                ..
+            }) = r.kind()
+            && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
+        {
+            mismatched.push(LateEarlyMismatch::EarlyInImpl(
+                impl_generics.region_param(ebr, tcx).def_id,
+                trait_param_def_id,
+                id_arg.expect_region(),
+            ));
+        }
+    }
+
+    let trait_generics = tcx.generics_of(trait_m.def_id);
+    for (id_arg, arg) in
+        std::iter::zip(ty::GenericArgs::identity_for_item(tcx, trait_m.def_id), trait_m_args)
+    {
+        if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
+            && let ty::ReVar(vid) = r.kind()
+            && let r = infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_var(tcx, vid)
+            && let ty::ReLateParam(ty::LateParamRegion {
+                kind: ty::LateParamRegionKind::Named(impl_param_def_id, _),
+                ..
+            }) = r.kind()
+            && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
+        {
+            mismatched.push(LateEarlyMismatch::LateInImpl(
+                impl_param_def_id,
+                trait_generics.region_param(ebr, tcx).def_id,
+                id_arg.expect_region(),
+            ));
+        }
+    }
+
+    if mismatched.is_empty() {
+        return None;
+    }
+
+    let spans: Vec<_> = mismatched
+        .iter()
+        .map(|param| {
+            let (LateEarlyMismatch::EarlyInImpl(impl_param_def_id, ..)
+            | LateEarlyMismatch::LateInImpl(impl_param_def_id, ..)) = param;
+            tcx.def_span(impl_param_def_id)
+        })
+        .collect();
+
+    let mut diag = tcx
+        .dcx()
+        .struct_span_err(spans, "lifetime parameters do not match the trait definition")
+        .with_note("lifetime parameters differ in whether they are early- or late-bound")
+        .with_code(E0195);
+    for mismatch in mismatched {
+        match mismatch {
+            LateEarlyMismatch::EarlyInImpl(
+                impl_param_def_id,
+                trait_param_def_id,
+                early_bound_region,
+            ) => {
+                let mut multispan = MultiSpan::from_spans(vec![
+                    tcx.def_span(impl_param_def_id),
+                    tcx.def_span(trait_param_def_id),
+                ]);
+                multispan
+                    .push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
+                multispan
+                    .push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
+                multispan.push_span_label(
+                    tcx.def_span(impl_param_def_id),
+                    format!("`{}` is early-bound", tcx.item_name(impl_param_def_id)),
+                );
+                multispan.push_span_label(
+                    tcx.def_span(trait_param_def_id),
+                    format!("`{}` is late-bound", tcx.item_name(trait_param_def_id)),
+                );
+                if let Some(span) =
+                    find_region_in_predicates(tcx, impl_m.def_id, early_bound_region)
+                {
+                    multispan.push_span_label(
+                        span,
+                        format!(
+                            "this lifetime bound makes `{}` early-bound",
+                            tcx.item_name(impl_param_def_id)
+                        ),
+                    );
+                }
+                diag.span_note(
+                    multispan,
+                    format!(
+                        "`{}` differs between the trait and impl",
+                        tcx.item_name(impl_param_def_id)
+                    ),
+                );
+            }
+            LateEarlyMismatch::LateInImpl(
+                impl_param_def_id,
+                trait_param_def_id,
+                early_bound_region,
+            ) => {
+                let mut multispan = MultiSpan::from_spans(vec![
+                    tcx.def_span(impl_param_def_id),
+                    tcx.def_span(trait_param_def_id),
+                ]);
+                multispan
+                    .push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
+                multispan
+                    .push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
+                multispan.push_span_label(
+                    tcx.def_span(impl_param_def_id),
+                    format!("`{}` is late-bound", tcx.item_name(impl_param_def_id)),
+                );
+                multispan.push_span_label(
+                    tcx.def_span(trait_param_def_id),
+                    format!("`{}` is early-bound", tcx.item_name(trait_param_def_id)),
+                );
+                if let Some(span) =
+                    find_region_in_predicates(tcx, trait_m.def_id, early_bound_region)
+                {
+                    multispan.push_span_label(
+                        span,
+                        format!(
+                            "this lifetime bound makes `{}` early-bound",
+                            tcx.item_name(trait_param_def_id)
+                        ),
+                    );
+                }
+                diag.span_note(
+                    multispan,
+                    format!(
+                        "`{}` differs between the trait and impl",
+                        tcx.item_name(impl_param_def_id)
+                    ),
+                );
+            }
+        }
+    }
+
+    Some(diag.emit())
+}
+
+fn find_region_in_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    early_bound_region: ty::Region<'tcx>,
+) -> Option<Span> {
+    for (pred, span) in tcx.explicit_predicates_of(def_id).instantiate_identity(tcx) {
+        if pred.visit_with(&mut FindRegion(early_bound_region)).is_break() {
+            return Some(span);
+        }
+    }
+
+    struct FindRegion<'tcx>(ty::Region<'tcx>);
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindRegion<'tcx> {
+        type Result = ControlFlow<()>;
+        fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
+            if r == self.0 { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
+        }
+    }
+
+    None
+}
+
 #[instrument(level = "debug", skip(infcx))]
 fn extract_spans_for_error_reporting<'tcx>(
     infcx: &infer::InferCtxt<'tcx>,
diff --git a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs
index 141ad5bd2c4..7c0378e068b 100644
--- a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs
+++ b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs
@@ -17,11 +17,11 @@ pub trait Foo<'a, 't> {
 
 impl<'a, 't> Foo<'a, 't> for &'a isize {
     fn no_bound<'b:'a>(self, b: Inv<'b>) {
-        //~^ ERROR lifetime parameters or bounds on method `no_bound` do not match
+        //~^ ERROR lifetime parameters do not match the trait definition
     }
 
     fn has_bound<'b>(self, b: Inv<'b>) {
-        //~^ ERROR lifetime parameters or bounds on method `has_bound` do not match
+        //~^ ERROR lifetime parameters do not match the trait definition
     }
 
     fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
@@ -40,7 +40,7 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
     }
 
     fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
-        //~^ ERROR lifetime parameters or bounds on method `wrong_bound2` do not match the trait
+        //~^ ERROR lifetime parameters do not match the trait definition
     }
 
     fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {
diff --git a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
index 2afacd4cc95..207ca57af38 100644
--- a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
+++ b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
@@ -1,23 +1,48 @@
-error[E0195]: lifetime parameters or bounds on method `no_bound` do not match the trait declaration
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:19:16
+error[E0195]: lifetime parameters do not match the trait definition
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:19:17
    |
+LL |     fn no_bound<'b:'a>(self, b: Inv<'b>) {
+   |                 ^^
+   |
+   = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'b` differs between the trait and impl
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:10:17
+   |
+LL | pub trait Foo<'a, 't> {
+   | --------------------- in this trait...
 LL |     fn no_bound<'b>(self, b: Inv<'b>);
-   |                ---- lifetimes in impl do not match this method in trait
+   |                 ^^ `'b` is late-bound
 ...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+   | -------------------------------------- in this impl...
 LL |     fn no_bound<'b:'a>(self, b: Inv<'b>) {
-   |                ^^^^^^^ lifetimes do not match method in trait
+   |                 ^^ -- this lifetime bound makes `'b` early-bound
+   |                 |
+   |                 `'b` is early-bound
 
-error[E0195]: lifetime parameters or bounds on method `has_bound` do not match the trait declaration
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:23:17
+error[E0195]: lifetime parameters do not match the trait definition
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:23:18
+   |
+LL |     fn has_bound<'b>(self, b: Inv<'b>) {
+   |                  ^^
    |
+   = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'b` differs between the trait and impl
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:11:18
+   |
+LL | pub trait Foo<'a, 't> {
+   | --------------------- in this trait...
+LL |     fn no_bound<'b>(self, b: Inv<'b>);
 LL |     fn has_bound<'b:'a>(self, b: Inv<'b>);
-   |                 -------
-   |                 |   |
-   |                 |   this bound might be missing in the impl
-   |                 lifetimes in impl do not match this method in trait
+   |                  ^^ -- this lifetime bound makes `'b` early-bound
+   |                  |
+   |                  `'b` is early-bound
+...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+   | -------------------------------------- in this impl...
 ...
 LL |     fn has_bound<'b>(self, b: Inv<'b>) {
-   |                 ^^^^ lifetimes do not match method in trait
+   |                  ^^ `'b` is late-bound
 
 error[E0308]: method not compatible with trait
   --> $DIR/regions-bound-missing-bound-in-impl.rs:27:5
@@ -57,18 +82,45 @@ note: ...does not necessarily outlive the lifetime `'c` as defined here
 LL |     fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
    |                        ^^
 
-error[E0195]: lifetime parameters or bounds on method `wrong_bound2` do not match the trait declaration
-  --> $DIR/regions-bound-missing-bound-in-impl.rs:42:20
+error[E0195]: lifetime parameters do not match the trait definition
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:42:30
+   |
+LL |     fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
+   |                              ^^^             ^^^
    |
+   = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'_` differs between the trait and impl
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:13:21
+   |
+LL | pub trait Foo<'a, 't> {
+   | --------------------- in this trait...
+...
 LL |     fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
-   |                    ----------------
-   |                    |         |  |
-   |                    |         |  this bound might be missing in the impl
-   |                    |         this bound might be missing in the impl
-   |                    lifetimes in impl do not match this method in trait
+   |                     ^^          -- this lifetime bound makes `'b` early-bound
+   |                     |
+   |                     `'b` is early-bound
+...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+   | -------------------------------------- in this impl...
+...
+LL |     fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
+   |                              ^^^ `'_` is late-bound
+note: `'_` differs between the trait and impl
+  --> $DIR/regions-bound-missing-bound-in-impl.rs:13:27
+   |
+LL | pub trait Foo<'a, 't> {
+   | --------------------- in this trait...
+...
+LL |     fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
+   |                           ^^ -- this lifetime bound makes `'d` early-bound
+   |                           |
+   |                           `'d` is early-bound
+...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+   | -------------------------------------- in this impl...
 ...
 LL |     fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
-   |                    ^ lifetimes do not match method in trait
+   |                                              ^^^ `'_` is late-bound
 
 error[E0276]: impl has stricter requirements than trait
   --> $DIR/regions-bound-missing-bound-in-impl.rs:49:26
diff --git a/tests/ui/error-codes/E0195.rs b/tests/ui/error-codes/E0195.rs
index 8280901b1cd..66968f70bd9 100644
--- a/tests/ui/error-codes/E0195.rs
+++ b/tests/ui/error-codes/E0195.rs
@@ -1,14 +1,25 @@
 trait Trait {
+//~^ NOTE in this trait...
+//~| NOTE in this trait...
     fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
-    //~^ NOTE lifetimes in impl do not match this associated function in trait
+    //~^ NOTE `'a` is early-bound
+    //~| NOTE this lifetime bound makes `'a` early-bound
+    //~| NOTE `'b` is early-bound
+    //~| NOTE this lifetime bound makes `'b` early-bound
 }
 
 struct Foo;
 
 impl Trait for Foo {
-    fn bar<'a,'b>(x: &'a str, y: &'b str) { //~ ERROR E0195
-    //~^ NOTE lifetimes do not match associated function in trait
-    //~| NOTE this bound might be missing in the impl
+//~^ NOTE in this impl...
+//~| NOTE in this impl...
+    fn bar<'a,'b>(x: &'a str, y: &'b str) {
+    //~^ ERROR E0195
+    //~| NOTE `'a` differs between the trait and impl
+    //~| NOTE `'a` is late-bound
+    //~| NOTE `'b` differs between the trait and impl
+    //~| NOTE `'b` is late-bound
+    //~| NOTE lifetime parameters differ in whether they are early- or late-bound
     }
 }
 
diff --git a/tests/ui/error-codes/E0195.stderr b/tests/ui/error-codes/E0195.stderr
index 6eac910bedf..d0295b36434 100644
--- a/tests/ui/error-codes/E0195.stderr
+++ b/tests/ui/error-codes/E0195.stderr
@@ -1,14 +1,42 @@
-error[E0195]: lifetime parameters or bounds on associated function `bar` do not match the trait declaration
-  --> $DIR/E0195.rs:9:11
+error[E0195]: lifetime parameters do not match the trait definition
+  --> $DIR/E0195.rs:16:12
    |
+LL |     fn bar<'a,'b>(x: &'a str, y: &'b str) {
+   |            ^^ ^^
+   |
+   = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'a` differs between the trait and impl
+  --> $DIR/E0195.rs:4:12
+   |
+LL | trait Trait {
+   | ----------- in this trait...
+...
 LL |     fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
-   |           ----------
-   |           |      |
-   |           |      this bound might be missing in the impl
-   |           lifetimes in impl do not match this associated function in trait
+   |            ^^    -- this lifetime bound makes `'a` early-bound
+   |            |
+   |            `'a` is early-bound
+...
+LL | impl Trait for Foo {
+   | ------------------ in this impl...
+...
+LL |     fn bar<'a,'b>(x: &'a str, y: &'b str) {
+   |            ^^ `'a` is late-bound
+note: `'b` differs between the trait and impl
+  --> $DIR/E0195.rs:4:15
+   |
+LL | trait Trait {
+   | ----------- in this trait...
+...
+LL |     fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
+   |               ^^ -- this lifetime bound makes `'b` early-bound
+   |               |
+   |               `'b` is early-bound
+...
+LL | impl Trait for Foo {
+   | ------------------ in this impl...
 ...
 LL |     fn bar<'a,'b>(x: &'a str, y: &'b str) {
-   |           ^^^^^^^ lifetimes do not match associated function in trait
+   |               ^^ `'b` is late-bound
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr b/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
index e26cb22163f..f8a6252f991 100644
--- a/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
+++ b/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
@@ -10,26 +10,53 @@ LL |     fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
 LL |     fn foo<'a, K>(self, _: (), _: K) where {
    |           ^^^^^^^ lifetimes do not match method in trait
 
-error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
-  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:11
+error[E0195]: lifetime parameters do not match the trait definition
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:12
    |
-LL |     fn foo<'a>(&self, state: &'a State) -> &'a T
-   |           ---- lifetimes in impl do not match this method in trait
-LL |     where
-LL |         T: 'a;
-   |            -- this bound might be missing in the impl
-...
 LL |     fn foo<'a>(&self, state: &'a State) -> &'a T {
-   |           ^^^^ lifetimes do not match method in trait
+   |            ^^
+   |
+   = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'a` differs between the trait and impl
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:14:12
+   |
+LL |   trait Foo<T> {
+   |   ------------ in this trait...
+LL |       fn foo<'a>(&self, state: &'a State) -> &'a T
+   |              ^^ `'a` is early-bound
+LL |       where
+LL |           T: 'a;
+   |              -- this lifetime bound makes `'a` early-bound
+...
+LL | / impl<F, T> Foo<T> for F
+LL | | where
+LL | |     F: Fn(&State) -> &T,
+   | |________________________- in this impl...
+LL |   {
+LL |       fn foo<'a>(&self, state: &'a State) -> &'a T {
+   |              ^^ `'a` is late-bound
 
-error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
-  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:11
+error[E0195]: lifetime parameters do not match the trait definition
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:12
+   |
+LL |     fn foo<'a: 'a>(&'a self) {}
+   |            ^^
+   |
+   = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'a` differs between the trait and impl
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:29:12
    |
+LL | trait Bar {
+   | --------- in this trait...
 LL |     fn foo<'a>(&'a self) {}
-   |           ---- lifetimes in impl do not match this method in trait
+   |            ^^ `'a` is late-bound
 ...
+LL | impl Bar for () {
+   | --------------- in this impl...
 LL |     fn foo<'a: 'a>(&'a self) {}
-   |           ^^^^^^^^ lifetimes do not match method in trait
+   |            ^^  -- this lifetime bound makes `'a` early-bound
+   |            |
+   |            `'a` is early-bound
 
 error: aborting due to 3 previous errors