diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 298bff9984444..845bbdca96aba 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -402,39 +402,29 @@ fn check_opaque_meets_bounds<'tcx>(
         let guar = infcx.err_ctxt().report_fulfillment_errors(errors);
         return Err(guar);
     }
-    match origin {
-        // Checked when type checking the function containing them.
-        hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
-            // HACK: this should also fall through to the hidden type check below, but the original
-            // implementation had a bug where equivalent lifetimes are not identical. This caused us
-            // to reject existing stable code that is otherwise completely fine. The real fix is to
-            // compare the hidden types via our type equivalence/relation infra instead of doing an
-            // identity check.
-            let _ = infcx.take_opaque_types();
-            return Ok(());
-        }
-        // Nested opaque types occur only in associated types:
-        // ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
-        // They can only be referenced as `<Opaque<T> as Trait<&'static T>>::AssocTy`.
-        // We don't have to check them here because their well-formedness follows from the WF of
-        // the projection input types in the defining- and use-sites.
-        hir::OpaqueTyOrigin::TyAlias { .. }
-            if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
-        // Can have different predicates to their defining use
-        hir::OpaqueTyOrigin::TyAlias { .. } => {
-            let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
-            let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
-            let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
-            ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
+
+    let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?;
+    let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
+    let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
+    ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
+
+    if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
+        // HACK: this should also fall through to the hidden type check below, but the original
+        // implementation had a bug where equivalent lifetimes are not identical. This caused us
+        // to reject existing stable code that is otherwise completely fine. The real fix is to
+        // compare the hidden types via our type equivalence/relation infra instead of doing an
+        // identity check.
+        let _ = infcx.take_opaque_types();
+        Ok(())
+    } else {
+        // Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
+        for (mut key, mut ty) in infcx.take_opaque_types() {
+            ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
+            key = infcx.resolve_vars_if_possible(key);
+            sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
         }
+        Ok(())
     }
-    // Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
-    for (mut key, mut ty) in infcx.take_opaque_types() {
-        ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
-        key = infcx.resolve_vars_if_possible(key);
-        sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
-    }
-    Ok(())
 }
 
 fn sanity_check_found_hidden_type<'tcx>(
@@ -1549,6 +1539,8 @@ fn opaque_type_cycle_error(
     err.emit()
 }
 
+// FIXME(@lcnr): This should not be computed per coroutine, but instead once for
+// each typeck root.
 pub(super) fn check_coroutine_obligations(
     tcx: TyCtxt<'_>,
     def_id: LocalDefId,
@@ -1556,7 +1548,7 @@ pub(super) fn check_coroutine_obligations(
     debug_assert!(tcx.is_coroutine(def_id.to_def_id()));
 
     let typeck = tcx.typeck(def_id);
-    let param_env = tcx.param_env(def_id);
+    let param_env = tcx.param_env(typeck.hir_owner.def_id);
 
     let coroutine_interior_predicates = &typeck.coroutine_interior_predicates[&def_id];
     debug!(?coroutine_interior_predicates);
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 68b6a2f62fb63..b6f3c38cb3f01 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -592,8 +592,25 @@ impl<'tcx> InferCtxt<'tcx> {
         obligations: &mut Vec<PredicateObligation<'tcx>>,
     ) {
         let tcx = self.tcx;
-        let item_bounds = tcx.explicit_item_bounds(def_id);
+        // Require that the hidden type is well-formed. We have to
+        // make sure we wf-check the hidden type to fix #114728.
+        //
+        // However, we don't check that all types are well-formed.
+        // We only do so for types provided by the user or if they are
+        // "used", e.g. for method selection.
+        //
+        // This means we never check the wf requirements of the hidden
+        // type during MIR borrowck, causing us to infer the wrong
+        // lifetime for its member constraints which then results in
+        // unexpected region errors.
+        obligations.push(traits::Obligation::new(
+            tcx,
+            cause.clone(),
+            param_env,
+            ty::ClauseKind::WellFormed(hidden_ty.into()),
+        ));
 
+        let item_bounds = tcx.explicit_item_bounds(def_id);
         for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) {
             let predicate = predicate.fold_with(&mut BottomUpFolder {
                 tcx,
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index 3f9bd509b087c..191671bcc1edd 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -121,18 +121,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
             }
         }
         DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
-        DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
-            DefKind::TyAlias => ty::List::empty(),
-            DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
-            // Nested opaque types only occur in associated types:
-            // ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
-            // assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
-            // and `&'static T`.
-            DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"),
-            def_kind => {
-                bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
-            }
-        },
+        DefKind::OpaqueTy => bug!("implied bounds are not defined for opaques"),
         DefKind::Mod
         | DefKind::Struct
         | DefKind::Union
diff --git a/tests/ui/closures/issue-78720.rs b/tests/ui/closures/issue-78720.rs
index 0e1f78ae3c69b..0c4f337ba57b8 100644
--- a/tests/ui/closures/issue-78720.rs
+++ b/tests/ui/closures/issue-78720.rs
@@ -1,7 +1,7 @@
 fn server() -> impl {
-//~^ ERROR at least one trait must be specified
+    //~^ ERROR at least one trait must be specified
+    //~| ERROR type annotations needed
     ().map2(|| "")
-    //~^ ERROR type annotations needed
 }
 
 trait FilterBase2 {
diff --git a/tests/ui/closures/issue-78720.stderr b/tests/ui/closures/issue-78720.stderr
index d8d3811af5a74..2f57c7616f121 100644
--- a/tests/ui/closures/issue-78720.stderr
+++ b/tests/ui/closures/issue-78720.stderr
@@ -23,10 +23,10 @@ LL | struct Map2<Segment2, F> {
    |                     +++
 
 error[E0282]: type annotations needed
-  --> $DIR/issue-78720.rs:3:5
+  --> $DIR/issue-78720.rs:1:16
    |
-LL |     ().map2(|| "")
-   |     ^^^^^^^^^^^^^^ cannot infer type
+LL | fn server() -> impl {
+   |                ^^^^ cannot infer type
 
 error[E0308]: mismatched types
   --> $DIR/issue-78720.rs:8:39
diff --git a/tests/ui/impl-trait/issues/issue-86800.rs b/tests/ui/impl-trait/issues/issue-86800.rs
index ae6e198c2ad46..172ab04f58df4 100644
--- a/tests/ui/impl-trait/issues/issue-86800.rs
+++ b/tests/ui/impl-trait/issues/issue-86800.rs
@@ -1,12 +1,6 @@
 #![feature(type_alias_impl_trait)]
 
 //@ edition:2021
-//@ compile-flags:-Z treat-err-as-bug=2
-//@ error-pattern: due to `-Z treat-err-as-bug=2
-//@ failure-status:101
-//@ normalize-stderr-test ".*note: .*\n\n" -> ""
-//@ normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> ""
-//@ rustc-env:RUST_BACKTRACE=0
 
 use std::future::Future;
 
@@ -29,6 +23,7 @@ struct Context {
 type TransactionResult<O> = Result<O, ()>;
 
 type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
+//~^ ERROR unconstrained opaque type
 
 fn execute_transaction_fut<'f, F, O>(
     f: F,
@@ -37,6 +32,7 @@ where
     F: FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> + 'f
 {
     f
+    //~^ ERROR expected generic lifetime parameter, found `'_`
 }
 
 impl Context {
@@ -44,6 +40,7 @@ impl Context {
         &self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>
     ) -> TransactionResult<O>
     {
+        //~^ ERROR expected generic lifetime parameter, found `'_`
         let mut conn = Connection {};
         let mut transaction = TestTransaction { conn: &mut conn };
         f(&mut transaction).await
diff --git a/tests/ui/impl-trait/issues/issue-86800.stderr b/tests/ui/impl-trait/issues/issue-86800.stderr
index 7af4846a9593f..146d2f67942be 100644
--- a/tests/ui/impl-trait/issues/issue-86800.stderr
+++ b/tests/ui/impl-trait/issues/issue-86800.stderr
@@ -1,11 +1,13 @@
 error: unconstrained opaque type
-  --> $DIR/issue-86800.rs:31:34
+  --> $DIR/issue-86800.rs:25:34
    |
 LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-error: internal compiler error[E0792]: expected generic lifetime parameter, found `'_`
-  --> $DIR/issue-86800.rs:39:5
+   = note: `TransactionFuture` must be used in combination with a concrete type within the same module
+
+error[E0792]: expected generic lifetime parameter, found `'_`
+  --> $DIR/issue-86800.rs:34:5
    |
 LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
    |                        --- this generic parameter must be used with a generic lifetime parameter
@@ -13,9 +15,20 @@ LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResu
 LL |     f
    |     ^
 
-error: the compiler unexpectedly panicked. this is a bug.
+error[E0792]: expected generic lifetime parameter, found `'_`
+  --> $DIR/issue-86800.rs:42:5
+   |
+LL |   type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
+   |                          --- this generic parameter must be used with a generic lifetime parameter
+...
+LL | /     {
+LL | |
+LL | |         let mut conn = Connection {};
+LL | |         let mut transaction = TestTransaction { conn: &mut conn };
+LL | |         f(&mut transaction).await
+LL | |     }
+   | |_____^
+
+error: aborting due to 3 previous errors
 
-query stack during panic:
-#0 [mir_borrowck] borrow-checking `execute_transaction_fut`
-#1 [type_of_opaque] computing type of opaque `execute_transaction_fut::{opaque#0}`
-end of query stack
+For more information about this error, try `rustc --explain E0792`.
diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr b/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr
index fee3b86034af1..4a5e4bfe94b56 100644
--- a/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr
+++ b/tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr
@@ -1,9 +1,9 @@
 error[E0282]: type annotations needed
-  --> $DIR/recursive-coroutine-boxed.rs:10:23
+  --> $DIR/recursive-coroutine-boxed.rs:11:23
    |
 LL |         let mut gen = Box::pin(foo());
    |                       ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
-...
+LL |
 LL |         let mut r = gen.as_mut().resume(());
    |                         ------ type must be known at this point
    |
@@ -13,10 +13,10 @@ LL |         let mut gen = Box::<T>::pin(foo());
    |                          +++++
 
 error[E0282]: type annotations needed
-  --> $DIR/recursive-coroutine-boxed.rs:10:32
+  --> $DIR/recursive-coroutine-boxed.rs:8:13
    |
-LL |         let mut gen = Box::pin(foo());
-   |                                ^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
+LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/impl-trait/recursive-coroutine-boxed.rs b/tests/ui/impl-trait/recursive-coroutine-boxed.rs
index a42ae68f28e97..8f0bbb400cff0 100644
--- a/tests/ui/impl-trait/recursive-coroutine-boxed.rs
+++ b/tests/ui/impl-trait/recursive-coroutine-boxed.rs
@@ -6,10 +6,10 @@
 use std::ops::{Coroutine, CoroutineState};
 
 fn foo() -> impl Coroutine<Yield = (), Return = ()> {
+    //[next]~^ ERROR type annotations needed
     || {
         let mut gen = Box::pin(foo());
         //[next]~^ ERROR type annotations needed
-        //[next]~| ERROR type annotations needed
         let mut r = gen.as_mut().resume(());
         while let CoroutineState::Yielded(v) = r {
             yield v;
diff --git a/tests/ui/impl-trait/wf-check-hidden-type.rs b/tests/ui/impl-trait/wf-check-hidden-type.rs
new file mode 100644
index 0000000000000..c3b1182a98f48
--- /dev/null
+++ b/tests/ui/impl-trait/wf-check-hidden-type.rs
@@ -0,0 +1,20 @@
+//! Regression test for #114728.
+
+trait Extend<'a, 'b> {
+    fn extend(self, _: &'a str) -> &'b str;
+}
+
+impl<'a, 'b> Extend<'a, 'b> for Option<&'b &'a ()> {
+    fn extend(self, s: &'a str) -> &'b str {
+        s
+    }
+}
+
+fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
+    None::<&'_ &'_ ()> //~ ERROR lifetime may not live long enough
+}
+
+fn main() {
+    let y = boom().extend(&String::from("temporary"));
+    println!("{}", y);
+}
diff --git a/tests/ui/impl-trait/wf-check-hidden-type.stderr b/tests/ui/impl-trait/wf-check-hidden-type.stderr
new file mode 100644
index 0000000000000..86ba7aff54ada
--- /dev/null
+++ b/tests/ui/impl-trait/wf-check-hidden-type.stderr
@@ -0,0 +1,14 @@
+error: lifetime may not live long enough
+  --> $DIR/wf-check-hidden-type.rs:14:5
+   |
+LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
+   |         --  -- lifetime `'b` defined here
+   |         |
+   |         lifetime `'a` defined here
+LL |     None::<&'_ &'_ ()>
+   |     ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+   |
+   = help: consider adding the following bound: `'a: 'b`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs
index 03913869503c5..85eeb5d4c901e 100644
--- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs
+++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.rs
@@ -6,6 +6,7 @@ use std::future::Future;
 async fn wrapper<F>(f: F)
 //~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
 //~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
+//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
 where
 F:,
 for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
diff --git a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr
index 89ebdb57f3c31..578ba149baf3f 100644
--- a/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr
+++ b/tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr
@@ -4,6 +4,7 @@ error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
 LL | / async fn wrapper<F>(f: F)
 LL | |
 LL | |
+LL | |
 LL | | where
 LL | | F:,
 LL | | for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
@@ -20,7 +21,21 @@ LL | async fn wrapper<F>(f: F)
    = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
 
 error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
-  --> $DIR/issue-76168-hr-outlives-3.rs:12:1
+  --> $DIR/issue-76168-hr-outlives-3.rs:6:1
+   |
+LL | / async fn wrapper<F>(f: F)
+LL | |
+LL | |
+LL | |
+LL | | where
+LL | | F:,
+LL | | for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
+   | |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32`
+   |
+   = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
+
+error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
+  --> $DIR/issue-76168-hr-outlives-3.rs:13:1
    |
 LL | / {
 LL | |
@@ -31,6 +46,6 @@ LL | | }
    |
    = help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr
index 3a4415ed23aca..6c259621466d3 100644
--- a/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr
+++ b/tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr
@@ -21,6 +21,20 @@ help: consider adding an explicit lifetime bound
 LL |     for<F> F: 'a, !1_"F": 'a
    |                 ~~~~~~~~~~~~
 
-error: aborting due to 1 previous error; 1 warning emitted
+error[E0309]: the placeholder type `!2_"F"` may not live long enough
+  --> $DIR/type-match-with-late-bound.rs:11:1
+   |
+LL | async fn walk2<'a, T: 'a>(_: T)
+   |                -- the placeholder type `!2_"F"` must be valid for the lifetime `'a` as defined here...
+...
+LL | {}
+   | ^^ ...so that the type `F` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL |     for<F> F: 'a, !2_"F": 'a
+   |                 ~~~~~~~~~~~~
+
+error: aborting due to 2 previous errors; 1 warning emitted
 
 For more information about this error, try `rustc --explain E0309`.
diff --git a/tests/ui/type-alias-impl-trait/issue-90400-2.stderr b/tests/ui/type-alias-impl-trait/issue-90400-2.stderr
index 5e978e97d6b50..b4b78f8175fa2 100644
--- a/tests/ui/type-alias-impl-trait/issue-90400-2.stderr
+++ b/tests/ui/type-alias-impl-trait/issue-90400-2.stderr
@@ -2,15 +2,13 @@ error[E0277]: the trait bound `B: Bar` is not satisfied
   --> $DIR/issue-90400-2.rs:25:9
    |
 LL |         MyBaz(bar)
-   |         ^^^^^^^^^^ the trait `Bar` is not implemented for `B`, which is required by `MyBaz<B>: Baz`
+   |         ^^^^^^^^^^ the trait `Bar` is not implemented for `B`
    |
-note: required for `MyBaz<B>` to implement `Baz`
-  --> $DIR/issue-90400-2.rs:30:14
+note: required by a bound in `MyBaz`
+  --> $DIR/issue-90400-2.rs:29:17
    |
-LL | impl<B: Bar> Baz for MyBaz<B> {
-   |         ---  ^^^     ^^^^^^^^
-   |         |
-   |         unsatisfied trait bound introduced here
+LL | struct MyBaz<B: Bar>(B);
+   |                 ^^^ required by this bound in `MyBaz`
 help: consider restricting type parameter `B`
    |
 LL |     type FooFn<B: Bar> = impl Baz;
diff --git a/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs b/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs
new file mode 100644
index 0000000000000..19dd4c1793677
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/wf-check-definition-site.rs
@@ -0,0 +1,40 @@
+//@ check-pass
+
+// Regression test for #114572, We were inferring an ill-formed type:
+//
+// `Opaque<'a> = Static<&'a str>`, vs
+// `Opaque<'a> = Static<&'static str>`.
+//
+// The hidden type of the opaque ends up as `Static<'?0 str>`. When
+// computing member constraints we end up choosing `'a` for `?0` unless
+// `?0` is already required to outlive `'a`. We achieve this by checking
+// that `Static<'?0 str>` is well-formed.
+#![feature(type_alias_impl_trait)]
+
+struct Static<T: 'static>(T);
+
+type OpaqueRet<'a> = impl Sized + 'a;
+fn test_return<'a>(msg: Static<&'static u8>) -> OpaqueRet<'a> {
+    msg
+}
+
+fn test_rpit<'a>(msg: Static<&'static u8>) -> impl Sized + 'a {
+    msg
+}
+
+type OpaqueAssign<'a> = impl Sized + 'a;
+fn test_assign<'a>(msg: Static<&'static u8>) -> Option<OpaqueAssign<'a>> {
+    let _: OpaqueAssign<'a> = msg;
+    None
+}
+
+// `OpaqueRef<'a, T> = Ref<'a, T>`, vs
+// `OpaqueRef<'a, T> = Ref<'static, T>`.
+trait RefAt<'a>: 'a {}
+struct Ref<'a, T: RefAt<'a>>(&'a T);
+type OpaqueRef<'a, T: RefAt<'static>> = impl Sized + 'a;
+fn test_trait<'a, T: RefAt<'static>>(msg: Ref<'static, T>) -> OpaqueRef<'a, T> {
+    msg
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
index 17c1f8897bf0c..79b726f83dde0 100644
--- a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
+++ b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr
@@ -1,21 +1,16 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/wf-nested.rs:57:27
+  --> $DIR/wf-nested.rs:64:38
    |
-LL |     type InnerOpaque<T> = impl Sized;
-   |                           ^^^^^^^^^^
-   |                           |
-   |                           the parameter type `T` must be valid for the static lifetime...
-   |                           ...so that the type `T` will meet its required lifetime bounds...
+LL |     fn define<T>() -> OuterOpaque<T> {}
+   |                                      ^^
+   |                                      |
+   |                                      the parameter type `T` must be valid for the static lifetime...
+   |                                      ...so that the type `T` will meet its required lifetime bounds
    |
-note: ...that is required by this bound
-  --> $DIR/wf-nested.rs:12:20
-   |
-LL | struct IsStatic<T: 'static>(T);
-   |                    ^^^^^^^
 help: consider adding an explicit lifetime bound
    |
-LL |     type InnerOpaque<T: 'static> = impl Sized;
-   |                       +++++++++
+LL |     fn define<T: 'static>() -> OuterOpaque<T> {}
+   |                +++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr
new file mode 100644
index 0000000000000..b61b69d8e407e
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr
@@ -0,0 +1,31 @@
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/wf-nested.rs:34:38
+   |
+LL |     fn define<T>() -> OuterOpaque<T> {}
+   |                                      ^^
+   |                                      |
+   |                                      the parameter type `T` must be valid for the static lifetime...
+   |                                      ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL |     fn define<T: 'static>() -> OuterOpaque<T> {}
+   |                +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/wf-nested.rs:37:69
+   |
+LL |     fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
+   |                                                                     ^^
+   |                                                                     |
+   |                                                                     the parameter type `T` must be valid for the static lifetime...
+   |                                                                     ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL |     fn define_rpit<T: 'static>() -> impl Trait<&'static T, Out = impl Sized> {}
+   |                     +++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0310`.
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
index f5d3a218542cd..dbd3a1394f89b 100644
--- a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
+++ b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr
@@ -1,5 +1,19 @@
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/wf-nested.rs:46:17
+  --> $DIR/wf-nested.rs:46:38
+   |
+LL |     fn define<T>() -> OuterOpaque<T> {}
+   |                                      ^^
+   |                                      |
+   |                                      the parameter type `T` must be valid for the static lifetime...
+   |                                      ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL |     fn define<T: 'static>() -> OuterOpaque<T> {}
+   |                +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/wf-nested.rs:51:17
    |
 LL |         let _ = outer.get();
    |                 ^^^^^^^^^^^
@@ -13,7 +27,7 @@ LL |     fn test<T: 'static>() {
    |              +++++++++
 
 error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/wf-nested.rs:46:17
+  --> $DIR/wf-nested.rs:51:17
    |
 LL |         let _ = outer.get();
    |                 ^^^^^^^^^^^
@@ -27,6 +41,6 @@ help: consider adding an explicit lifetime bound
 LL |     fn test<T: 'static>() {
    |              +++++++++
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0310`.
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.rs b/tests/ui/type-alias-impl-trait/wf-nested.rs
index 1fc93a3cd2793..56c524c6db066 100644
--- a/tests/ui/type-alias-impl-trait/wf-nested.rs
+++ b/tests/ui/type-alias-impl-trait/wf-nested.rs
@@ -1,12 +1,8 @@
 // Well-formedness of nested opaque types, i.e. `impl Sized` in
-// `type Outer = impl Trait<Assoc = impl Sized>`.
-// See the comments below.
-//
-//@ revisions: pass pass_sound fail
-//@ [pass] check-pass
-//@ [pass_sound] check-fail
-//@ [fail] check-fail
-
+// `type Outer = impl Trait<Assoc = impl Sized>`. We check that
+// the nested type is well-formed, even though this would also
+// be implied by the item bounds of the opaque being
+// well-formed. See the comments below.
 #![feature(type_alias_impl_trait)]
 
 struct IsStatic<T: 'static>(T);
@@ -23,40 +19,26 @@ impl<T> Trait<&'static T> for () {
     type Out = IsStatic<T>;
 }
 
-// The hidden type for `impl Sized` is `IsStatic<T>`, which requires `T: 'static`.
-// We know it is well-formed because it can *only* be referenced as a projection:
-// <OuterOpaque<T> as Trait<&'static T>>::Out`.
-// So any instantiation of the type already requires proving `T: 'static`.
-#[cfg(pass)]
-mod pass {
-    use super::*;
-    type OuterOpaque<T> = impl Trait<&'static T, Out = impl Sized>;
-    fn define<T>() -> OuterOpaque<T> {}
-}
-
-// Test the soundness of `pass` - We should require `T: 'static` at the use site.
-#[cfg(pass_sound)]
-mod pass_sound {
-    use super::*;
-    type OuterOpaque<T> = impl Trait<&'static T, Out = impl Sized>;
-    fn define<T>() -> OuterOpaque<T> {}
-
-    fn test<T>() {
-        let outer = define::<T>();
-        let _ = outer.get();
-        //[pass_sound]~^ ERROR `T` may not live long enough
-        //[pass_sound]~| ERROR `T` may not live long enough
-    }
-}
 
-// Similar to `pass` but here `impl Sized` can be referenced directly as
-// InnerOpaque<T>, so we require an explicit bound `T: 'static`.
-#[cfg(fail)]
-mod fail {
-    use super::*;
-    type InnerOpaque<T> = impl Sized; //[fail]~ ERROR `T` may not live long enough
-    type OuterOpaque<T> = impl Trait<&'static T, Out = InnerOpaque<T>>;
-    fn define<T>() -> OuterOpaque<T> {}
-}
+// We could theoretically allow this (and previously did), as even
+// though the nested opaque is not well-formed, it can only be
+// used by normalizing the projection
+//    <OuterOpaque1<T> as Trait<&'static T>>::Out
+// Assuming that we check that this projection is well-formed, the wf
+// of the nested opaque is implied.
+type OuterOpaque1<T> = impl Trait<&'static T, Out = impl Sized>;
+fn define<T>() -> OuterOpaque1<T> {}
+//~^ ERROR `T` may not live long enough
+
+fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
+//~^ ERROR the parameter type `T` may not live long enough
+
+// Similar to `define` but here `impl Sized` can be referenced directly as
+// InnerOpaque<T>, so the `'static` bound is definitely required for
+// soundness.
+type InnerOpaque<T> = impl Sized;
+type OuterOpaque2<T> = impl Trait<&'static T, Out = InnerOpaque<T>>;
+fn define_nested_rpit<T>() -> OuterOpaque2<T> {}
+//~^ ERROR the parameter type `T` may not live long enough
 
 fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/wf-nested.stderr b/tests/ui/type-alias-impl-trait/wf-nested.stderr
new file mode 100644
index 0000000000000..6d50e11c5da84
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/wf-nested.stderr
@@ -0,0 +1,45 @@
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/wf-nested.rs:30:35
+   |
+LL | fn define<T>() -> OuterOpaque1<T> {}
+   |                                   ^^
+   |                                   |
+   |                                   the parameter type `T` must be valid for the static lifetime...
+   |                                   ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL | fn define<T: 'static>() -> OuterOpaque1<T> {}
+   |            +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/wf-nested.rs:33:65
+   |
+LL | fn define_rpit<T>() -> impl Trait<&'static T, Out = impl Sized> {}
+   |                                                                 ^^
+   |                                                                 |
+   |                                                                 the parameter type `T` must be valid for the static lifetime...
+   |                                                                 ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL | fn define_rpit<T: 'static>() -> impl Trait<&'static T, Out = impl Sized> {}
+   |                 +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/wf-nested.rs:41:47
+   |
+LL | fn define_nested_rpit<T>() -> OuterOpaque2<T> {}
+   |                                               ^^
+   |                                               |
+   |                                               the parameter type `T` must be valid for the static lifetime...
+   |                                               ...so that the type `T` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL | fn define_nested_rpit<T: 'static>() -> OuterOpaque2<T> {}
+   |                        +++++++++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0310`.