diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 77914802bf7..d7b1447d980 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -368,7 +368,7 @@ fn check_opaque_meets_bounds<'tcx>(
         // 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 implied_bounds = infcx.implied_bounds_tys_compat(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)?;
         }
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 774feb94f7d..e2cdeb5b204 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -378,7 +378,7 @@ fn compare_method_predicate_entailment<'tcx>(
     // lifetime parameters.
     let outlives_env = OutlivesEnvironment::with_bounds(
         param_env,
-        infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
+        infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
     );
     let errors = infcx.resolve_regions(&outlives_env);
     if !errors.is_empty() {
@@ -702,7 +702,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
     // lifetime parameters.
     let outlives_env = OutlivesEnvironment::with_bounds(
         param_env,
-        infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
+        infcx.implied_bounds_tys_compat(param_env, impl_m_def_id, &wf_tys),
     );
     ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;
 
@@ -2070,7 +2070,8 @@ pub(super) fn check_type_bounds<'tcx>(
 
     // Finally, resolve all regions. This catches wily misuses of
     // lifetime parameters.
-    let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
+    let implied_bounds =
+        infcx.implied_bounds_tys_compat(param_env, impl_ty_def_id, &assumed_wf_types);
     let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
     ocx.resolve_regions_and_report_errors(impl_ty_def_id, &outlives_env)
 }
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index f7fc0c81b95..3509a39fec9 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -158,7 +158,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
     }
     let outlives_env = OutlivesEnvironment::with_bounds(
         param_env,
-        infcx.implied_bounds_tys(param_env, impl_m.def_id.expect_local(), implied_wf_types),
+        infcx.implied_bounds_tys_compat(param_env, impl_m.def_id.expect_local(), &implied_wf_types),
     );
     let errors = infcx.resolve_regions(&outlives_env);
     if !errors.is_empty() {
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 4772bae58c4..1af6282070e 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -15,6 +15,7 @@ use rustc_infer::infer::outlives::obligations::TypeOutlives;
 use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::query::Providers;
+use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
 use rustc_middle::ty::{
     self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
@@ -112,8 +113,6 @@ where
 
     let assumed_wf_types = wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)?;
 
-    let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
-
     let errors = wfcx.select_all_or_error();
     if !errors.is_empty() {
         let err = infcx.err_ctxt().report_fulfillment_errors(errors);
@@ -128,10 +127,58 @@ where
         }
     }
 
+    let infcx_compat = infcx.fork();
+
+    debug!(?assumed_wf_types);
+    let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, &assumed_wf_types);
     let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
 
-    wfcx.ocx.resolve_regions_and_report_errors(body_def_id, &outlives_env)?;
-    infcx.tainted_by_errors().error_reported()
+    let errors = infcx.resolve_regions(&outlives_env);
+    if errors.is_empty() {
+        return Ok(());
+    }
+
+    let is_bevy = 'is_bevy: {
+        // We don't want to emit this for dependents of Bevy, for now.
+        // See #119956
+        let is_bevy_paramset = |def: ty::AdtDef<'_>| {
+            let adt_did = with_no_trimmed_paths!(infcx.tcx.def_path_str(def.0.did));
+            adt_did.contains("ParamSet")
+        };
+        for ty in assumed_wf_types.iter() {
+            match ty.kind() {
+                ty::Adt(def, _) => {
+                    if is_bevy_paramset(*def) {
+                        break 'is_bevy true;
+                    }
+                }
+                ty::Ref(_, ty, _) => match ty.kind() {
+                    ty::Adt(def, _) => {
+                        if is_bevy_paramset(*def) {
+                            break 'is_bevy true;
+                        }
+                    }
+                    _ => {}
+                },
+                _ => {}
+            }
+        }
+        false
+    };
+
+    if is_bevy {
+        let implied_bounds =
+            infcx_compat.implied_bounds_tys_compat(param_env, body_def_id, &assumed_wf_types);
+        let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
+        let errors_compat = infcx_compat.resolve_regions(&outlives_env);
+        if errors_compat.is_empty() {
+            Ok(())
+        } else {
+            Err(infcx_compat.err_ctxt().report_region_errors(body_def_id, &errors_compat))
+        }
+    } else {
+        Err(infcx.err_ctxt().report_region_errors(body_def_id, &errors))
+    }
 }
 
 fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorGuaranteed> {
@@ -723,7 +770,7 @@ fn resolve_regions_with_wf_tys<'tcx>(
     let infcx = tcx.infer_ctxt().build();
     let outlives_environment = OutlivesEnvironment::with_bounds(
         param_env,
-        infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
+        infcx.implied_bounds_tys_compat(param_env, id, wf_tys),
     );
     let region_bound_pairs = outlives_environment.region_bound_pairs();
 
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 93dd2342a4d..a922209deb8 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -202,7 +202,8 @@ fn get_impl_args(
         return Err(guar);
     }
 
-    let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
+    let implied_bounds =
+        infcx.implied_bounds_tys_compat(param_env, impl1_def_id, &assumed_wf_types);
     let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
     let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
     let Ok(impl2_args) = infcx.fully_resolve(impl2_args) else {
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index b9200f1abf1..7122f131731 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -74,6 +74,10 @@ impl<T> EraseType for Result<&'_ T, traits::query::NoSolution> {
     type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
 }
 
+impl<T> EraseType for Result<&'_ [T], traits::query::NoSolution> {
+    type Result = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()];
+}
+
 impl<T> EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
     type Result = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
 }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 1dc77220881..f5b76f08f0b 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1949,7 +1949,7 @@ rustc_queries! {
         desc { "normalizing `{}`", goal.value }
     }
 
-    query implied_outlives_bounds(
+    query implied_outlives_bounds_compat(
         goal: CanonicalTyGoal<'tcx>
     ) -> Result<
         &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
@@ -1958,6 +1958,15 @@ rustc_queries! {
         desc { "computing implied outlives bounds for `{}`", goal.value.value }
     }
 
+    query implied_outlives_bounds(
+        goal: CanonicalTyGoal<'tcx>
+    ) -> Result<
+        &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
+        NoSolution,
+    > {
+        desc { "computing implied outlives bounds v2 for `{}`", goal.value.value }
+    }
+
     /// Do not call this query directly:
     /// invoke `DropckOutlives::new(dropped_ty)).fully_perform(typeck.infcx)` instead.
     query dropck_outlives(
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 5f02dd22961..61d96cad57d 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -191,7 +191,7 @@ pub struct NormalizationResult<'tcx> {
 /// case they are called implied bounds). They are fed to the
 /// `OutlivesEnv` which in turn is supplied to the region checker and
 /// other parts of the inference system.
-#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
+#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
 pub enum OutlivesBound<'tcx> {
     RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
     RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index 073174127d6..52de8c4ceae 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -191,10 +191,10 @@ pub fn all_fields_implement_trait<'tcx>(
             // Check regions assuming the self type of the impl is WF
             let outlives_env = OutlivesEnvironment::with_bounds(
                 param_env,
-                infcx.implied_bounds_tys(
+                infcx.implied_bounds_tys_compat(
                     param_env,
                     parent_cause.body_id,
-                    FxIndexSet::from_iter([self_type]),
+                    &FxIndexSet::from_iter([self_type]),
                 ),
             );
             let errors = infcx.resolve_regions(&outlives_env);
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 5b349d576b6..ec17024345a 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -9,123 +9,139 @@ use rustc_span::def_id::LocalDefId;
 
 pub use rustc_middle::traits::query::OutlivesBound;
 
+pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
 pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
 pub trait InferCtxtExt<'a, 'tcx> {
-    fn implied_outlives_bounds(
-        &self,
+    fn implied_bounds_tys_compat(
+        &'a self,
         param_env: ty::ParamEnv<'tcx>,
         body_id: LocalDefId,
-        ty: Ty<'tcx>,
-    ) -> Vec<OutlivesBound<'tcx>>;
+        tys: &'a FxIndexSet<Ty<'tcx>>,
+    ) -> BoundsCompat<'a, 'tcx>;
 
     fn implied_bounds_tys(
         &'a self,
         param_env: ty::ParamEnv<'tcx>,
         body_id: LocalDefId,
-        tys: FxIndexSet<Ty<'tcx>>,
+        tys: &'a FxIndexSet<Ty<'tcx>>,
     ) -> Bounds<'a, 'tcx>;
 }
 
-impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
-    /// Implied bounds are region relationships that we deduce
-    /// automatically. The idea is that (e.g.) a caller must check that a
-    /// function's argument types are well-formed immediately before
-    /// calling that fn, and hence the *callee* can assume that its
-    /// argument types are well-formed. This may imply certain relationships
-    /// between generic parameters. For example:
-    /// ```
-    /// fn foo<T>(x: &T) {}
-    /// ```
-    /// can only be called with a `'a` and `T` such that `&'a T` is WF.
-    /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
-    ///
-    /// # Parameters
-    ///
-    /// - `param_env`, the where-clauses in scope
-    /// - `body_id`, the body-id to use when normalizing assoc types.
-    ///   Note that this may cause outlives obligations to be injected
-    ///   into the inference context with this body-id.
-    /// - `ty`, the type that we are supposed to assume is WF.
-    #[instrument(level = "debug", skip(self, param_env, body_id), ret)]
-    fn implied_outlives_bounds(
-        &self,
-        param_env: ty::ParamEnv<'tcx>,
-        body_id: LocalDefId,
-        ty: Ty<'tcx>,
-    ) -> Vec<OutlivesBound<'tcx>> {
-        let ty = self.resolve_vars_if_possible(ty);
-        let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
+/// Implied bounds are region relationships that we deduce
+/// automatically. The idea is that (e.g.) a caller must check that a
+/// function's argument types are well-formed immediately before
+/// calling that fn, and hence the *callee* can assume that its
+/// argument types are well-formed. This may imply certain relationships
+/// between generic parameters. For example:
+/// ```
+/// fn foo<T>(x: &T) {}
+/// ```
+/// can only be called with a `'a` and `T` such that `&'a T` is WF.
+/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
+///
+/// # Parameters
+///
+/// - `param_env`, the where-clauses in scope
+/// - `body_id`, the body-id to use when normalizing assoc types.
+///   Note that this may cause outlives obligations to be injected
+///   into the inference context with this body-id.
+/// - `ty`, the type that we are supposed to assume is WF.
+#[instrument(level = "debug", skip(infcx, param_env, body_id), ret)]
+fn implied_outlives_bounds<'a, 'tcx>(
+    infcx: &'a InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    body_id: LocalDefId,
+    ty: Ty<'tcx>,
+    compat: bool,
+) -> Vec<OutlivesBound<'tcx>> {
+    let ty = infcx.resolve_vars_if_possible(ty);
+    let ty = OpportunisticRegionResolver::new(infcx).fold_ty(ty);
 
-        // We do not expect existential variables in implied bounds.
-        // We may however encounter unconstrained lifetime variables
-        // in very rare cases.
-        //
-        // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
-        // an example.
-        assert!(!ty.has_non_region_infer());
+    // We do not expect existential variables in implied bounds.
+    // We may however encounter unconstrained lifetime variables
+    // in very rare cases.
+    //
+    // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
+    // an example.
+    assert!(!ty.has_non_region_infer());
 
-        let mut canonical_var_values = OriginalQueryValues::default();
-        let canonical_ty = self.canonicalize_query(param_env.and(ty), &mut canonical_var_values);
-        let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else {
-            return vec![];
-        };
+    let mut canonical_var_values = OriginalQueryValues::default();
+    let canonical_ty = infcx.canonicalize_query(param_env.and(ty), &mut canonical_var_values);
+    let implied_bounds_result = if compat {
+        infcx.tcx.implied_outlives_bounds_compat(canonical_ty)
+    } else {
+        infcx.tcx.implied_outlives_bounds(canonical_ty)
+    };
+    let Ok(canonical_result) = implied_bounds_result else {
+        return vec![];
+    };
 
-        let mut constraints = QueryRegionConstraints::default();
-        let Ok(InferOk { value: mut bounds, obligations }) = self
-            .instantiate_nll_query_response_and_region_obligations(
-                &ObligationCause::dummy(),
+    let mut constraints = QueryRegionConstraints::default();
+    let Ok(InferOk { value: mut bounds, obligations }) = infcx
+        .instantiate_nll_query_response_and_region_obligations(
+            &ObligationCause::dummy(),
+            param_env,
+            &canonical_var_values,
+            canonical_result,
+            &mut constraints,
+        )
+    else {
+        return vec![];
+    };
+    assert_eq!(&obligations, &[]);
+
+    // Because of #109628, we may have unexpected placeholders. Ignore them!
+    // FIXME(#109628): panic in this case once the issue is fixed.
+    bounds.retain(|bound| !bound.has_placeholders());
+
+    if !constraints.is_empty() {
+        let span = infcx.tcx.def_span(body_id);
+
+        debug!(?constraints);
+        if !constraints.member_constraints.is_empty() {
+            span_bug!(span, "{:#?}", constraints.member_constraints);
+        }
+
+        // Instantiation may have produced new inference variables and constraints on those
+        // variables. Process these constraints.
+        let ocx = ObligationCtxt::new(infcx);
+        let cause = ObligationCause::misc(span, body_id);
+        for &constraint in &constraints.outlives {
+            ocx.register_obligation(infcx.query_outlives_constraint_to_obligation(
+                constraint,
+                cause.clone(),
                 param_env,
-                &canonical_var_values,
-                canonical_result,
-                &mut constraints,
-            )
-        else {
-            return vec![];
-        };
-        assert_eq!(&obligations, &[]);
+            ));
+        }
 
-        // Because of #109628, we may have unexpected placeholders. Ignore them!
-        // FIXME(#109628): panic in this case once the issue is fixed.
-        bounds.retain(|bound| !bound.has_placeholders());
+        let errors = ocx.select_all_or_error();
+        if !errors.is_empty() {
+            infcx.dcx().span_delayed_bug(
+                span,
+                "implied_outlives_bounds failed to solve obligations from instantiation",
+            );
+        }
+    };
 
-        if !constraints.is_empty() {
-            let span = self.tcx.def_span(body_id);
+    bounds
+}
 
-            debug!(?constraints);
-            if !constraints.member_constraints.is_empty() {
-                span_bug!(span, "{:#?}", constraints.member_constraints);
-            }
-
-            // Instantiation may have produced new inference variables and constraints on those
-            // variables. Process these constraints.
-            let ocx = ObligationCtxt::new(self);
-            let cause = ObligationCause::misc(span, body_id);
-            for &constraint in &constraints.outlives {
-                ocx.register_obligation(self.query_outlives_constraint_to_obligation(
-                    constraint,
-                    cause.clone(),
-                    param_env,
-                ));
-            }
-
-            let errors = ocx.select_all_or_error();
-            if !errors.is_empty() {
-                self.dcx().span_delayed_bug(
-                    span,
-                    "implied_outlives_bounds failed to solve obligations from instantiation",
-                );
-            }
-        };
-
-        bounds
+impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
+    fn implied_bounds_tys_compat(
+        &'a self,
+        param_env: ParamEnv<'tcx>,
+        body_id: LocalDefId,
+        tys: &'a FxIndexSet<Ty<'tcx>>,
+    ) -> BoundsCompat<'a, 'tcx> {
+        tys.iter().flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, true))
     }
 
     fn implied_bounds_tys(
         &'a self,
         param_env: ParamEnv<'tcx>,
         body_id: LocalDefId,
-        tys: FxIndexSet<Ty<'tcx>>,
+        tys: &'a FxIndexSet<Ty<'tcx>>,
     ) -> Bounds<'a, 'tcx> {
-        tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty))
+        tys.iter().flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, false))
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index ba6ed298774..bdc53f895fe 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -5,10 +5,11 @@ use crate::traits::ObligationCtxt;
 
 use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
+use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::traits::query::OutlivesBound;
 use rustc_middle::infer::canonical::CanonicalQueryResponse;
 use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::DUMMY_SP;
 use smallvec::{smallvec, SmallVec};
@@ -47,14 +48,14 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
             param_env.and(ty)
         });
 
-        tcx.implied_outlives_bounds(canonicalized)
+        tcx.implied_outlives_bounds_compat(canonicalized)
     }
 
     fn perform_locally_with_next_solver(
         ocx: &ObligationCtxt<'_, 'tcx>,
         key: ParamEnvAnd<'tcx, Self>,
     ) -> Result<Self::QueryResponse, NoSolution> {
-        compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty)
+        compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty)
     }
 }
 
@@ -62,6 +63,85 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
     ocx: &ObligationCtxt<'_, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     ty: Ty<'tcx>,
+) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
+    let normalize_op = |ty| {
+        let ty = ocx.normalize(&ObligationCause::dummy(), param_env, ty);
+        if !ocx.select_all_or_error().is_empty() {
+            return Err(NoSolution);
+        }
+        let ty = ocx.infcx.resolve_vars_if_possible(ty);
+        let ty = OpportunisticRegionResolver::new(&ocx.infcx).fold_ty(ty);
+        Ok(ty)
+    };
+
+    // Sometimes when we ask what it takes for T: WF, we get back that
+    // U: WF is required; in that case, we push U onto this stack and
+    // process it next. Because the resulting predicates aren't always
+    // guaranteed to be a subset of the original type, so we need to store the
+    // WF args we've computed in a set.
+    let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
+    let mut wf_args = vec![ty.into(), normalize_op(ty)?.into()];
+
+    let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = vec![];
+
+    while let Some(arg) = wf_args.pop() {
+        if !checked_wf_args.insert(arg) {
+            continue;
+        }
+
+        // From the full set of obligations, just filter down to the region relationships.
+        for obligation in
+            wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
+        {
+            assert!(!obligation.has_escaping_bound_vars());
+            let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
+                continue;
+            };
+            match pred {
+                // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
+                // if we ever support that
+                ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
+                | ty::PredicateKind::Subtype(..)
+                | ty::PredicateKind::Coerce(..)
+                | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
+                | ty::PredicateKind::ObjectSafe(..)
+                | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
+                | ty::PredicateKind::ConstEquate(..)
+                | ty::PredicateKind::Ambiguous
+                | ty::PredicateKind::NormalizesTo(..)
+                | ty::PredicateKind::AliasRelate(..) => {}
+
+                // We need to search through *all* WellFormed predicates
+                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                    wf_args.push(arg);
+                }
+
+                // We need to register region relationships
+                ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(
+                    ty::OutlivesPredicate(r_a, r_b),
+                )) => outlives_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)),
+
+                ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
+                    ty_a,
+                    r_b,
+                ))) => {
+                    let ty_a = normalize_op(ty_a)?;
+                    let mut components = smallvec![];
+                    push_outlives_components(ocx.infcx.tcx, ty_a, &mut components);
+                    outlives_bounds.extend(implied_bounds_from_components(r_b, components))
+                }
+            }
+        }
+    }
+
+    Ok(outlives_bounds)
+}
+
+pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
+    ocx: &ObligationCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
 ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
     let tcx = ocx.infcx.tcx;
 
diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs
index 959838ab348..24e91c263e3 100644
--- a/compiler/rustc_traits/src/implied_outlives_bounds.rs
+++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs
@@ -8,13 +8,29 @@ use rustc_infer::traits::query::OutlivesBound;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
-use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::compute_implied_outlives_bounds_inner;
+use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{
+    compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner,
+};
 use rustc_trait_selection::traits::query::{CanonicalTyGoal, NoSolution};
 
 pub(crate) fn provide(p: &mut Providers) {
+    *p = Providers { implied_outlives_bounds_compat, ..*p };
     *p = Providers { implied_outlives_bounds, ..*p };
 }
 
+fn implied_outlives_bounds_compat<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    goal: CanonicalTyGoal<'tcx>,
+) -> Result<
+    &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
+    NoSolution,
+> {
+    tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| {
+        let (param_env, ty) = key.into_parts();
+        compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty)
+    })
+}
+
 fn implied_outlives_bounds<'tcx>(
     tcx: TyCtxt<'tcx>,
     goal: CanonicalTyGoal<'tcx>,
diff --git a/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs b/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs
deleted file mode 100644
index b373d39f4d9..00000000000
--- a/tests/ui/implied-bounds/auxiliary/bevy_ecs.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Related to Bevy regression #118553
-
-pub trait WorldQuery {}
-impl WorldQuery for &u8 {}
-
-pub struct Query<Q: WorldQuery>(Q);
-
-pub trait SystemParam {
-    type State;
-}
-impl<Q: WorldQuery + 'static> SystemParam for Query<Q> {
-    type State = ();
-    // `Q: 'static` is required because we need the TypeId of Q ...
-}
-
-pub struct ParamSet<T: SystemParam>(T)
-where
-    T::State: Sized;
diff --git a/tests/ui/implied-bounds/bevy_world_query.rs b/tests/ui/implied-bounds/bevy_world_query.rs
index f8e64632676..d5de0e9ef21 100644
--- a/tests/ui/implied-bounds/bevy_world_query.rs
+++ b/tests/ui/implied-bounds/bevy_world_query.rs
@@ -1,11 +1,26 @@
-// aux-crate:bevy_ecs=bevy_ecs.rs
 // check-pass
-// Related to Bevy regression #118553
 
-extern crate bevy_ecs;
+// We currently special case bevy from erroring on incorrect implied bounds
+// from normalization (issue #109628).
+// Otherwise, we would expect this to hit that error.
 
-use bevy_ecs::*;
+pub trait WorldQuery {}
+impl WorldQuery for &u8 {}
+
+pub struct Query<Q: WorldQuery>(Q);
+
+pub trait SystemParam {
+    type State;
+}
+impl<Q: WorldQuery + 'static> SystemParam for Query<Q> {
+    type State = ();
+    // `Q: 'static` is required because we need the TypeId of Q ...
+}
+
+pub struct ParamSet<T: SystemParam>(T) where T::State: Sized;
 
 fn handler<'a>(_: ParamSet<Query<&'a u8>>) {}
 
+fn ref_handler<'a>(_: &ParamSet<Query<&'a u8>>) {}
+
 fn main() {}
diff --git a/tests/ui/implied-bounds/from-trait-impl.rs b/tests/ui/implied-bounds/from-trait-impl.rs
index d13fddd9b8d..6e126575aa9 100644
--- a/tests/ui/implied-bounds/from-trait-impl.rs
+++ b/tests/ui/implied-bounds/from-trait-impl.rs
@@ -1,6 +1,3 @@
-// check-pass
-// known-bug: #109628
-
 trait Trait {
     type Assoc;
 }
@@ -14,11 +11,13 @@ where
     T::Assoc: Clone; // any predicate using `T::Assoc` works here
 
 fn func1(foo: Foo<(&str,)>) {
+    //~^ ERROR `&str` does not fulfill the required lifetime
     let _: &'static str = foo.0.0;
 }
 
 trait TestTrait {}
 
 impl<X> TestTrait for [Foo<(X,)>; 1] {}
+//~^ ERROR `X` may not live long enough
 
 fn main() {}
diff --git a/tests/ui/implied-bounds/from-trait-impl.stderr b/tests/ui/implied-bounds/from-trait-impl.stderr
new file mode 100644
index 00000000000..4151d206ae2
--- /dev/null
+++ b/tests/ui/implied-bounds/from-trait-impl.stderr
@@ -0,0 +1,26 @@
+error[E0477]: the type `&str` does not fulfill the required lifetime
+  --> $DIR/from-trait-impl.rs:13:15
+   |
+LL | fn func1(foo: Foo<(&str,)>) {
+   |               ^^^^^^^^^^^^
+   |
+   = note: type must satisfy the static lifetime
+
+error[E0310]: the parameter type `X` may not live long enough
+  --> $DIR/from-trait-impl.rs:20:23
+   |
+LL | impl<X> TestTrait for [Foo<(X,)>; 1] {}
+   |                       ^^^^^^^^^^^^^^
+   |                       |
+   |                       the parameter type `X` must be valid for the static lifetime...
+   |                       ...so that the type `X` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL | impl<X: 'static> TestTrait for [Foo<(X,)>; 1] {}
+   |       +++++++++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0310, E0477.
+For more information about an error, try `rustc --explain E0310`.
diff --git a/tests/ui/implied-bounds/gluon_salsa.rs b/tests/ui/implied-bounds/gluon_salsa.rs
index 98951af8ac2..cd5500cb458 100644
--- a/tests/ui/implied-bounds/gluon_salsa.rs
+++ b/tests/ui/implied-bounds/gluon_salsa.rs
@@ -1,5 +1,5 @@
 // check-pass
-// Related to Bevy regression #118553
+// Found in a crater run on #118553
 
 pub trait QueryBase {
     type Db;
@@ -17,8 +17,7 @@ pub struct QueryTable<'me, Q, DB> {
     _marker: Option<&'me ()>,
 }
 
-impl<'me, Q> QueryTable<'me, Q, <Q as QueryBase>::Db>
-// projection is important
+impl<'me, Q> QueryTable<'me, Q, <Q as QueryBase>::Db> // projection is important
 //   ^^^ removing 'me (and in QueryTable) gives a different error
 where
     Q: for<'f> AsyncQueryFunction<'f>,
diff --git a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr b/tests/ui/implied-bounds/normalization-nested.lifetime.stderr
index e020230d86a..041892c7542 100644
--- a/tests/ui/implied-bounds/normalization-nested.lifetime.stderr
+++ b/tests/ui/implied-bounds/normalization-nested.lifetime.stderr
@@ -1,33 +1,10 @@
-error[E0759]: `fn` parameter has lifetime `'x` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/normalization-nested.rs:35:28
-   |
-LL | pub fn test_wfcheck<'x>(_: Map<Vec<&'x ()>>) {}
-   |                            ^^^^^^^^^^^^^^^^
-   |                            |
-   |                            this data with lifetime `'x`...
-   |                            ...is used and required to live as long as `'static` here
-   |
-note: `'static` lifetime requirement introduced by this bound
-  --> $DIR/normalization-nested.rs:33:14
-   |
-LL |     I::Item: 'static;
-   |              ^^^^^^^
-
-error[E0759]: `fn` parameter has lifetime `'x` but it needs to satisfy a `'static` lifetime requirement
-  --> $DIR/normalization-nested.rs:37:29
+error: lifetime may not live long enough
+  --> $DIR/normalization-nested.rs:38:5
    |
 LL | pub fn test_borrowck<'x>(_: Map<Vec<&'x ()>>, s: &'x str) -> &'static str {
-   |                             ^^^^^^^^^^^^^^^^
-   |                             |
-   |                             this data with lifetime `'x`...
-   |                             ...is used and required to live as long as `'static` here
-   |
-note: `'static` lifetime requirement introduced by this bound
-  --> $DIR/normalization-nested.rs:33:14
-   |
-LL |     I::Item: 'static;
-   |              ^^^^^^^
+   |                      -- lifetime `'x` defined here
+LL |     s
+   |     ^ returning this value requires that `'x` must outlive `'static`
 
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0759`.
diff --git a/tests/ui/implied-bounds/sod_service_chain.rs b/tests/ui/implied-bounds/sod_service_chain.rs
index f45ced71f75..7443a29f30c 100644
--- a/tests/ui/implied-bounds/sod_service_chain.rs
+++ b/tests/ui/implied-bounds/sod_service_chain.rs
@@ -1,5 +1,4 @@
-// check-pass
-// Related to crater regressions on #118553
+// Found in a crater run on #118553
 
 pub trait Debug {}
 
@@ -30,6 +29,9 @@ impl<P: Service, S: Service<Input = P::Output>> ServiceChainBuilder<P, S> {
     pub fn next<NS: Service<Input = S::Output>>(
         self,
     ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> {
+        //~^ the associated type
+        //~| the associated type
+        //~| the associated type
         panic!();
     }
 }
diff --git a/tests/ui/implied-bounds/sod_service_chain.stderr b/tests/ui/implied-bounds/sod_service_chain.stderr
new file mode 100644
index 00000000000..1c0ef573e7d
--- /dev/null
+++ b/tests/ui/implied-bounds/sod_service_chain.stderr
@@ -0,0 +1,31 @@
+error[E0310]: the associated type `<P as Service>::Error` may not live long enough
+  --> $DIR/sod_service_chain.rs:31:10
+   |
+LL |     ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> {
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |          |
+   |          the associated type `<P as Service>::Error` must be valid for the static lifetime...
+   |          ...so that the type `<P as Service>::Error` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL |     ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> where <P as Service>::Error: 'static {
+   |                                                      ++++++++++++++++++++++++++++++++++++
+
+error[E0310]: the associated type `<S as Service>::Error` may not live long enough
+  --> $DIR/sod_service_chain.rs:31:10
+   |
+LL |     ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> {
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |          |
+   |          the associated type `<S as Service>::Error` must be valid for the static lifetime...
+   |          ...so that the type `<S as Service>::Error` will meet its required lifetime bounds
+   |
+help: consider adding an explicit lifetime bound
+   |
+LL |     ) -> ServiceChainBuilder<ServiceChain<P, S>, NS> where <S as Service>::Error: 'static {
+   |                                                      ++++++++++++++++++++++++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0310`.