diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 5b89dcf7dd8..bcfbd7b0e97 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -937,27 +937,7 @@ fn check_impl_items_against_trait<'tcx>(
 
     let trait_def = tcx.trait_def(trait_ref.def_id);
 
-    let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis());
-
-    let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
-    let cause = ObligationCause::misc(tcx.def_span(impl_id), impl_id);
-    let param_env = tcx.param_env(impl_id);
-
-    let self_is_guaranteed_unsized = tcx
-        .struct_tail_raw(
-            trait_ref.self_ty(),
-            |ty| {
-                ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| {
-                    Ty::new_error_with_message(
-                        tcx,
-                        tcx.def_span(impl_id),
-                        "struct tail should be computable",
-                    )
-                })
-            },
-            || (),
-        )
-        .is_guaranteed_unsized_raw();
+    let self_is_guaranteed_unsize_self = tcx.impl_self_is_guaranteed_unsized(impl_id);
 
     for &impl_item in impl_item_refs {
         let ty_impl_item = tcx.associated_item(impl_item);
@@ -988,7 +968,7 @@ fn check_impl_items_against_trait<'tcx>(
             }
         }
 
-        if self_is_guaranteed_unsized && tcx.generics_require_sized_self(ty_trait_item.def_id) {
+        if self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(ty_trait_item.def_id) {
             tcx.emit_node_span_lint(
                 rustc_lint_defs::builtin::DEAD_CODE,
                 tcx.local_def_id_to_hir_id(ty_impl_item.def_id.expect_local()),
@@ -1023,7 +1003,7 @@ fn check_impl_items_against_trait<'tcx>(
             if !is_implemented
                 && tcx.defaultness(impl_id).is_final()
                 // unsized types don't need to implement methods that have `Self: Sized` bounds.
-                && !(self_is_guaranteed_unsized && tcx.generics_require_sized_self(trait_item_id))
+                && !(self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(trait_item_id))
             {
                 missing_items.push(tcx.associated_item(trait_item_id));
             }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index a1df27ac788..40d0028db86 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1026,6 +1026,13 @@ rustc_queries! {
         separate_provide_extern
     }
 
+    /// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due
+    /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct
+    /// whose tail is one of those types.
+    query impl_self_is_guaranteed_unsized(impl_def_id: DefId) -> bool {
+        desc { |tcx| "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) }
+    }
+
     /// Maps a `DefId` of a type to a list of its inherent impls.
     /// Contains implementations of methods that are inherent to a type.
     /// Methods in these implementations don't need to be exported.
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 162ca1f4af8..abf6cbbcd87 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -437,6 +437,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         )
     }
 
+    fn impl_self_is_guaranteed_unsized(self, impl_def_id: DefId) -> bool {
+        self.impl_self_is_guaranteed_unsized(impl_def_id)
+    }
+
     fn has_target_features(self, def_id: DefId) -> bool {
         !self.codegen_fn_attrs(def_id).target_features.is_empty()
     }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index fb15ab8d848..27ee363f1c1 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2029,43 +2029,6 @@ impl<'tcx> Ty<'tcx> {
     pub fn is_known_rigid(self) -> bool {
         self.kind().is_known_rigid()
     }
-
-    /// Returns true if the type is guaranteed to be one of the three built-in unsized types:
-    /// `dyn Trait`/`[T]`/`str`. This function is *raw* because it does not compute the struct
-    /// tail of the type, so you are responsible for doing that yourself.
-    // NOTE: Keep this in sync with `rustc_type_ir`'s copy.
-    pub fn is_guaranteed_unsized_raw(self) -> bool {
-        match self.kind() {
-            Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
-            Bool
-            | Char
-            | Int(_)
-            | Uint(_)
-            | Float(_)
-            | Adt(_, _)
-            | Foreign(_)
-            | Array(_, _)
-            | Pat(_, _)
-            | RawPtr(_, _)
-            | Ref(_, _, _)
-            | FnDef(_, _)
-            | FnPtr(_, _)
-            | UnsafeBinder(_)
-            | Closure(_, _)
-            | CoroutineClosure(_, _)
-            | Coroutine(_, _)
-            | CoroutineWitness(_, _)
-            | Never
-            | Tuple(_)
-            | Alias(_, _)
-            | Param(_)
-            | Bound(_, _)
-            | Placeholder(_)
-            | Infer(_)
-            | Error(_)
-            | Dynamic(_, _, ty::DynStar) => false,
-        }
-    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Tys<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx>> {
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index f4f15ff6d46..87fb61623d1 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -237,7 +237,7 @@ where
                 // has a trivially false `Sized` predicate). If it's the latter, we cannot
                 // delay a bug because we can have trivially false where clauses, so we
                 // treat it as rigid.
-                if goal_trait_ref.self_ty().is_guaranteed_unsized_raw() {
+                if cx.impl_self_is_guaranteed_unsized(impl_def_id) {
                     ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
                     return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
                 } else {
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 8c5f1809a49..83591219b14 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -2007,21 +2007,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
             "confirm_impl_candidate: no associated type {:?} for {:?}",
             assoc_ty.item.name, obligation.predicate
         );
-        let tail = selcx.tcx().struct_tail_raw(
-            tcx.type_of(impl_def_id).instantiate(tcx, args),
-            |ty| {
-                normalize_with_depth_to(
-                    selcx,
-                    obligation.param_env,
-                    obligation.cause.clone(),
-                    obligation.recursion_depth + 1,
-                    ty,
-                    &mut nested,
-                )
-            },
-            || {},
-        );
-        if tail.is_guaranteed_unsized_raw() {
+        if tcx.impl_self_is_guaranteed_unsized(impl_def_id) {
             // We treat this projection as rigid here, which is represented via
             // `Projected::NoProgress`. This will ensure that the projection is
             // checked for well-formedness, and it's either satisfied by a trivial
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 9dc4f11e456..31d69eef5ec 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -3,6 +3,7 @@ use rustc_hir as hir;
 use rustc_hir::LangItem;
 use rustc_hir::def::DefKind;
 use rustc_index::bit_set::DenseBitSet;
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
@@ -312,6 +313,61 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSe
     unsizing_params
 }
 
+fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) -> bool {
+    debug_assert_eq!(tcx.def_kind(impl_def_id), DefKind::Impl { of_trait: true });
+
+    let infcx = tcx.infer_ctxt().ignoring_regions().build(ty::TypingMode::non_body_analysis());
+
+    let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
+    let cause = traits::ObligationCause::dummy();
+    let param_env = tcx.param_env(impl_def_id);
+
+    let tail = tcx.struct_tail_raw(
+        tcx.type_of(impl_def_id).instantiate_identity(),
+        |ty| {
+            ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| {
+                Ty::new_error_with_message(
+                    tcx,
+                    tcx.def_span(impl_def_id),
+                    "struct tail should be computable",
+                )
+            })
+        },
+        || (),
+    );
+
+    match tail.kind() {
+        ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
+        ty::Bool
+        | ty::Char
+        | ty::Int(_)
+        | ty::Uint(_)
+        | ty::Float(_)
+        | ty::Adt(_, _)
+        | ty::Foreign(_)
+        | ty::Array(_, _)
+        | ty::Pat(_, _)
+        | ty::RawPtr(_, _)
+        | ty::Ref(_, _, _)
+        | ty::FnDef(_, _)
+        | ty::FnPtr(_, _)
+        | ty::UnsafeBinder(_)
+        | ty::Closure(_, _)
+        | ty::CoroutineClosure(_, _)
+        | ty::Coroutine(_, _)
+        | ty::CoroutineWitness(_, _)
+        | ty::Never
+        | ty::Tuple(_)
+        | ty::Alias(_, _)
+        | ty::Param(_)
+        | ty::Bound(_, _)
+        | ty::Placeholder(_)
+        | ty::Infer(_)
+        | ty::Error(_)
+        | ty::Dynamic(_, _, ty::DynStar) => false,
+    }
+}
+
 pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         asyncness,
@@ -320,6 +376,7 @@ pub(crate) fn provide(providers: &mut Providers) {
         param_env_normalized_for_post_analysis,
         defaultness,
         unsizing_params_for_adt,
+        impl_self_is_guaranteed_unsized,
         ..*providers
     };
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index fce93b735d7..a9e6764e218 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -255,6 +255,8 @@ pub trait Interner:
         def_id: Self::DefId,
     ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>;
 
+    fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool;
+
     fn has_target_features(self, def_id: Self::DefId) -> bool;
 
     fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId;
diff --git a/tests/ui/associated-types/impl-wf-cycle-4.rs b/tests/ui/associated-types/impl-wf-cycle-4.rs
index bfa8adc71a1..1c1b3991d32 100644
--- a/tests/ui/associated-types/impl-wf-cycle-4.rs
+++ b/tests/ui/associated-types/impl-wf-cycle-4.rs
@@ -2,7 +2,7 @@ trait Filter {
     type ToMatch;
 }
 
-impl<T> Filter for T //~ ERROR overflow evaluating the requirement
+impl<T> Filter for T //~ ERROR cycle detected when
 where
     T: Fn(Self::ToMatch),
 {
diff --git a/tests/ui/associated-types/impl-wf-cycle-4.stderr b/tests/ui/associated-types/impl-wf-cycle-4.stderr
index cdbac267d34..c966579aecf 100644
--- a/tests/ui/associated-types/impl-wf-cycle-4.stderr
+++ b/tests/ui/associated-types/impl-wf-cycle-4.stderr
@@ -1,4 +1,4 @@
-error[E0275]: overflow evaluating the requirement `<T as Filter>::ToMatch == <T as Filter>::ToMatch`
+error[E0391]: cycle detected when computing normalized predicates of `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>`
   --> $DIR/impl-wf-cycle-4.rs:5:1
    |
 LL | / impl<T> Filter for T
@@ -6,20 +6,23 @@ LL | | where
 LL | |     T: Fn(Self::ToMatch),
    | |_________________________^
    |
-note: required for `T` to implement `Filter`
-  --> $DIR/impl-wf-cycle-4.rs:5:9
+note: ...which requires computing whether `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>` has a guaranteed unsized self type...
+  --> $DIR/impl-wf-cycle-4.rs:5:1
    |
-LL | impl<T> Filter for T
-   |         ^^^^^^     ^
-LL | where
-LL |     T: Fn(Self::ToMatch),
-   |        ----------------- unsatisfied trait bound introduced here
-note: associated types for the current `impl` cannot be restricted in `where` clauses
-  --> $DIR/impl-wf-cycle-4.rs:7:11
+LL | / impl<T> Filter for T
+LL | | where
+LL | |     T: Fn(Self::ToMatch),
+   | |_________________________^
+   = note: ...which again requires computing normalized predicates of `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>`, completing the cycle
+note: cycle used when checking that `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>` is well-formed
+  --> $DIR/impl-wf-cycle-4.rs:5:1
    |
-LL |     T: Fn(Self::ToMatch),
-   |           ^^^^^^^^^^^^^
+LL | / impl<T> Filter for T
+LL | | where
+LL | |     T: Fn(Self::ToMatch),
+   | |_________________________^
+   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0275`.
+For more information about this error, try `rustc --explain E0391`.
diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr
new file mode 100644
index 00000000000..bf8d3c40cf6
--- /dev/null
+++ b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr
@@ -0,0 +1,54 @@
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection-2.rs:22:12
+   |
+LL | const FOO: <Tail as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `Tail`, the trait `Sized` is not implemented for `[()]`
+note: required because it appears within the type `Tail`
+  --> $DIR/trivial-unsized-projection-2.rs:17:8
+   |
+LL | struct Tail([()]);
+   |        ^^^^
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection-2.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection-2.rs:22:12
+   |
+LL | const FOO: <Tail as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `Tail`, the trait `Sized` is not implemented for `[()]`
+note: required because it appears within the type `Tail`
+  --> $DIR/trivial-unsized-projection-2.rs:17:8
+   |
+LL | struct Tail([()]);
+   |        ^^^^
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection-2.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr
new file mode 100644
index 00000000000..bf8d3c40cf6
--- /dev/null
+++ b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr
@@ -0,0 +1,54 @@
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection-2.rs:22:12
+   |
+LL | const FOO: <Tail as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `Tail`, the trait `Sized` is not implemented for `[()]`
+note: required because it appears within the type `Tail`
+  --> $DIR/trivial-unsized-projection-2.rs:17:8
+   |
+LL | struct Tail([()]);
+   |        ^^^^
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection-2.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection-2.rs:22:12
+   |
+LL | const FOO: <Tail as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `Tail`, the trait `Sized` is not implemented for `[()]`
+note: required because it appears within the type `Tail`
+  --> $DIR/trivial-unsized-projection-2.rs:17:8
+   |
+LL | struct Tail([()]);
+   |        ^^^^
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection-2.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/trivial-unsized-projection-2.rs b/tests/ui/traits/trivial-unsized-projection-2.rs
new file mode 100644
index 00000000000..af4e12f6f90
--- /dev/null
+++ b/tests/ui/traits/trivial-unsized-projection-2.rs
@@ -0,0 +1,34 @@
+//@ revisions: good bad good_new bad_new
+//@[good_new] compile-flags: -Znext-solver
+//@[bad_new] compile-flags: -Znext-solver
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[good] check-pass
+//@[good_new] check-pass
+
+#![feature(trivial_bounds)]
+#![allow(trivial_bounds)]
+
+trait Bad {
+    type Assert
+    where
+        Self: Sized;
+}
+
+struct Tail([()]);
+
+impl Bad for Tail {}
+
+#[cfg(any(bad, bad_new))]
+const FOO: <Tail as Bad>::Assert = todo!();
+//[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time
+//[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time
+//[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time
+//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time
+
+#[cfg(any(good, good_new))]
+// Well-formed in trivially false param-env
+fn foo() where Tail: Sized {
+    let _: <Tail as Bad>::Assert;
+}
+
+fn main() {}