diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index 167960918308c..9fab7ad914a84 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -6,7 +6,7 @@ use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
 use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::TypeVisitable;
+use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{Span, DUMMY_SP};
 
@@ -109,23 +109,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
                 self.add_outlives(r1_vid, r2_vid);
             }
 
-            GenericArgKind::Type(mut t1) => {
+            GenericArgKind::Type(t1) => {
                 // we don't actually use this for anything, but
                 // the `TypeOutlives` code needs an origin.
                 let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
 
-                // Placeholder regions need to be converted now because it may
-                // create new region variables, which can't be done later when
-                // verifying these bounds.
-                if t1.has_placeholders() {
-                    t1 = tcx.fold_regions(t1, |r, _| match *r {
-                        ty::RePlaceholder(placeholder) => {
-                            self.constraints.placeholder_region(self.infcx, placeholder)
-                        }
-                        _ => r,
-                    });
-                }
-
                 TypeOutlives::new(
                     &mut *self,
                     tcx,
@@ -143,6 +131,25 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         }
     }
 
+    /// Placeholder regions need to be converted eagerly because it may
+    /// create new region variables, which we must not do when verifying
+    /// our region bounds.
+    ///
+    /// FIXME: This should get removed once higher ranked region obligations
+    /// are dealt with during trait solving.
+    fn replace_placeholders_with_nll<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
+        if value.has_placeholders() {
+            self.tcx.fold_regions(value, |r, _| match *r {
+                ty::RePlaceholder(placeholder) => {
+                    self.constraints.placeholder_region(self.infcx, placeholder)
+                }
+                _ => r,
+            })
+        } else {
+            value
+        }
+    }
+
     fn verify_to_type_test(
         &mut self,
         generic_kind: GenericKind<'tcx>,
@@ -150,7 +157,6 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
         verify_bound: VerifyBound<'tcx>,
     ) -> TypeTest<'tcx> {
         let lower_bound = self.to_region_vid(region);
-
         TypeTest { generic_kind, lower_bound, locations: self.locations, verify_bound }
     }
 
@@ -198,6 +204,8 @@ impl<'a, 'b, 'tcx> TypeOutlivesDelegate<'tcx> for &'a mut ConstraintConversion<'
         a: ty::Region<'tcx>,
         bound: VerifyBound<'tcx>,
     ) {
+        let kind = self.replace_placeholders_with_nll(kind);
+        let bound = self.replace_placeholders_with_nll(bound);
         let type_test = self.verify_to_type_test(kind, a, bound);
         self.add_type_test(type_test);
     }
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index cc0318ede54a0..74655369faf03 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -242,10 +242,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
         let constraint_sets: Vec<_> = unnormalized_input_output_tys
             .flat_map(|ty| {
                 debug!("build: input_or_output={:?}", ty);
-                // We only add implied bounds for the normalized type as the unnormalized
-                // type may not actually get checked by the caller.
-                //
-                // Can otherwise be unsound, see #91068.
+                // We add implied bounds from both the unnormalized and normalized ty.
+                // See issue #87748
+                let constraints_implied1 = self.add_implied_bounds(ty);
                 let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self
                     .param_env
                     .and(type_op::normalize::Normalize::new(ty))
@@ -273,9 +272,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                 // }
                 // ```
                 // Both &Self::Bar and &() are WF
-                let constraints_implied = self.add_implied_bounds(norm_ty);
+                let constraints_implied2 =
+                    if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None };
                 normalized_inputs_and_output.push(norm_ty);
-                constraints1.into_iter().chain(constraints_implied)
+                constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2)
             })
             .collect();
 
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index d32b1edcd8fd7..7bf7f7357bf4f 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1448,9 +1448,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     ))
                 });
                 debug!(?sig);
-                let sig = self.normalize(sig, term_location);
-                self.check_call_dest(body, term, &sig, *destination, target, term_location);
-
+                // IMPORTANT: We have to prove well formed for the function signature before
+                // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc`
+                // get normalized away, causing us to ignore the `'b: 'a` bound used by the function.
+                //
+                // Normalization results in a well formed type if the input is well formed, so we
+                // don't have to check it twice.
+                //
+                // See #91068 for an example.
                 self.prove_predicates(
                     sig.inputs_and_output
                         .iter()
@@ -1458,6 +1463,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     term_location.to_locations(),
                     ConstraintCategory::Boring,
                 );
+                let sig = self.normalize(sig, term_location);
+                self.check_call_dest(body, term, &sig, *destination, target, term_location);
 
                 // The ordinary liveness rules will ensure that all
                 // regions in the type of the callee are live here. We
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index 0d4472a1cfd9c..780e6ead10efa 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -187,7 +187,7 @@ pub enum GenericKind<'tcx> {
 /// }
 /// ```
 /// This is described with an `AnyRegion('a, 'b)` node.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, TypeFoldable, TypeVisitable)]
 pub enum VerifyBound<'tcx> {
     /// See [`VerifyIfEq`] docs
     IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>),
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 79309097e785b..a7833ab64310f 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -594,6 +594,29 @@ impl<'tcx> Predicate<'tcx> {
         }
         self
     }
+
+    /// Whether this projection can be soundly normalized.
+    ///
+    /// Wf predicates must not be normalized, as normalization
+    /// can remove required bounds which would cause us to
+    /// unsoundly accept some programs. See #91068.
+    #[inline]
+    pub fn allow_normalization(self) -> bool {
+        match self.kind().skip_binder() {
+            PredicateKind::WellFormed(_) => false,
+            PredicateKind::Trait(_)
+            | PredicateKind::RegionOutlives(_)
+            | PredicateKind::TypeOutlives(_)
+            | PredicateKind::Projection(_)
+            | PredicateKind::ObjectSafe(_)
+            | PredicateKind::ClosureKind(_, _, _)
+            | PredicateKind::Subtype(_)
+            | PredicateKind::Coerce(_)
+            | PredicateKind::ConstEvaluatable(_)
+            | PredicateKind::ConstEquate(_, _)
+            | PredicateKind::TypeWellFormedFromEnv(_) => true,
+        }
+    }
 }
 
 impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index adf47ece69d99..74625cc7bb750 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -619,6 +619,15 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
             constant.eval(self.selcx.tcx(), self.param_env)
         }
     }
+
+    #[inline]
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+            p.super_fold_with(self)
+        } else {
+            p
+        }
+    }
 }
 
 pub struct BoundVarReplacer<'me, 'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 449d7a7b47b1f..38b3a4b7253da 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -351,4 +351,16 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
             mir::ConstantKind::Val(_, _) => constant.try_super_fold_with(self)?,
         })
     }
+
+    #[inline]
+    fn try_fold_predicate(
+        &mut self,
+        p: ty::Predicate<'tcx>,
+    ) -> Result<ty::Predicate<'tcx>, Self::Error> {
+        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+            p.try_super_fold_with(self)
+        } else {
+            Ok(p)
+        }
+    }
 }
diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs
index 666498403c4f7..15a995ae59a8b 100644
--- a/compiler/rustc_typeck/src/check/compare_method.rs
+++ b/compiler/rustc_typeck/src/check/compare_method.rs
@@ -264,8 +264,13 @@ fn compare_predicate_entailment<'tcx>(
 
         let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
         let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
+        // Next, add all inputs and output as well-formed tys. Importantly,
+        // we have to do this before normalization, since the normalized ty may
+        // not contain the input parameters. See issue #87748.
+        wf_tys.extend(trait_sig.inputs_and_output.iter());
         let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
-        // Add the resulting inputs and output as well-formed.
+        // We also have to add the normalized trait signature
+        // as we don't normalize during implied bounds computation.
         wf_tys.extend(trait_sig.inputs_and_output.iter());
         let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
 
diff --git a/src/test/ui/associated-types/issue-59324.rs b/src/test/ui/associated-types/issue-59324.rs
index 162f9e00edd81..9e68e9e77515b 100644
--- a/src/test/ui/associated-types/issue-59324.rs
+++ b/src/test/ui/associated-types/issue-59324.rs
@@ -15,9 +15,9 @@ pub trait ThriftService<Bug: NotFoo>:
 {
     fn get_service(
     //~^ ERROR the trait bound `Bug: Foo` is not satisfied
+    //~| ERROR the trait bound `Bug: Foo` is not satisfied
         &self,
     ) -> Self::AssocType;
-    //~^ the trait bound `Bug: Foo` is not satisfied
 }
 
 fn with_factory<H>(factory: dyn ThriftService<()>) {}
diff --git a/src/test/ui/associated-types/issue-59324.stderr b/src/test/ui/associated-types/issue-59324.stderr
index a84b599b52b68..dd5ec7175b5f1 100644
--- a/src/test/ui/associated-types/issue-59324.stderr
+++ b/src/test/ui/associated-types/issue-59324.stderr
@@ -20,7 +20,7 @@ LL | |
 LL | |
 LL | |     Service<AssocType = <Bug as Foo>::OnlyFoo>
 ...  |
-LL | |
+LL | |     ) -> Self::AssocType;
 LL | | }
    | |_^ the trait `Foo` is not implemented for `Bug`
    |
@@ -34,6 +34,7 @@ error[E0277]: the trait bound `Bug: Foo` is not satisfied
    |
 LL | /     fn get_service(
 LL | |
+LL | |
 LL | |         &self,
 LL | |     ) -> Self::AssocType;
    | |_________________________^ the trait `Foo` is not implemented for `Bug`
@@ -50,10 +51,10 @@ LL | fn with_factory<H>(factory: dyn ThriftService<()>) {}
    |                             ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
 
 error[E0277]: the trait bound `Bug: Foo` is not satisfied
-  --> $DIR/issue-59324.rs:19:10
+  --> $DIR/issue-59324.rs:16:8
    |
-LL |     ) -> Self::AssocType;
-   |          ^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
+LL |     fn get_service(
+   |        ^^^^^^^^^^^ the trait `Foo` is not implemented for `Bug`
    |
 help: consider further restricting this bound
    |
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-2.rs b/src/test/ui/fn/implied-bounds-unnorm-associated-type-2.rs
index 5a92bcd37b6ee..5d924555625cd 100644
--- a/src/test/ui/fn/implied-bounds-unnorm-associated-type-2.rs
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type-2.rs
@@ -1,4 +1,4 @@
-// check-pass
+// check-fail
 
 trait Trait {
     type Type;
@@ -17,6 +17,7 @@ where
 
 fn g<'a, 'b>() {
     f::<'a, 'b>(());
+    //~^ ERROR lifetime may not live long enough
 }
 
 fn main() {}
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-2.stderr b/src/test/ui/fn/implied-bounds-unnorm-associated-type-2.stderr
new file mode 100644
index 0000000000000..0c3df04eabcd2
--- /dev/null
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type-2.stderr
@@ -0,0 +1,17 @@
+error: lifetime may not live long enough
+  --> $DIR/implied-bounds-unnorm-associated-type-2.rs:19:5
+   |
+LL | fn g<'a, 'b>() {
+   |      --  -- lifetime `'b` defined here
+   |      |
+   |      lifetime `'a` defined here
+LL |     f::<'a, 'b>(());
+   |     ^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'b: 'a`
+   = note: requirement occurs because of a function pointer to `f`
+   = note: the function `f` is invariant over the parameter `'a`
+   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-3.rs b/src/test/ui/fn/implied-bounds-unnorm-associated-type-3.rs
index dc25ac086137a..888f74cf6b337 100644
--- a/src/test/ui/fn/implied-bounds-unnorm-associated-type-3.rs
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type-3.rs
@@ -1,6 +1,4 @@
-// check-fail
-// See issue #91899. If we treat unnormalized args as WF, `Self` can also be a
-// source of unsoundness.
+// check-pass
 
 pub trait Yokeable<'a>: 'static {
     type Output: 'a;
@@ -17,7 +15,6 @@ pub trait ZeroCopyFrom<C: ?Sized>: for<'a> Yokeable<'a> {
 
 impl<T> ZeroCopyFrom<[T]> for &'static [T] {
     fn zero_copy_from<'b>(cart: &'b [T]) -> &'b [T] {
-        //~^ the parameter
         cart
     }
 }
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-3.stderr b/src/test/ui/fn/implied-bounds-unnorm-associated-type-3.stderr
deleted file mode 100644
index 95cf4fb168f1e..0000000000000
--- a/src/test/ui/fn/implied-bounds-unnorm-associated-type-3.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error[E0310]: the parameter type `T` may not live long enough
-  --> $DIR/implied-bounds-unnorm-associated-type-3.rs:19:5
-   |
-LL |     fn zero_copy_from<'b>(cart: &'b [T]) -> &'b [T] {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `[T]` will meet its required lifetime bounds
-   |
-help: consider adding an explicit lifetime bound...
-   |
-LL | impl<T: 'static> ZeroCopyFrom<[T]> for &'static [T] {
-   |       +++++++++
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0310`.
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-4.rs b/src/test/ui/fn/implied-bounds-unnorm-associated-type-4.rs
new file mode 100644
index 0000000000000..12859252c8794
--- /dev/null
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type-4.rs
@@ -0,0 +1,24 @@
+// A regression test for #98543
+
+trait Trait {
+    type Type;
+}
+
+impl<T> Trait for T {
+    type Type = ();
+}
+
+fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str
+where
+    &'a &'b (): Trait, // <- adding this bound is the change from #91068
+{
+    s
+}
+
+fn main() {
+    let x = String::from("Hello World!");
+    let y = f(&x, ());
+    drop(x);
+    //~^ ERROR cannot move out of `x` because it is borrowed
+    println!("{}", y);
+}
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-4.stderr b/src/test/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
new file mode 100644
index 0000000000000..fcbaa91d19f82
--- /dev/null
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type-4.stderr
@@ -0,0 +1,14 @@
+error[E0505]: cannot move out of `x` because it is borrowed
+  --> $DIR/implied-bounds-unnorm-associated-type-4.rs:21:10
+   |
+LL |     let y = f(&x, ());
+   |               -- borrow of `x` occurs here
+LL |     drop(x);
+   |          ^ move out of `x` occurs here
+LL |
+LL |     println!("{}", y);
+   |                    - borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0505`.
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-5.rs b/src/test/ui/fn/implied-bounds-unnorm-associated-type-5.rs
new file mode 100644
index 0000000000000..2a9a6a8cc6c81
--- /dev/null
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type-5.rs
@@ -0,0 +1,23 @@
+trait Trait<'a>: 'a {
+    type Type;
+}
+
+// if the `T: 'a` bound gets implied we would probably get ub here again
+impl<'a, T> Trait<'a> for T {
+    //~^ ERROR the parameter type `T` may not live long enough
+    type Type = ();
+}
+
+fn f<'a, 'b>(s: &'b str, _: <&'b () as Trait<'a>>::Type) -> &'a str
+where
+    &'b (): Trait<'a>,
+{
+    s
+}
+
+fn main() {
+    let x = String::from("Hello World!");
+    let y = f(&x, ());
+    drop(x);
+    println!("{}", y);
+}
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type-5.stderr b/src/test/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
new file mode 100644
index 0000000000000..458756a3dcd96
--- /dev/null
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type-5.stderr
@@ -0,0 +1,19 @@
+error[E0309]: the parameter type `T` may not live long enough
+  --> $DIR/implied-bounds-unnorm-associated-type-5.rs:6:13
+   |
+LL | impl<'a, T> Trait<'a> for T {
+   |             ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
+   |
+note: ...that is required by this bound
+  --> $DIR/implied-bounds-unnorm-associated-type-5.rs:1:18
+   |
+LL | trait Trait<'a>: 'a {
+   |                  ^^
+help: consider adding an explicit lifetime bound...
+   |
+LL | impl<'a, T: 'a> Trait<'a> for T {
+   |           ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0309`.
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type.rs b/src/test/ui/fn/implied-bounds-unnorm-associated-type.rs
index 04b6f4dd84e2e..d58d25036c5bb 100644
--- a/src/test/ui/fn/implied-bounds-unnorm-associated-type.rs
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type.rs
@@ -1,6 +1,6 @@
 // check-fail
-// See issue #91068. Types in the substs of an associated type can't be implied
-// to be WF, since they don't actually have to be constructed.
+// See issue #91068. We check that the unnormalized associated types in
+// function signatures are implied
 
 trait Trait {
     type Type;
@@ -12,12 +12,12 @@ impl<T> Trait for T {
 
 fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str {
     s
-    //~^ ERROR lifetime may not live long enough
 }
 
 fn main() {
     let x = String::from("Hello World!");
     let y = f(&x, ());
     drop(x);
+    //~^ ERROR cannot move out of `x` because it is borrowed
     println!("{}", y);
 }
diff --git a/src/test/ui/fn/implied-bounds-unnorm-associated-type.stderr b/src/test/ui/fn/implied-bounds-unnorm-associated-type.stderr
index 8096f08385c8c..e35f46e4439a9 100644
--- a/src/test/ui/fn/implied-bounds-unnorm-associated-type.stderr
+++ b/src/test/ui/fn/implied-bounds-unnorm-associated-type.stderr
@@ -1,14 +1,14 @@
-error: lifetime may not live long enough
-  --> $DIR/implied-bounds-unnorm-associated-type.rs:14:5
+error[E0505]: cannot move out of `x` because it is borrowed
+  --> $DIR/implied-bounds-unnorm-associated-type.rs:20:10
    |
-LL | fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str {
-   |      --  -- lifetime `'b` defined here
-   |      |
-   |      lifetime `'a` defined here
-LL |     s
-   |     ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
-   |
-   = help: consider adding the following bound: `'b: 'a`
+LL |     let y = f(&x, ());
+   |               -- borrow of `x` occurs here
+LL |     drop(x);
+   |          ^ move out of `x` occurs here
+LL |
+LL |     println!("{}", y);
+   |                    - borrow later used here
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0505`.
diff --git a/src/test/ui/generic-associated-types/bugs/issue-87748.stderr b/src/test/ui/generic-associated-types/bugs/issue-87748.stderr
deleted file mode 100644
index ac197dfe6ff69..0000000000000
--- a/src/test/ui/generic-associated-types/bugs/issue-87748.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error[E0478]: lifetime bound not satisfied
-  --> $DIR/issue-87748.rs:18:5
-   |
-LL |     fn do_sth(_: u32) {}
-   |     ^^^^^^^^^^^^^^^^^
-   |
-note: lifetime parameter instantiated with the anonymous lifetime as defined here
-  --> $DIR/issue-87748.rs:18:5
-   |
-LL |     fn do_sth(_: u32) {}
-   |     ^^^^^^^^^^^^^^^^^
-note: but lifetime parameter must outlive the anonymous lifetime as defined here
-  --> $DIR/issue-87748.rs:18:5
-   |
-LL |     fn do_sth(_: u32) {}
-   |     ^^^^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0478`.
diff --git a/src/test/ui/generic-associated-types/bugs/issue-87748.rs b/src/test/ui/generic-associated-types/issue-87748.rs
similarity index 53%
rename from src/test/ui/generic-associated-types/bugs/issue-87748.rs
rename to src/test/ui/generic-associated-types/issue-87748.rs
index a3d00ee03b13e..1a1ab9bf8a4eb 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-87748.rs
+++ b/src/test/ui/generic-associated-types/issue-87748.rs
@@ -1,13 +1,14 @@
-// check-fail
-// known-bug: #87748
+// Checks that we properly add implied bounds from unnormalized projections in
+// inputs when typechecking functions.
 
-// This should pass, but unnormalized input args aren't treated as implied.
+// check-pass
 
 #![feature(generic_associated_types)]
 
 trait MyTrait {
     type Assoc<'a, 'b> where 'b: 'a;
     fn do_sth(arg: Self::Assoc<'_, '_>);
+    fn do_sth2(arg: Self::Assoc<'_, '_>) {}
 }
 
 struct Foo;
@@ -16,8 +17,7 @@ impl MyTrait for Foo {
     type Assoc<'a, 'b> = u32 where 'b: 'a;
 
     fn do_sth(_: u32) {}
-    // fn do_sth(_: Self::Assoc<'static, 'static>) {}
-    // fn do_sth(_: Self::Assoc<'_, '_>) {}
+    fn do_sth2(_: Self::Assoc<'static, 'static>) {}
 }
 
 fn main() {}
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs
index 172bf218c0d4c..de9348f539709 100644
--- a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.rs
@@ -7,7 +7,6 @@ trait SomeTrait<'a> {
 fn give_me_ice<T>() {
     callee::<fn(&()) -> <T as SomeTrait<'_>>::Associated>();
     //~^ ERROR the trait bound `for<'r> T: SomeTrait<'r>` is not satisfied [E0277]
-    //~| ERROR the trait bound `for<'r> T: SomeTrait<'r>` is not satisfied [E0277]
 }
 
 fn callee<T: Fn<(&'static (),)>>() {
diff --git a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr
index ecca4b999e7e8..6a948a116e03b 100644
--- a/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr
+++ b/src/test/ui/higher-rank-trait-bounds/normalize-under-binder/issue-85455.stderr
@@ -9,17 +9,6 @@ help: consider restricting type parameter `T`
 LL | fn give_me_ice<T: for<'r> SomeTrait<'r>>() {
    |                 +++++++++++++++++++++++
 
-error[E0277]: the trait bound `for<'r> T: SomeTrait<'r>` is not satisfied
-  --> $DIR/issue-85455.rs:8:14
-   |
-LL |     callee::<fn(&()) -> <T as SomeTrait<'_>>::Associated>();
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'r> SomeTrait<'r>` is not implemented for `T`
-   |
-help: consider restricting type parameter `T`
-   |
-LL | fn give_me_ice<T: for<'r> SomeTrait<'r>>() {
-   |                 +++++++++++++++++++++++
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.rs b/src/test/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.rs
new file mode 100644
index 0000000000000..33b746c5edfb2
--- /dev/null
+++ b/src/test/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.rs
@@ -0,0 +1,27 @@
+// Test for a less than ideal interaction of implied bounds and normalization.
+trait Tr {
+    type Ty;
+}
+
+impl<T: 'static> Tr for T {
+    type Ty = &'static T;
+}
+
+// `<&'a u8 as Tr>::Ty` should cause an error because `&'a u8: Tr` doesn't hold for
+// all possible 'a. However, we consider normalized types for implied bounds.
+//
+// We normalize this projection to `&'static &'a u8` and add a nested `&'a u8: 'static`
+// bound. This bound is then proven using the implied bounds for `&'static &'a u8` which
+// we only get by normalizing in the first place.
+fn test<'a>(x: &'a u8, _wf: <&'a u8 as Tr>::Ty) -> &'static u8 { x }
+
+fn main() {
+    // This works as we have 'static references due to promotion.
+    let _: &'static u8 = test(&3, &&3);
+    // This causes an error because the projection requires 'a to be 'static.
+    // It would be unsound if this compiled.
+    let x: u8 = 3;
+    let _: &'static u8 = test(&x, &&3);
+    //~^ ERROR `x` does not live long enough
+
+}
diff --git a/src/test/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.stderr b/src/test/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.stderr
new file mode 100644
index 0000000000000..d0249e74f39e9
--- /dev/null
+++ b/src/test/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.stderr
@@ -0,0 +1,15 @@
+error[E0597]: `x` does not live long enough
+  --> $DIR/assoc-ty-wf-used-to-get-assoc-ty.rs:24:31
+   |
+LL |     let _: &'static u8 = test(&x, &&3);
+   |                          -----^^------
+   |                          |    |
+   |                          |    borrowed value does not live long enough
+   |                          argument requires that `x` is borrowed for `'static`
+...
+LL | }
+   | - `x` dropped here while still borrowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs
index c4db6fc97dc32..05e2ea047f65a 100644
--- a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs
+++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs
@@ -30,4 +30,5 @@ fn main() {
     let _x = <fn(&())>::make_f();
     //~^ ERROR implementation of `Y` is not general enough
     //~| ERROR implementation of `Y` is not general enough
+    //~| ERROR implementation of `Y` is not general enough
 }
diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr
index 51adfca3e79f4..8c47379886d2a 100644
--- a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr
+++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr
@@ -16,5 +16,14 @@ LL |     let _x = <fn(&())>::make_f();
    = note: `Y` would have to be implemented for the type `for<'r> fn(&'r ())`
    = note: ...but `Y` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0`
 
-error: aborting due to 2 previous errors
+error: implementation of `Y` is not general enough
+  --> $DIR/impl-fn-ignore-binder-via-bottom.rs:30:14
+   |
+LL |     let _x = <fn(&())>::make_f();
+   |              ^^^^^^^^^^^^^^^^^^^ implementation of `Y` is not general enough
+   |
+   = note: `Y` would have to be implemented for the type `for<'r> fn(&'r ())`
+   = note: ...but `Y` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0`
+
+error: aborting due to 3 previous errors