about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2023-03-21 01:46:52 +0100
committerLeón Orell Valerian Liehr <me@fmease.dev>2023-05-04 16:59:10 +0200
commite8139dfd5a747842a8303f33d8c842378913d594 (patch)
tree977f9a1bd7bc5c0e8efc36b3a23c911c64189cc3
parent6f8c0557e0b73c73a8a7163a15f4a5a3feca7d5c (diff)
downloadrust-e8139dfd5a747842a8303f33d8c842378913d594.tar.gz
rust-e8139dfd5a747842a8303f33d8c842378913d594.zip
IAT: Introduce AliasKind::Inherent
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs61
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/constrained_generic_params.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs2
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs3
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs7
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs7
-rw-r--r--compiler/rustc_lint/src/types.rs4
-rw-r--r--compiler/rustc_middle/src/query/mod.rs10
-rw-r--r--compiler/rustc_middle/src/ty/context.rs14
-rw-r--r--compiler/rustc_middle/src/ty/error.rs4
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs14
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/mod.rs6
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs2
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs5
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs51
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs3
-rw-r--r--compiler/rustc_privacy/src/lib.rs33
-rw-r--r--compiler/rustc_trait_selection/messages.ftl2
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs8
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs143
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs3
-rw-r--r--compiler/rustc_traits/src/chalk/lowering.rs1
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs29
-rw-r--r--compiler/rustc_type_ir/src/lib.rs27
-rw-r--r--compiler/rustc_type_ir/src/sty.rs1
-rw-r--r--src/librustdoc/clean/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs1
-rw-r--r--tests/rustdoc/inherent-projections.rs14
-rw-r--r--tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs10
-rw-r--r--tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr49
-rw-r--r--tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs16
-rw-r--r--tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr37
-rw-r--r--tests/ui/associated-inherent-types/bugs/ice-substitution.rs23
-rw-r--r--tests/ui/associated-inherent-types/bugs/ice-substitution.stderr6
-rw-r--r--tests/ui/associated-inherent-types/bugs/inference-fail.rs15
-rw-r--r--tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs19
-rw-r--r--tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs15
-rw-r--r--tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs2
-rw-r--r--tests/ui/associated-inherent-types/former-subst-ice.rs16
-rw-r--r--tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr15
-rw-r--r--tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr15
-rw-r--r--tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr11
-rw-r--r--tests/ui/associated-inherent-types/generic-associated-types-bad.rs26
-rw-r--r--tests/ui/associated-inherent-types/inference-fail.rs11
-rw-r--r--tests/ui/associated-inherent-types/inference-fail.stderr (renamed from tests/ui/associated-inherent-types/bugs/inference-fail.stderr)6
-rw-r--r--tests/ui/associated-inherent-types/inference.rs39
-rw-r--r--tests/ui/associated-inherent-types/late-bound-regions.rs25
-rw-r--r--tests/ui/associated-inherent-types/late-bound-regions.stderr12
-rw-r--r--tests/ui/associated-inherent-types/normalization-overflow.rs12
-rw-r--r--tests/ui/associated-inherent-types/normalization-overflow.stderr8
-rw-r--r--tests/ui/associated-inherent-types/private-in-public.rs26
-rw-r--r--tests/ui/associated-inherent-types/private-in-public.stderr34
-rw-r--r--tests/ui/associated-inherent-types/regionck-0.rs14
-rw-r--r--tests/ui/associated-inherent-types/regionck-0.stderr10
-rw-r--r--tests/ui/associated-inherent-types/regionck-1.rs13
-rw-r--r--tests/ui/associated-inherent-types/regionck-1.stderr29
-rw-r--r--tests/ui/associated-inherent-types/regionck-2.rs14
-rw-r--r--tests/ui/associated-inherent-types/regionck-2.stderr18
-rw-r--r--tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs26
-rw-r--r--tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs12
-rw-r--r--tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr17
-rw-r--r--tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs14
-rw-r--r--tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr18
82 files changed, 1008 insertions, 167 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index c199b9da6fe..e50e4800621 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -2378,6 +2378,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             return Ok(None);
         }
 
+        //
+        // Select applicable inherent associated type candidates modulo regions.
+        //
+
         // In contexts that have no inference context, just make a new one.
         // We do need a local variable to store it, though.
         let infcx_;
@@ -2390,7 +2394,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
         };
 
-        let param_env = tcx.param_env(block.owner.to_def_id());
+        // FIXME(inherent_associated_types): Acquiring the ParamEnv this early leads to cycle errors
+        // when inside of an ADT (#108491) or where clause.
+        let param_env = tcx.param_env(block.owner);
         let cause = ObligationCause::misc(span, block.owner.def_id);
 
         let mut fulfillment_errors = Vec::new();
@@ -2398,6 +2404,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             let universe = infcx.create_next_universe();
 
             // Regions are not considered during selection.
+            // FIXME(non_lifetime_binders): Here we are "truncating" or "flattening" the universes
+            // of type and const binders. Is that correct in the selection phase? See also #109505.
             let self_ty = tcx.replace_escaping_bound_vars_uncached(
                 self_ty,
                 FnMutDelegate {
@@ -2413,41 +2421,40 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
             candidates
                 .iter()
-                .filter_map(|&(impl_, (assoc_item, def_scope))| {
+                .copied()
+                .filter(|&(impl_, _)| {
                     infcx.probe(|_| {
                         let ocx = ObligationCtxt::new_in_snapshot(&infcx);
 
-                        let impl_ty = tcx.type_of(impl_);
                         let impl_substs = infcx.fresh_item_substs(impl_);
-                        let impl_ty = impl_ty.subst(tcx, impl_substs);
+                        let impl_ty = tcx.type_of(impl_).subst(tcx, impl_substs);
                         let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
 
-                        // Check that the Self-types can be related.
-                        // FIXME(fmease): Should we use `eq` here?
-                        ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).ok()?;
+                        // Check that the self types can be related.
+                        // FIXME(inherent_associated_types): Should we use `eq` here? Method probing uses
+                        // `sup` for this situtation, too. What for? To constrain inference variables?
+                        if ocx.sup(&ObligationCause::dummy(), param_env, impl_ty, self_ty).is_err()
+                        {
+                            return false;
+                        }
 
                         // Check whether the impl imposes obligations we have to worry about.
-                        let impl_bounds = tcx.predicates_of(impl_);
-                        let impl_bounds = impl_bounds.instantiate(tcx, impl_substs);
-
+                        let impl_bounds = tcx.predicates_of(impl_).instantiate(tcx, impl_substs);
                         let impl_bounds = ocx.normalize(&cause, param_env, impl_bounds);
-
                         let impl_obligations = traits::predicates_for_generics(
                             |_, _| cause.clone(),
                             param_env,
                             impl_bounds,
                         );
-
                         ocx.register_obligations(impl_obligations);
 
                         let mut errors = ocx.select_where_possible();
                         if !errors.is_empty() {
                             fulfillment_errors.append(&mut errors);
-                            return None;
+                            return false;
                         }
 
-                        // FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot.
-                        Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs)))
+                        true
                     })
                 })
                 .collect()
@@ -2456,24 +2463,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         if applicable_candidates.len() > 1 {
             return Err(self.complain_about_ambiguous_inherent_assoc_type(
                 name,
-                applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(),
+                applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
                 span,
             ));
         }
 
-        if let Some((assoc_item, def_scope, impl_substs)) = applicable_candidates.pop() {
+        if let Some((impl_, (assoc_item, def_scope))) = applicable_candidates.pop() {
             self.check_assoc_ty(assoc_item, name, def_scope, block, span);
 
-            // FIXME(inherent_associated_types): To fully *confirm* the *probed* candidate, we still
-            // need to relate the Self-type with fresh item substs & register region obligations for
-            // regionck to prove/disprove.
-
-            let item_substs =
-                self.create_substs_for_associated_item(span, assoc_item, segment, impl_substs);
+            // FIXME(fmease): Currently creating throwaway `parent_substs` to please
+            // `create_substs_for_associated_item`. Modify the latter instead (or sth. similar) to
+            // not require the parent substs logic.
+            let parent_substs = InternalSubsts::identity_for_item(tcx, impl_);
+            let substs =
+                self.create_substs_for_associated_item(span, assoc_item, segment, parent_substs);
+            let substs = tcx.mk_substs_from_iter(
+                std::iter::once(ty::GenericArg::from(self_ty))
+                    .chain(substs.into_iter().skip(parent_substs.len())),
+            );
 
-            // FIXME(fmease, #106722): Check if the bounds on the parameters of the
-            // associated type hold, if any.
-            let ty = tcx.type_of(assoc_item).subst(tcx, item_substs);
+            let ty = tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(assoc_item, substs));
 
             return Ok(Some((ty, assoc_item)));
         }
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 5ce8a83aad7..272177dfbd0 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -210,6 +210,19 @@ fn do_orphan_check_impl<'tcx>(
                 NonlocalImpl::DisallowOther,
             ),
 
+            // ```
+            // struct S<T>(T);
+            // impl<T: ?Sized> S<T> {
+            //     type This = T;
+            // }
+            // impl<T: ?Sized> AutoTrait for S<T>::This {}
+            // ```
+            // FIXME(inherent_associated_types): The example code above currently leads to a cycle
+            ty::Alias(AliasKind::Inherent, _) => (
+                LocalImpl::Disallow { problematic_kind: "associated type" },
+                NonlocalImpl::DisallowOther,
+            ),
+
             // type Opaque = impl Trait;
             // impl AutoTrait for Opaque {}
             ty::Alias(AliasKind::Opaque, _) => (
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 2e5d058c6ed..cb80e5b1350 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1938,7 +1938,7 @@ fn is_late_bound_map(
                 ty::Param(param_ty) => {
                     self.arg_is_constrained[param_ty.index as usize] = true;
                 }
-                ty::Alias(ty::Projection, _) => return ControlFlow::Continue(()),
+                ty::Alias(ty::Projection | ty::Inherent, _) => return ControlFlow::Continue(()),
                 _ => (),
             }
             t.super_visit_with(self)
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index c20fbfd1e40..8df0166f76b 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -127,7 +127,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
             // the def_id that this query was called with. We filter to only type and const args here
             // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
             // but it can't hurt to be safe ^^
-            if let ty::Alias(ty::Projection, projection) = ty.kind() {
+            if let ty::Alias(ty::Projection | ty::Inherent, projection) = ty.kind() {
                 let generics = tcx.generics_of(projection.def_id);
 
                 let arg_index = segment
diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index e18b0f08279..9200c2aecf5 100644
--- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -59,7 +59,7 @@ struct ParameterCollector {
 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match *t.kind() {
-            ty::Alias(ty::Projection, ..) if !self.include_nonconstraining => {
+            ty::Alias(ty::Projection | ty::Inherent, ..) if !self.include_nonconstraining => {
                 // projections are not injective
                 return ControlFlow::Continue(());
             }
diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
index d53c429ca15..0cd2fc1aa29 100644
--- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
@@ -210,6 +210,9 @@ fn insert_required_predicates_to_be_wf<'tcx>(
                 );
             }
 
+            // FIXME(inherent_associated_types): Handle this case properly.
+            ty::Alias(ty::Inherent, _) => {}
+
             _ => {}
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 70ce45e21ea..05e5d850bf9 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -843,7 +843,7 @@ fn find_param_in_ty<'tcx>(
             return true;
         }
         if let ty::GenericArgKind::Type(ty) = arg.unpack()
-                && let ty::Alias(ty::Projection, ..) = ty.kind()
+                && let ty::Alias(ty::Projection | ty::Inherent, ..) = ty.kind()
             {
                 // This logic may seem a bit strange, but typically when
                 // we have a projection type in a function signature, the
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 73a7bbebb65..67f45f9aa3f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -300,7 +300,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
         match ty.kind() {
             ty::Adt(adt_def, _) => Some(*adt_def),
             // FIXME(#104767): Should we handle bound regions here?
-            ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => {
+            ty::Alias(ty::Projection | ty::Inherent, _) if !ty.has_escaping_bound_vars() => {
                 self.normalize(span, ty).ty_adt_def()
             }
             _ => None,
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 30f0978d190..3741672e568 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -2211,7 +2211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         | ty::Float(_)
                         | ty::Adt(_, _)
                         | ty::Str
-                        | ty::Alias(ty::Projection, _)
+                        | ty::Alias(ty::Projection | ty::Inherent, _)
                         | ty::Param(_) => format!("{deref_ty}"),
                         // we need to test something like  <&[_]>::len or <(&[u32])>::len
                         // and Vec::function();
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index c9e13be02ff..44c8b08196e 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -125,7 +125,8 @@ impl<'tcx> InferCtxt<'tcx> {
                 bug!()
             }
 
-            (_, ty::Alias(AliasKind::Projection, _)) | (ty::Alias(AliasKind::Projection, _), _)
+            (_, ty::Alias(AliasKind::Projection | AliasKind::Inherent, _))
+            | (ty::Alias(AliasKind::Projection | AliasKind::Inherent, _), _)
                 if self.tcx.trait_solver_next() =>
             {
                 relation.register_type_relate_obligation(a, b);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 98da5ba65b7..ce70f39cc40 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -2354,7 +2354,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let labeled_user_string = match bound_kind {
             GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
             GenericKind::Alias(ref p) => match p.kind(self.tcx) {
-                ty::AliasKind::Projection => format!("the associated type `{}`", p),
+                ty::AliasKind::Projection | ty::AliasKind::Inherent => {
+                    format!("the associated type `{}`", p)
+                }
                 ty::AliasKind::Opaque => format!("the opaque type `{}`", p),
             },
         };
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index a3116351940..064811bd29d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -71,9 +71,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                              #traits-as-parameters",
                         );
                     }
-                    (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => {
+                    (ty::Alias(ty::Projection | ty::Inherent, _), ty::Alias(ty::Projection | ty::Inherent, _)) => {
                         diag.note("an associated type was expected, but a different one was found");
                     }
+                    // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
                     (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
                         if !tcx.is_impl_trait_in_trait(proj.def_id) =>
                     {
@@ -222,7 +223,7 @@ impl<T> Trait<T> for X {
                             diag.span_label(p_span, "this type parameter");
                         }
                     }
-                    (ty::Alias(ty::Projection, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
+                    (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
                         self.expected_projection(
                             diag,
                             proj_ty,
@@ -231,7 +232,7 @@ impl<T> Trait<T> for X {
                             cause.code(),
                         );
                     }
-                    (_, ty::Alias(ty::Projection, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
+                    (_, ty::Alias(ty::Projection | ty::Inherent, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
                         let msg = format!(
                             "consider constraining the associated type `{}` to `{}`",
                             values.found, values.expected,
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 334395945ea..362b22b23a8 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -549,6 +549,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     // We can't normalize associated types from `rustc_infer`,
                     // but we can eagerly register inference variables for them.
                     // FIXME(RPITIT): Don't replace RPITITs with inference vars.
+                    // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
                     ty::Alias(ty::Projection, projection_ty)
                         if !projection_ty.has_escaping_bound_vars()
                             && !tcx.is_impl_trait_in_trait(projection_ty.def_id) =>
@@ -569,6 +570,7 @@ impl<'tcx> InferCtxt<'tcx> {
                         hidden_ty
                     }
                     // FIXME(RPITIT): This can go away when we move to associated types
+                    // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
                     ty::Alias(
                         ty::Projection,
                         ty::AliasTy { def_id: def_id2, substs: substs2, .. },
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index aeb791901bd..08c02e1c253 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -62,6 +62,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::layout::{LayoutError, LayoutOf};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
 use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
 use rustc_span::edition::Edition;
@@ -1461,6 +1462,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
             // Bounds are respected for `type X = impl Trait`
             return;
         }
+        if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() {
+            // Bounds are respected for `type X = … Type::Inherent …`
+            return;
+        }
         // There must not be a where clause
         if type_alias_generics.predicates.is_empty() {
             return;
@@ -1580,7 +1585,6 @@ declare_lint_pass!(
 
 impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
-        use rustc_middle::ty::visit::TypeVisitableExt;
         use rustc_middle::ty::Clause;
         use rustc_middle::ty::PredicateKind::*;
 
@@ -2917,6 +2921,7 @@ impl ClashingExternDeclarations {
                         | (Generator(..), Generator(..))
                         | (GeneratorWitness(..), GeneratorWitness(..))
                         | (Alias(ty::Projection, ..), Alias(ty::Projection, ..))
+                        | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..))
                         | (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false,
 
                         // These definitely should have been caught above.
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index a6ba742201a..125b4dc5503 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1119,14 +1119,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
             // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
             //  so they are currently ignored for the purposes of this lint.
-            ty::Param(..) | ty::Alias(ty::Projection, ..)
+            ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
                 if matches!(self.mode, CItemKind::Definition) =>
             {
                 FfiSafe
             }
 
             ty::Param(..)
-            | ty::Alias(ty::Projection, ..)
+            | ty::Alias(ty::Projection | ty::Inherent, ..)
             | ty::Infer(..)
             | ty::Bound(..)
             | ty::Error(_)
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index be2657d25a6..ab73832ae68 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1821,6 +1821,16 @@ rustc_queries! {
         desc { "normalizing `{}`", goal.value.value }
     }
 
+    /// Do not call this query directly: invoke `normalize` instead.
+    query normalize_inherent_projection_ty(
+        goal: CanonicalProjectionGoal<'tcx>
+    ) -> Result<
+        &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
+        NoSolution,
+    > {
+        desc { "normalizing `{}`", goal.value.value }
+    }
+
     /// Do not call this query directly: invoke `try_normalize_erasing_regions` instead.
     query try_normalize_generic_arg_after_erasing_regions(
         goal: ParamEnvAnd<'tcx, GenericArg<'tcx>>
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index c62254cd79c..212720f3b69 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1846,7 +1846,17 @@ impl<'tcx> TyCtxt<'tcx> {
         let substs = substs.into_iter().map(Into::into);
         #[cfg(debug_assertions)]
         {
-            let n = self.generics_of(_def_id).count();
+            let generics = self.generics_of(_def_id);
+
+            let n = if let DefKind::AssocTy = self.def_kind(_def_id)
+                && let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id))
+            {
+                // If this is an inherent projection.
+
+                generics.params.len() + 1
+            } else {
+                generics.count()
+            };
             assert_eq!(
                 (n, Some(n)),
                 substs.size_hint(),
@@ -2007,7 +2017,7 @@ impl<'tcx> TyCtxt<'tcx> {
         debug_assert_matches!(
             (kind, self.def_kind(alias_ty.def_id)),
             (ty::Opaque, DefKind::OpaqueTy)
-                | (ty::Projection, DefKind::AssocTy)
+                | (ty::Projection | ty::Inherent, DefKind::AssocTy)
                 | (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder)
         );
         self.mk_ty_from_kind(Alias(kind, alias_ty))
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 1be61e16dbe..49ab9b79e96 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -265,7 +265,7 @@ impl<'tcx> Ty<'tcx> {
             ty::Infer(ty::FreshTy(_)) => "fresh type".into(),
             ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".into(),
             ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".into(),
-            ty::Alias(ty::Projection, _) => "associated type".into(),
+            ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(),
             ty::Param(p) => format!("type parameter `{p}`").into(),
             ty::Alias(ty::Opaque, ..) => if tcx.ty_is_opaque_future(self) { "future".into() } else { "opaque type".into() },
             ty::Error(_) => "type error".into(),
@@ -312,7 +312,7 @@ impl<'tcx> Ty<'tcx> {
             ty::Tuple(..) => "tuple".into(),
             ty::Placeholder(..) => "higher-ranked type".into(),
             ty::Bound(..) => "bound type variable".into(),
-            ty::Alias(ty::Projection, _) => "associated type".into(),
+            ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(),
             ty::Param(_) => "type parameter".into(),
             ty::Alias(ty::Opaque, ..) => "opaque type".into(),
         }
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 68002bfcfbd..d64875a9f00 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -176,14 +176,14 @@ impl FlagComputation {
                 self.add_substs(substs);
             }
 
-            &ty::Alias(ty::Projection, data) => {
-                self.add_flags(TypeFlags::HAS_TY_PROJECTION);
-                self.add_alias_ty(data);
-            }
+            &ty::Alias(kind, data) => {
+                self.add_flags(match kind {
+                    ty::Projection => TypeFlags::HAS_TY_PROJECTION,
+                    ty::Inherent => TypeFlags::HAS_TY_INHERENT,
+                    ty::Opaque => TypeFlags::HAS_TY_OPAQUE,
+                });
 
-            &ty::Alias(ty::Opaque, ty::AliasTy { substs, .. }) => {
-                self.add_flags(TypeFlags::HAS_TY_OPAQUE);
-                self.add_substs(substs);
+                self.add_alias_ty(data);
             }
 
             &ty::Dynamic(obj, r, _) => {
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
index 92a040068dd..9e672004cf9 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
@@ -113,6 +113,12 @@ impl<'tcx> Ty<'tcx> {
             }
             Never => InhabitedPredicate::False,
             Param(_) | Alias(ty::Projection, _) => InhabitedPredicate::GenericType(self),
+            // FIXME(inherent_associated_types): Most likely we can just map to `GenericType` like above.
+            // However it's unclear if the substs passed to `InhabitedPredicate::subst` are of the correct
+            // format, i.e. don't contain parent substs. If you hit this case, please verify this beforehand.
+            Alias(ty::Inherent, _) => {
+                bug!("unimplemented: inhabitedness checking for inherent projections")
+            }
             Tuple(tys) if tys.is_empty() => InhabitedPredicate::True,
             // use a query for more complex cases
             Adt(..) | Array(..) | Tuple(_) => tcx.inhabited_predicate_type(self),
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index f2a2e67cf82..47cf48f46cf 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -324,7 +324,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
                 let non_zero = !ty.is_unsafe_ptr();
                 let tail = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
                 match tail.kind() {
-                    ty::Param(_) | ty::Alias(ty::Projection, _) => {
+                    ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => {
                         debug_assert!(tail.has_non_region_param());
                         Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
                     }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 23d6242899f..9fc0110b354 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1004,7 +1004,7 @@ impl<'tcx> Term<'tcx> {
         match self.unpack() {
             TermKind::Ty(ty) => match ty.kind() {
                 ty::Alias(kind, alias_ty) => match kind {
-                    AliasKind::Projection => Some(*alias_ty),
+                    AliasKind::Projection | AliasKind::Inherent => Some(*alias_ty),
                     AliasKind::Opaque => None,
                 },
                 _ => None,
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 6ac9f950450..8a4d3820d7b 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -729,7 +729,7 @@ pub trait PrettyPrinter<'tcx>:
             ty::Foreign(def_id) => {
                 p!(print_def_path(def_id, &[]));
             }
-            ty::Alias(ty::Projection, ref data) => {
+            ty::Alias(ty::Projection | ty::Inherent, ref data) => {
                 if !(self.should_print_verbose() || NO_QUERIES.with(|q| q.get()))
                     && self.tcx().is_impl_trait_in_trait(data.def_id)
                 {
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 7f28ed6c263..da43475941e 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -550,6 +550,11 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>(
             Ok(tcx.mk_projection(projection_ty.def_id, projection_ty.substs))
         }
 
+        (&ty::Alias(ty::Inherent, a_data), &ty::Alias(ty::Inherent, b_data)) => {
+            let alias_ty = relation.relate(a_data, b_data)?;
+            Ok(tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(alias_ty.def_id, alias_ty.substs)))
+        }
+
         (
             &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, substs: a_substs, .. }),
             &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, substs: b_substs, .. }),
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 646384eebc8..8d0737e1eee 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1190,9 +1190,9 @@ where
 
 /// Represents the projection of an associated type.
 ///
-/// For a projection, this would be `<Ty as Trait<...>>::N`.
-///
-/// For an opaque type, there is no explicit syntax.
+/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
+/// * For an inherent projection, this would be `Ty::N<...>`.
+/// * For an opaque type, there is no explicit syntax.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
 #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
 pub struct AliasTy<'tcx> {
@@ -1201,12 +1201,16 @@ pub struct AliasTy<'tcx> {
     /// For a projection, these are the substitutions for the trait and the
     /// GAT substitutions, if there are any.
     ///
+    /// For an inherent projection, they consist of the self type and the GAT substitutions,
+    /// if there are any.
+    ///
     /// For RPIT the substitutions are for the generics of the function,
     /// while for TAIT it is used for the generic parameters of the alias.
     pub substs: SubstsRef<'tcx>,
 
-    /// The `DefId` of the `TraitItem` for the associated type `N` if this is a projection,
-    /// or the `OpaqueType` item if this is an opaque.
+    /// The `DefId` of the `TraitItem` or `ImplItem` for the associated type `N` depending on whether
+    /// this is a projection or an inherent projection or the `DefId` of the `OpaqueType` item if
+    /// this is an opaque.
     ///
     /// During codegen, `tcx.type_of(def_id)` can be used to get the type of the
     /// underlying type if the type is an opaque.
@@ -1224,6 +1228,7 @@ pub struct AliasTy<'tcx> {
 impl<'tcx> AliasTy<'tcx> {
     pub fn kind(self, tcx: TyCtxt<'tcx>) -> ty::AliasKind {
         match tcx.def_kind(self.def_id) {
+            DefKind::AssocTy if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(self.def_id)) => ty::Inherent,
             DefKind::AssocTy | DefKind::ImplTraitPlaceholder => ty::Projection,
             DefKind::OpaqueTy => ty::Opaque,
             kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),
@@ -1237,6 +1242,17 @@ impl<'tcx> AliasTy<'tcx> {
 
 /// The following methods work only with associated type projections.
 impl<'tcx> AliasTy<'tcx> {
+    pub fn self_ty(self) -> Ty<'tcx> {
+        self.substs.type_at(0)
+    }
+
+    pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+        tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1)))
+    }
+}
+
+/// The following methods work only with trait associated type projections.
+impl<'tcx> AliasTy<'tcx> {
     pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
         match tcx.def_kind(self.def_id) {
             DefKind::AssocTy | DefKind::AssocConst => tcx.parent(self.def_id),
@@ -1274,13 +1290,28 @@ impl<'tcx> AliasTy<'tcx> {
         let def_id = self.trait_def_id(tcx);
         ty::TraitRef::new(tcx, def_id, self.substs.truncate_to(tcx, tcx.generics_of(def_id)))
     }
+}
 
-    pub fn self_ty(self) -> Ty<'tcx> {
-        self.substs.type_at(0)
-    }
+/// The following methods work only with inherent associated type projections.
+impl<'tcx> AliasTy<'tcx> {
+    /// Transform the substitutions to have the given `impl` substs as the base and the GAT substs on top of that.
+    ///
+    /// Does the following transformation:
+    ///
+    /// ```text
+    /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m]
+    ///
+    ///     I_i impl subst
+    ///     P_j GAT subst
+    /// ```
+    pub fn rebase_substs_onto_impl(
+        self,
+        impl_substs: ty::SubstsRef<'tcx>,
+        tcx: TyCtxt<'tcx>,
+    ) -> ty::SubstsRef<'tcx> {
+        debug_assert_eq!(self.kind(tcx), ty::Inherent);
 
-    pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
-        tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1)))
+        tcx.mk_substs_from_iter(impl_substs.into_iter().chain(self.substs.into_iter().skip(1)))
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 5eaa58d69ed..520bb55e031 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -49,6 +49,9 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
     fn has_projections(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_PROJECTION)
     }
+    fn has_inherent_projections(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
+    }
     fn has_opaque_types(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
     }
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 04ac585076f..d6eb5463870 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -243,6 +243,39 @@ where
                 // This will also visit substs if necessary, so we don't need to recurse.
                 return self.visit_projection_ty(proj);
             }
+            ty::Alias(ty::Inherent, data) => {
+                if self.def_id_visitor.skip_assoc_tys() {
+                    // Visitors searching for minimal visibility/reachability want to
+                    // conservatively approximate associated types like `Type::Alias`
+                    // as visible/reachable even if `Type` is private.
+                    // Ideally, associated types should be substituted in the same way as
+                    // free type aliases, but this isn't done yet.
+                    return ControlFlow::Continue(());
+                }
+
+                self.def_id_visitor.visit_def_id(
+                    data.def_id,
+                    "associated type",
+                    &LazyDefPathStr { def_id: data.def_id, tcx },
+                )?;
+
+                struct LazyDefPathStr<'tcx> {
+                    def_id: DefId,
+                    tcx: TyCtxt<'tcx>,
+                }
+                impl<'tcx> fmt::Display for LazyDefPathStr<'tcx> {
+                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                        write!(f, "{}", self.tcx.def_path_str(self.def_id))
+                    }
+                }
+
+                // This will also visit substs if necessary, so we don't need to recurse.
+                return if self.def_id_visitor.shallow() {
+                    ControlFlow::Continue(())
+                } else {
+                    data.substs.iter().try_for_each(|subst| subst.visit_with(self))
+                };
+            }
             ty::Dynamic(predicates, ..) => {
                 // All traits in the list are considered the "primary" part of the type
                 // and are visited by shallow visitors.
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 14eb4a5502d..8fea3fc140d 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -20,3 +20,5 @@ trait_selection_negative_positive_conflict = found both positive and negative im
     .negative_implementation_in_crate = negative implementation in crate `{$negative_impl_cname}`
     .positive_implementation_here = positive implementation here
     .positive_implementation_in_crate = positive implementation in crate `{$positive_impl_cname}`
+
+trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}`
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index df7c4df1868..54e22cc3d7f 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -89,3 +89,11 @@ impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> {
         diag
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_inherent_projection_normalization_overflow)]
+pub struct InherentProjectionNormalizationOverflow {
+    #[primary_span]
+    pub span: Span,
+    pub ty: String,
+}
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 996dc329dcb..0ede32c753c 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -33,7 +33,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         ty::Dynamic(..)
         | ty::Param(..)
         | ty::Foreign(..)
-        | ty::Alias(ty::Projection, ..)
+        | ty::Alias(ty::Projection | ty::Inherent, ..)
         | ty::Placeholder(..)
         | ty::Bound(..)
         | ty::Infer(_) => {
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 6c98fadd148..04b38edc126 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -655,7 +655,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             ty::Dynamic(..)
             | ty::Param(..)
             | ty::Foreign(..)
-            | ty::Alias(ty::Projection, ..)
+            | ty::Alias(ty::Projection | ty::Inherent, ..)
             | ty::Placeholder(..) => Some(Err(NoSolution)),
 
             ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index b7690f79933..402b09419c8 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -673,7 +673,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
             | ty::RawPtr(..)
             | ty::Never
             | ty::Tuple(..)
-            | ty::Alias(ty::Projection, ..) => self.found_non_local_ty(ty),
+            | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty),
 
             ty::Param(..) => self.found_param_ty(ty),
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 8f2a5d649f0..3884153202e 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1686,13 +1686,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 ty::Tuple(..) => Some(10),
                 ty::Param(..) => Some(11),
                 ty::Alias(ty::Projection, ..) => Some(12),
-                ty::Alias(ty::Opaque, ..) => Some(13),
-                ty::Never => Some(14),
-                ty::Adt(..) => Some(15),
-                ty::Generator(..) => Some(16),
-                ty::Foreign(..) => Some(17),
-                ty::GeneratorWitness(..) => Some(18),
-                ty::GeneratorWitnessMIR(..) => Some(19),
+                ty::Alias(ty::Inherent, ..) => Some(13),
+                ty::Alias(ty::Opaque, ..) => Some(14),
+                ty::Never => Some(15),
+                ty::Adt(..) => Some(16),
+                ty::Generator(..) => Some(17),
+                ty::Foreign(..) => Some(18),
+                ty::GeneratorWitness(..) => Some(19),
+                ty::GeneratorWitnessMIR(..) => Some(20),
                 ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None,
             }
         }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 8b8c50f6b83..5878accd84a 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -49,7 +49,8 @@ pub use self::object_safety::astconv_object_safety_violations;
 pub use self::object_safety::is_vtable_safe_method;
 pub use self::object_safety::MethodViolationCode;
 pub use self::object_safety::ObjectSafetyViolation;
-pub use self::project::{normalize_projection_type, NormalizeExt};
+pub use self::project::NormalizeExt;
+pub use self::project::{normalize_inherent_projection, normalize_projection_type};
 pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
 pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
 pub use self::specialize::specialization_graph::FutureCompatOverlapError;
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 8c74860cdf3..852780fccac 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -16,6 +16,7 @@ use super::{
 };
 use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
 
+use crate::errors::InherentProjectionNormalizationOverflow;
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
 use crate::traits::error_reporting::TypeErrCtxtExt as _;
@@ -370,10 +371,14 @@ pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
     reveal: Reveal,
 ) -> bool {
     match reveal {
-        Reveal::UserFacing => value
-            .has_type_flags(ty::TypeFlags::HAS_TY_PROJECTION | ty::TypeFlags::HAS_CT_PROJECTION),
+        Reveal::UserFacing => value.has_type_flags(
+            ty::TypeFlags::HAS_TY_PROJECTION
+                | ty::TypeFlags::HAS_TY_INHERENT
+                | ty::TypeFlags::HAS_CT_PROJECTION,
+        ),
         Reveal::All => value.has_type_flags(
             ty::TypeFlags::HAS_TY_PROJECTION
+                | ty::TypeFlags::HAS_TY_INHERENT
                 | ty::TypeFlags::HAS_TY_OPAQUE
                 | ty::TypeFlags::HAS_CT_PROJECTION,
         ),
@@ -616,6 +621,51 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
                 );
                 normalized_ty
             }
+
+            ty::Inherent if !data.has_escaping_bound_vars() => {
+                // This branch is *mostly* just an optimization: when we don't
+                // have escaping bound vars, we don't need to replace them with
+                // placeholders (see branch below). *Also*, we know that we can
+                // register an obligation to *later* project, since we know
+                // there won't be bound vars there.
+
+                let data = data.fold_with(self);
+
+                // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement`
+                // here like `ty::Projection`?
+                normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    &mut self.obligations,
+                )
+            }
+
+            ty::Inherent => {
+                let infcx = self.selcx.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let data = data.fold_with(self);
+                let ty = normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    &mut self.obligations,
+                );
+
+                PlaceholderReplacer::replace_placeholders(
+                    infcx,
+                    mapped_regions,
+                    mapped_types,
+                    mapped_consts,
+                    &self.universes,
+                    ty,
+                )
+            }
         }
     }
 
@@ -1204,6 +1254,95 @@ fn normalize_to_error<'a, 'tcx>(
     Normalized { value: new_value, obligations: vec![trait_obligation] }
 }
 
+/// Confirm and normalize the given inherent projection.
+#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
+pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    alias_ty: ty::AliasTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> Ty<'tcx> {
+    let tcx = selcx.tcx();
+
+    if !tcx.recursion_limit().value_within_limit(depth) {
+        // Halt compilation because it is important that overflows never be masked.
+        tcx.sess.emit_fatal(InherentProjectionNormalizationOverflow {
+            span: cause.span,
+            ty: alias_ty.to_string(),
+        });
+    }
+
+    let impl_def_id = tcx.parent(alias_ty.def_id);
+    let impl_substs = selcx.infcx.fresh_substs_for_item(cause.span, impl_def_id);
+
+    let impl_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs);
+    let impl_ty =
+        normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, impl_ty, obligations);
+
+    // Infer the generic parameters of the impl by unifying the
+    // impl type with the self type of the projection.
+    let self_ty = alias_ty.self_ty();
+    match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) {
+        Ok(mut ok) => obligations.append(&mut ok.obligations),
+        Err(_) => {
+            tcx.sess.delay_span_bug(
+                cause.span,
+                format!(
+                    "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not"
+                ),
+            );
+        }
+    }
+
+    let substs = alias_ty.rebase_substs_onto_impl(impl_substs, tcx);
+
+    // Register the obligations arising from the impl and from the associated type itself.
+    let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, substs);
+    for (predicate, span) in predicates {
+        let predicate = normalize_with_depth_to(
+            selcx,
+            param_env,
+            cause.clone(),
+            depth + 1,
+            predicate,
+            obligations,
+        );
+
+        let nested_cause = ObligationCause::new(
+            cause.span,
+            cause.body_id,
+            // FIXME(inherent_associated_types): Since we can't pass along the self type to the
+            // cause code, inherent projections will be printed with identity substitutions in
+            // diagnostics which is not ideal.
+            // Consider creating separate cause codes for this specific situation.
+            if span.is_dummy() {
+                super::ItemObligation(alias_ty.def_id)
+            } else {
+                super::BindingObligation(alias_ty.def_id, span)
+            },
+        );
+
+        obligations.push(Obligation::with_depth(
+            tcx,
+            nested_cause,
+            depth + 1,
+            param_env,
+            predicate,
+        ));
+    }
+
+    let ty = tcx.type_of(alias_ty.def_id).subst(tcx, substs);
+
+    let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
+    if ty.has_projections() {
+        ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations);
+    }
+
+    ty
+}
+
 enum Projected<'tcx> {
     Progress(Progress<'tcx>),
     NoProgress(ty::Term<'tcx>),
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index a986a9b6a71..8bf934cb78a 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -257,11 +257,11 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
 
             ty::Opaque => ty.try_super_fold_with(self)?,
 
-            ty::Projection => {
+            ty::Projection | ty::Inherent => {
                 // See note in `rustc_trait_selection::traits::project`
 
-                let tcx = self.infcx.tcx;
                 let infcx = self.infcx;
+                let tcx = infcx.tcx;
                 // Just an optimization: When we don't have escaping bound vars,
                 // we don't need to replace them with placeholders.
                 let (data, maps) = if data.has_escaping_bound_vars() {
@@ -276,12 +276,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 let mut orig_values = OriginalQueryValues::default();
                 // HACK(matthewjasper) `'static` is special-cased in selection,
                 // so we cannot canonicalize it.
-                let c_data = self
-                    .infcx
+                let c_data = infcx
                     .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
                 debug!("QueryNormalizer: c_data = {:#?}", c_data);
                 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
-                let result = tcx.normalize_projection_ty(c_data)?;
+                let result = match kind {
+                    ty::Projection => tcx.normalize_projection_ty(c_data),
+                    ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
+                    _ => unreachable!(),
+                }?;
                 // We don't expect ambiguity.
                 if result.is_ambiguous() {
                     // Rustdoc normalizes possibly not well-formed types, so only
@@ -294,8 +297,8 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                     }
                     return Err(NoSolution);
                 }
-                let InferOk { value: result, obligations } =
-                    self.infcx.instantiate_query_response_and_region_obligations(
+                let InferOk { value: result, obligations } = infcx
+                    .instantiate_query_response_and_region_obligations(
                         self.cause,
                         self.param_env,
                         &orig_values,
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 33f502f8182..a8fb55df2d3 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -498,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     // this trait and type.
                 }
                 ty::Param(..)
-                | ty::Alias(ty::Projection, ..)
+                | ty::Alias(ty::Projection | ty::Inherent, ..)
                 | ty::Placeholder(..)
                 | ty::Bound(..) => {
                     // In these cases, we don't know what the actual
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 422285d9474..616187b69dd 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -1268,7 +1268,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
                 // If we have a projection type, make sure to normalize it so we replace it
                 // with a fresh infer variable
-                ty::Alias(ty::Projection, ..) => {
+                ty::Alias(ty::Projection | ty::Inherent, ..) => {
                     let predicate = normalize_with_depth_to(
                         self,
                         obligation.param_env,
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 246d3ea2ef2..e4f5a84f424 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2315,7 +2315,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | ty::Dynamic(..)
             | ty::Param(..)
             | ty::Foreign(..)
-            | ty::Alias(ty::Projection, ..)
+            | ty::Alias(ty::Projection | ty::Inherent, ..)
             | ty::Bound(..)
             | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
                 bug!("asked to assemble constituent types of unexpected type: {:?}", t);
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 22710c7c059..e5edceda944 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -605,6 +605,9 @@ impl<'tcx> WfPredicates<'tcx> {
                     walker.skip_current_subtree(); // Subtree handled by compute_projection.
                     self.compute_projection(data);
                 }
+                ty::Alias(ty::Inherent, _) => {
+                    // WF if their substs are WF.
+                }
 
                 ty::Adt(def, substs) => {
                     // WfNominalType
diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs
index 4d225e36b22..2f9e480d8bd 100644
--- a/compiler/rustc_traits/src/chalk/lowering.rs
+++ b/compiler/rustc_traits/src/chalk/lowering.rs
@@ -372,6 +372,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
                     substitution: substs.lower_into(interner),
                 }))
             }
+            ty::Alias(ty::Inherent, _) => unimplemented!(),
             ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
                 chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
                     opaque_ty_id: chalk_ir::OpaqueTyId(def_id),
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index e805eb42821..36d80a06ee7 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -10,7 +10,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext};
 use std::sync::atomic::Ordering;
 
 pub(crate) fn provide(p: &mut Providers) {
-    *p = Providers { normalize_projection_ty, ..*p };
+    *p = Providers { normalize_projection_ty, normalize_inherent_projection_ty, ..*p };
 }
 
 fn normalize_projection_ty<'tcx>(
@@ -42,3 +42,30 @@ fn normalize_projection_ty<'tcx>(
         },
     )
 }
+
+fn normalize_inherent_projection_ty<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    goal: CanonicalProjectionGoal<'tcx>,
+) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
+    debug!("normalize_provider(goal={:#?})", goal);
+
+    tcx.infer_ctxt().enter_canonical_trait_query(
+        &goal,
+        |ocx, ParamEnvAnd { param_env, value: goal }| {
+            let selcx = &mut SelectionContext::new(ocx.infcx);
+            let cause = ObligationCause::dummy();
+            let mut obligations = vec![];
+            let answer = traits::normalize_inherent_projection(
+                selcx,
+                param_env,
+                goal,
+                cause,
+                0,
+                &mut obligations,
+            );
+            ocx.register_obligations(obligations);
+
+            Ok(NormalizationResult { normalized_ty: answer })
+        },
+    )
+}
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 1e91e26e2af..7e5a4d1c735 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -229,29 +229,32 @@ bitflags! {
 
         /// Does this have `Projection`?
         const HAS_TY_PROJECTION           = 1 << 10;
+        /// Does this have `Inherent`?
+        const HAS_TY_INHERENT             = 1 << 11;
         /// Does this have `Opaque`?
-        const HAS_TY_OPAQUE               = 1 << 11;
+        const HAS_TY_OPAQUE               = 1 << 12;
         /// Does this have `ConstKind::Unevaluated`?
-        const HAS_CT_PROJECTION           = 1 << 12;
+        const HAS_CT_PROJECTION           = 1 << 13;
 
         /// Could this type be normalized further?
         const HAS_PROJECTION              = TypeFlags::HAS_TY_PROJECTION.bits
                                           | TypeFlags::HAS_TY_OPAQUE.bits
+                                          | TypeFlags::HAS_TY_INHERENT.bits
                                           | TypeFlags::HAS_CT_PROJECTION.bits;
 
         /// Is an error type/const reachable?
-        const HAS_ERROR                   = 1 << 13;
+        const HAS_ERROR                   = 1 << 14;
 
         /// Does this have any region that "appears free" in the type?
         /// Basically anything but `ReLateBound` and `ReErased`.
-        const HAS_FREE_REGIONS            = 1 << 14;
+        const HAS_FREE_REGIONS            = 1 << 15;
 
         /// Does this have any `ReLateBound` regions?
-        const HAS_RE_LATE_BOUND           = 1 << 15;
+        const HAS_RE_LATE_BOUND           = 1 << 16;
         /// Does this have any `Bound` types?
-        const HAS_TY_LATE_BOUND           = 1 << 16;
+        const HAS_TY_LATE_BOUND           = 1 << 17;
         /// Does this have any `ConstKind::Bound` consts?
-        const HAS_CT_LATE_BOUND           = 1 << 17;
+        const HAS_CT_LATE_BOUND           = 1 << 18;
         /// Does this have any bound variables?
         /// Used to check if a global bound is safe to evaluate.
         const HAS_LATE_BOUND              = TypeFlags::HAS_RE_LATE_BOUND.bits
@@ -259,20 +262,20 @@ bitflags! {
                                           | TypeFlags::HAS_CT_LATE_BOUND.bits;
 
         /// Does this have any `ReErased` regions?
-        const HAS_RE_ERASED               = 1 << 18;
+        const HAS_RE_ERASED               = 1 << 19;
 
         /// Does this value have parameters/placeholders/inference variables which could be
         /// replaced later, in a way that would change the results of `impl` specialization?
-        const STILL_FURTHER_SPECIALIZABLE = 1 << 19;
+        const STILL_FURTHER_SPECIALIZABLE = 1 << 20;
 
         /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`?
-        const HAS_TY_FRESH                = 1 << 20;
+        const HAS_TY_FRESH                = 1 << 21;
 
         /// Does this value have `InferConst::Fresh`?
-        const HAS_CT_FRESH                = 1 << 21;
+        const HAS_CT_FRESH                = 1 << 22;
 
         /// Does this have `Generator` or `GeneratorWitness`?
-        const HAS_TY_GENERATOR            = 1 << 22;
+        const HAS_TY_GENERATOR            = 1 << 23;
     }
 }
 
diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs
index 4c1f2dd0e53..f7344bacc02 100644
--- a/compiler/rustc_type_ir/src/sty.rs
+++ b/compiler/rustc_type_ir/src/sty.rs
@@ -37,6 +37,7 @@ pub enum DynKind {
 #[derive(Encodable, Decodable, HashStable_Generic)]
 pub enum AliasKind {
     Projection,
+    Inherent,
     Opaque,
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 23449a25c3a..72b60d95b8c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1836,6 +1836,10 @@ pub(crate) fn clean_middle_ty<'tcx>(
             clean_projection(bound_ty.rebind(*data), cx, parent_def_id)
         }
 
+        // FIXME(fmease): Clean inherent projections properly. This requires making the trait ref in
+        // `QPathData` optional or alternatively adding a new `clean::Type` variant.
+        ty::Alias(ty::Inherent, _data) => Type::Infer,
+
         ty::Param(ref p) => {
             if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) {
                 ImplTrait(bounds)
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 7f3f26bed7c..b27ffe73ffd 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1424,6 +1424,7 @@ fn ty_auto_deref_stability<'tcx>(
                 continue;
             },
             ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
+            ty::Alias(ty::Inherent, _) => unreachable!("inherent projection should have been normalized away above"),
             ty::Alias(ty::Projection, _) if ty.has_non_region_param() => {
                 TyPosition::new_deref_stable_for_result(precedence, ty)
             },
diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs
new file mode 100644
index 00000000000..bdd881ab456
--- /dev/null
+++ b/tests/rustdoc/inherent-projections.rs
@@ -0,0 +1,14 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// FIXME(fmease): Properly render inherent projections.
+
+// @has inherent_projections/fn.create.html
+// @has - '//pre[@class="rust item-decl"]' "create() -> _"
+pub fn create() -> Owner::Metadata {}
+
+pub struct Owner;
+
+impl Owner {
+    pub type Metadata = ();
+}
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs
new file mode 100644
index 00000000000..f41574403d8
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.rs
@@ -0,0 +1,10 @@
+// known-bug: #108491
+
+// FIXME(inherent_associated_types): This should pass.
+
+struct Foo {
+    bar: Self::Bar,
+}
+impl Foo {
+    pub type Bar = usize;
+}
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr
new file mode 100644
index 00000000000..f313c494671
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-adt.stderr
@@ -0,0 +1,49 @@
+error[E0601]: `main` function not found in crate `cycle_iat_inside_of_adt`
+  --> $DIR/cycle-iat-inside-of-adt.rs:10:2
+   |
+LL | }
+   |  ^ consider adding a `main` function to `$DIR/cycle-iat-inside-of-adt.rs`
+
+error[E0391]: cycle detected when computing predicates of `Foo`
+  --> $DIR/cycle-iat-inside-of-adt.rs:5:1
+   |
+LL | struct Foo {
+   | ^^^^^^^^^^
+   |
+note: ...which requires computing predicates of `Foo`...
+  --> $DIR/cycle-iat-inside-of-adt.rs:5:1
+   |
+LL | struct Foo {
+   | ^^^^^^^^^^
+note: ...which requires computing inferred outlives predicates of `Foo`...
+  --> $DIR/cycle-iat-inside-of-adt.rs:5:1
+   |
+LL | struct Foo {
+   | ^^^^^^^^^^
+   = note: ...which requires computing the inferred outlives predicates for items in this crate...
+note: ...which requires computing type of `Foo::bar`...
+  --> $DIR/cycle-iat-inside-of-adt.rs:6:5
+   |
+LL |     bar: Self::Bar,
+   |     ^^^^^^^^^^^^^^
+note: ...which requires computing normalized predicates of `Foo`...
+  --> $DIR/cycle-iat-inside-of-adt.rs:5:1
+   |
+LL | struct Foo {
+   | ^^^^^^^^^^
+   = note: ...which again requires computing predicates of `Foo`, completing the cycle
+note: cycle used when collecting item types in top-level module
+  --> $DIR/cycle-iat-inside-of-adt.rs:5:1
+   |
+LL | / struct Foo {
+LL | |     bar: Self::Bar,
+LL | | }
+LL | | impl Foo {
+LL | |     pub type Bar = usize;
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0391, E0601.
+For more information about an error, try `rustc --explain E0391`.
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs
new file mode 100644
index 00000000000..0c2a38b1173
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.rs
@@ -0,0 +1,16 @@
+// known-bug: unknown
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// FIXME(inherent_associated_types): This shouldn't lead to a cycle error.
+
+fn user<T>() where S<T>::P: std::fmt::Debug {}
+
+struct S<T>;
+
+impl<T: Copy> S<T> {
+    type P = ();
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr
new file mode 100644
index 00000000000..aaa9a39ea0f
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/cycle-iat-inside-of-where-predicate.stderr
@@ -0,0 +1,37 @@
+error[E0391]: cycle detected when computing predicates of `user`
+  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
+   |
+LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: ...which requires computing predicates of `user`...
+  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
+   |
+LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...which requires computing explicit predicates of `user`...
+  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
+   |
+LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...which requires computing normalized predicates of `user`...
+  --> $DIR/cycle-iat-inside-of-where-predicate.rs:8:1
+   |
+LL | fn user<T>() where S<T>::P: std::fmt::Debug {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: ...which again requires computing predicates of `user`, completing the cycle
+note: cycle used when collecting item types in top-level module
+  --> $DIR/cycle-iat-inside-of-where-predicate.rs:3:1
+   |
+LL | / #![feature(inherent_associated_types)]
+LL | | #![allow(incomplete_features)]
+LL | |
+LL | | // FIXME(inherent_associated_types): This shouldn't lead to a cycle error.
+...  |
+LL | |
+LL | | fn main() {}
+   | |____________^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0391`.
diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs
deleted file mode 100644
index 53ac79e0561..00000000000
--- a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// known-bug: unknown
-// failure-status: 101
-// normalize-stderr-test "note: .*\n\n" -> ""
-// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
-// rustc-env:RUST_BACKTRACE=0
-
-// FIXME: I presume a type variable that couldn't be solved by `resolve_vars_if_possible`
-//        escapes the InferCtxt snapshot.
-
-#![feature(inherent_associated_types)]
-#![allow(incomplete_features)]
-
-struct Cont<T>(T);
-
-impl<T: Copy> Cont<T> {
-    type Out = Vec<T>;
-}
-
-pub fn weird<T: Copy>(x: T) {
-    let _: Cont<_>::Out = vec![true];
-}
-
-fn main() {}
diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr b/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr
deleted file mode 100644
index 1648cfb266b..00000000000
--- a/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr
+++ /dev/null
@@ -1,6 +0,0 @@
-error: the compiler unexpectedly panicked. this is a bug.
-
-query stack during panic:
-#0 [typeck] type-checking `weird`
-#1 [used_trait_imports] finding used_trait_imports `weird`
-end of query stack
diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.rs b/tests/ui/associated-inherent-types/bugs/inference-fail.rs
deleted file mode 100644
index a920b412b1a..00000000000
--- a/tests/ui/associated-inherent-types/bugs/inference-fail.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-// known-bug: unknown
-
-#![feature(inherent_associated_types)]
-#![allow(incomplete_features)]
-
-struct S<T>(T);
-
-impl S<()> {
-    type P = i128;
-}
-
-fn main() {
-    // We fail to infer `_ == ()` here.
-    let _: S<_>::P;
-}
diff --git a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs
deleted file mode 100644
index 632dbf3854b..00000000000
--- a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-// known-bug: unknown
-// check-pass
-
-// We currently don't region-check inherent associated type projections at all.
-
-#![feature(inherent_associated_types)]
-#![allow(incomplete_features, dead_code)]
-
-struct S<T>(T);
-
-impl S<&'static ()> {
-    type T = ();
-}
-
-fn usr<'a>() {
-    let _: S::<&'a ()>::T; // this should *fail* but it doesn't!
-}
-
-fn main() {}
diff --git a/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs
new file mode 100644
index 00000000000..c7f66e645bb
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/wf-check-skipped.rs
@@ -0,0 +1,15 @@
+// known-bug: #100041
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// FIXME(inherent_associated_types): This should fail.
+
+struct Foo;
+
+impl Foo {
+    type Bar<T> = ();
+}
+
+fn main() -> Foo::Bar::<Vec<[u32]>> {}
diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs
index f846bfa4168..83be4f43b5e 100644
--- a/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs
+++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs
@@ -31,7 +31,7 @@ fn main() {
     let _: Select<u8>::Projection = ();
 
     let _: Choose<NonCopy>::Result = ();
-    let _: Choose<bool>::Result = vec![true];
+    let _: Choose<&str>::Result = vec!["…"]; // regression test for issue #108957
 }
 
 // Test if we use the correct `ParamEnv` when proving obligations.
diff --git a/tests/ui/associated-inherent-types/former-subst-ice.rs b/tests/ui/associated-inherent-types/former-subst-ice.rs
new file mode 100644
index 00000000000..48390b9430b
--- /dev/null
+++ b/tests/ui/associated-inherent-types/former-subst-ice.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct Cont<T>(T);
+
+impl<T: Copy> Cont<T> {
+    type Out = Vec<T>;
+}
+
+pub fn weird<T: Copy>(x: T) {
+    let _: Cont<_>::Out = vec![true];
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr
new file mode 100644
index 00000000000..464b59c249f
--- /dev/null
+++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.item.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/generic-associated-types-bad.rs:16:10
+   |
+LL | const _: Ty::Pr<String> = String::new();
+   |          ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `Ty::Pr`
+  --> $DIR/generic-associated-types-bad.rs:10:16
+   |
+LL |     type Pr<T: Copy> = T;
+   |                ^^^^ required by this bound in `Ty::Pr`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr
new file mode 100644
index 00000000000..4f371b24e80
--- /dev/null
+++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.local.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `Vec<()>: Copy` is not satisfied
+  --> $DIR/generic-associated-types-bad.rs:20:12
+   |
+LL |     let _: Ty::Pr<Vec<()>>;
+   |            ^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Vec<()>`
+   |
+note: required by a bound in `Ty::Pr`
+  --> $DIR/generic-associated-types-bad.rs:10:16
+   |
+LL |     type Pr<T: Copy> = T;
+   |                ^^^^ required by this bound in `Ty::Pr`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr b/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr
new file mode 100644
index 00000000000..74ec39424ed
--- /dev/null
+++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.region.stderr
@@ -0,0 +1,11 @@
+error: lifetime may not live long enough
+  --> $DIR/generic-associated-types-bad.rs:25:12
+   |
+LL | fn user<'a>() {
+   |         -- lifetime `'a` defined here
+LL |     #[cfg(region)]
+LL |     let _: Ty::Static<&'a str> = "";
+   |            ^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/associated-inherent-types/generic-associated-types-bad.rs b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs
new file mode 100644
index 00000000000..e66392a0a94
--- /dev/null
+++ b/tests/ui/associated-inherent-types/generic-associated-types-bad.rs
@@ -0,0 +1,26 @@
+// revisions: item local region
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+#[derive(Clone, Copy)]
+pub enum Ty {}
+
+impl Ty {
+    type Pr<T: Copy> = T;
+
+    type Static<Q: 'static> = Q;
+}
+
+#[cfg(item)]
+const _: Ty::Pr<String> = String::new(); //[item]~ the trait bound `String: Copy` is not satisfied
+
+fn main() {
+    #[cfg(local)]
+    let _: Ty::Pr<Vec<()>>; //[local]~ ERROR the trait bound `Vec<()>: Copy` is not satisfied
+}
+
+fn user<'a>() {
+    #[cfg(region)]
+    let _: Ty::Static<&'a str> = ""; //[region]~ ERROR lifetime may not live long enough
+}
diff --git a/tests/ui/associated-inherent-types/inference-fail.rs b/tests/ui/associated-inherent-types/inference-fail.rs
new file mode 100644
index 00000000000..939a4ff60f2
--- /dev/null
+++ b/tests/ui/associated-inherent-types/inference-fail.rs
@@ -0,0 +1,11 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<T>(T);
+
+impl<T> S<T> { type P = (); }
+
+fn main() {
+    // There is no way to infer this type.
+    let _: S<_>::P = (); //~ ERROR type annotations needed
+}
diff --git a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr b/tests/ui/associated-inherent-types/inference-fail.stderr
index 425691bd6c4..f29144e4aa7 100644
--- a/tests/ui/associated-inherent-types/bugs/inference-fail.stderr
+++ b/tests/ui/associated-inherent-types/inference-fail.stderr
@@ -1,8 +1,8 @@
 error[E0282]: type annotations needed
-  --> $DIR/inference-fail.rs:14:14
+  --> $DIR/inference-fail.rs:10:12
    |
-LL |     let _: S<_>::P;
-   |              ^ cannot infer type
+LL |     let _: S<_>::P = ();
+   |            ^^^^^^^ cannot infer type for type parameter `T`
 
 error: aborting due to previous error
 
diff --git a/tests/ui/associated-inherent-types/inference.rs b/tests/ui/associated-inherent-types/inference.rs
new file mode 100644
index 00000000000..38179214fa1
--- /dev/null
+++ b/tests/ui/associated-inherent-types/inference.rs
@@ -0,0 +1,39 @@
+// Testing inference capabilities.
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+use std::convert::identity;
+
+struct Container<T>(T);
+
+impl Container<u32> {
+    type Sink = ();
+}
+
+impl<Any> Container<Any> {
+    type Thing = Any;
+}
+
+impl<T> Container<(T, ())> {
+    type Output = ((), Wrapped<T>);
+}
+
+fn main() {
+    // Inferred via the Self type of the impl.
+    let _: Container<_>::Sink;
+
+    // Inferred via the RHS:
+
+    let _: Container<_>::Thing = 0;
+
+    let _: Container<Wrapped<_>>::Thing = Wrapped(false);
+
+    let _: Container<_>::Output = (drop(1), Wrapped("..."));
+
+    let binding: Container<_>::Thing = Default::default(); // unsolved at this point
+    identity::<String>(binding); // constrained and solved here
+}
+
+struct Wrapped<T>(T);
diff --git a/tests/ui/associated-inherent-types/late-bound-regions.rs b/tests/ui/associated-inherent-types/late-bound-regions.rs
new file mode 100644
index 00000000000..488a2cda649
--- /dev/null
+++ b/tests/ui/associated-inherent-types/late-bound-regions.rs
@@ -0,0 +1,25 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// Test if we correctly normalize `S<'a>::P` with respect to late-bound regions.
+
+type Function = for<'a> fn(&'a i32) -> S<'a>::P;
+
+struct S<'a>(&'a ());
+
+trait Inter {
+    type P;
+}
+
+impl<'a> S<'a> {
+    type P = &'a i32;
+}
+
+fn ret_ref_local<'e>() -> &'e i32 {
+    let f: Function = |x| x;
+
+    let local = 0;
+    f(&local) //~ ERROR cannot return value referencing local variable `local`
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/late-bound-regions.stderr b/tests/ui/associated-inherent-types/late-bound-regions.stderr
new file mode 100644
index 00000000000..4706fcca91d
--- /dev/null
+++ b/tests/ui/associated-inherent-types/late-bound-regions.stderr
@@ -0,0 +1,12 @@
+error[E0515]: cannot return value referencing local variable `local`
+  --> $DIR/late-bound-regions.rs:22:5
+   |
+LL |     f(&local)
+   |     ^^------^
+   |     | |
+   |     | `local` is borrowed here
+   |     returns a value referencing data owned by the current function
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0515`.
diff --git a/tests/ui/associated-inherent-types/normalization-overflow.rs b/tests/ui/associated-inherent-types/normalization-overflow.rs
new file mode 100644
index 00000000000..4228238aa7b
--- /dev/null
+++ b/tests/ui/associated-inherent-types/normalization-overflow.rs
@@ -0,0 +1,12 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// FIXME(fmease): I'd prefer to report a cycle error here instead of an overflow one.
+
+struct T;
+
+impl T {
+    type This = Self::This; //~ ERROR overflow evaluating associated type `T::This`
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/normalization-overflow.stderr b/tests/ui/associated-inherent-types/normalization-overflow.stderr
new file mode 100644
index 00000000000..16bb64281e3
--- /dev/null
+++ b/tests/ui/associated-inherent-types/normalization-overflow.stderr
@@ -0,0 +1,8 @@
+error: overflow evaluating associated type `T::This`
+  --> $DIR/normalization-overflow.rs:9:17
+   |
+LL |     type This = Self::This;
+   |                 ^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/associated-inherent-types/private-in-public.rs b/tests/ui/associated-inherent-types/private-in-public.rs
new file mode 100644
index 00000000000..a4b372537c7
--- /dev/null
+++ b/tests/ui/associated-inherent-types/private-in-public.rs
@@ -0,0 +1,26 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+#![crate_type = "lib"]
+
+#![deny(private_in_public)]
+
+pub type PubAlias0 = PubTy::PrivAssocTy;
+//~^ ERROR private associated type `PubTy::PrivAssocTy` in public interface (error E0446)
+//~| WARNING this was previously accepted
+pub type PubAlias1 = PrivTy::PubAssocTy;
+//~^ ERROR private type `PrivTy` in public interface (error E0446)
+//~| WARNING this was previously accepted
+pub type PubAlias2 = PubTy::PubAssocTy<PrivTy>;
+//~^ ERROR private type `PrivTy` in public interface (error E0446)
+//~| WARNING this was previously accepted
+
+pub struct PubTy;
+impl PubTy {
+    type PrivAssocTy = ();
+    pub type PubAssocTy<T> = T;
+}
+
+struct PrivTy;
+impl PrivTy {
+    pub type PubAssocTy = ();
+}
diff --git a/tests/ui/associated-inherent-types/private-in-public.stderr b/tests/ui/associated-inherent-types/private-in-public.stderr
new file mode 100644
index 00000000000..f0a64e96179
--- /dev/null
+++ b/tests/ui/associated-inherent-types/private-in-public.stderr
@@ -0,0 +1,34 @@
+error: private associated type `PubTy::PrivAssocTy` in public interface (error E0446)
+  --> $DIR/private-in-public.rs:7:1
+   |
+LL | pub type PubAlias0 = PubTy::PrivAssocTy;
+   | ^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537>
+note: the lint level is defined here
+  --> $DIR/private-in-public.rs:5:9
+   |
+LL | #![deny(private_in_public)]
+   |         ^^^^^^^^^^^^^^^^^
+
+error: private type `PrivTy` in public interface (error E0446)
+  --> $DIR/private-in-public.rs:10:1
+   |
+LL | pub type PubAlias1 = PrivTy::PubAssocTy;
+   | ^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537>
+
+error: private type `PrivTy` in public interface (error E0446)
+  --> $DIR/private-in-public.rs:13:1
+   |
+LL | pub type PubAlias2 = PubTy::PubAssocTy<PrivTy>;
+   | ^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #34537 <https://github.com/rust-lang/rust/issues/34537>
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/associated-inherent-types/regionck-0.rs b/tests/ui/associated-inherent-types/regionck-0.rs
new file mode 100644
index 00000000000..7c94539ace1
--- /dev/null
+++ b/tests/ui/associated-inherent-types/regionck-0.rs
@@ -0,0 +1,14 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<T>(T);
+
+impl S<&'static ()> {
+    type T = ();
+}
+
+fn user<'a>() {
+    let _: S::<&'a ()>::T; //~ ERROR lifetime may not live long enough
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/regionck-0.stderr b/tests/ui/associated-inherent-types/regionck-0.stderr
new file mode 100644
index 00000000000..3a438ee630e
--- /dev/null
+++ b/tests/ui/associated-inherent-types/regionck-0.stderr
@@ -0,0 +1,10 @@
+error: lifetime may not live long enough
+  --> $DIR/regionck-0.rs:11:12
+   |
+LL | fn user<'a>() {
+   |         -- lifetime `'a` defined here
+LL |     let _: S::<&'a ()>::T;
+   |            ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/associated-inherent-types/regionck-1.rs b/tests/ui/associated-inherent-types/regionck-1.rs
new file mode 100644
index 00000000000..ec663cd0f77
--- /dev/null
+++ b/tests/ui/associated-inherent-types/regionck-1.rs
@@ -0,0 +1,13 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct U;
+
+impl U {
+    // Don't imply any bounds here.
+
+    type NoTyOutliv<'a, T> = &'a T; //~ ERROR the parameter type `T` may not live long enough
+    type NoReOutliv<'a, 'b> = &'a &'b (); //~ ERROR reference has a longer lifetime than the data it references
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/regionck-1.stderr b/tests/ui/associated-inherent-types/regionck-1.stderr
new file mode 100644
index 00000000000..b17d89ca306
--- /dev/null
+++ b/tests/ui/associated-inherent-types/regionck-1.stderr
@@ -0,0 +1,29 @@
+error[E0309]: the parameter type `T` may not live long enough
+  --> $DIR/regionck-1.rs:9:30
+   |
+LL |     type NoTyOutliv<'a, T> = &'a T;
+   |                              ^^^^^- help: consider adding a where clause: `where T: 'a`
+   |                              |
+   |                              ...so that the reference type `&'a T` does not outlive the data it points at
+
+error[E0491]: in type `&'a &'b ()`, reference has a longer lifetime than the data it references
+  --> $DIR/regionck-1.rs:10:31
+   |
+LL |     type NoReOutliv<'a, 'b> = &'a &'b ();
+   |                               ^^^^^^^^^^
+   |
+note: the pointer is valid for the lifetime `'a` as defined here
+  --> $DIR/regionck-1.rs:10:21
+   |
+LL |     type NoReOutliv<'a, 'b> = &'a &'b ();
+   |                     ^^
+note: but the referenced data is only valid for the lifetime `'b` as defined here
+  --> $DIR/regionck-1.rs:10:25
+   |
+LL |     type NoReOutliv<'a, 'b> = &'a &'b ();
+   |                         ^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0309, E0491.
+For more information about an error, try `rustc --explain E0309`.
diff --git a/tests/ui/associated-inherent-types/regionck-2.rs b/tests/ui/associated-inherent-types/regionck-2.rs
new file mode 100644
index 00000000000..7a0b8b08300
--- /dev/null
+++ b/tests/ui/associated-inherent-types/regionck-2.rs
@@ -0,0 +1,14 @@
+// Regression test for issue #109299.
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct Lexer<'d>(&'d ());
+
+impl Lexer<'static> {
+    type Cursor = ();
+}
+
+fn test(_: Lexer::Cursor) {} //~ ERROR mismatched types
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/regionck-2.stderr b/tests/ui/associated-inherent-types/regionck-2.stderr
new file mode 100644
index 00000000000..b0a4ed35d56
--- /dev/null
+++ b/tests/ui/associated-inherent-types/regionck-2.stderr
@@ -0,0 +1,18 @@
+error[E0308]: mismatched types
+  --> $DIR/regionck-2.rs:12:12
+   |
+LL | fn test(_: Lexer::Cursor) {}
+   |            ^^^^^^^^^^^^^ lifetime mismatch
+   |
+   = note: expected struct `Lexer<'static>`
+              found struct `Lexer<'_>`
+note: the anonymous lifetime defined here...
+  --> $DIR/regionck-2.rs:12:12
+   |
+LL | fn test(_: Lexer::Cursor) {}
+   |            ^^^^^
+   = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs
new file mode 100644
index 00000000000..b32b4288ac9
--- /dev/null
+++ b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs
@@ -0,0 +1,26 @@
+// check-pass
+// compile-flags: --crate-type=lib
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// Bounds on the self type play a major role in the resolution of inherent associated types (*).
+// As a result of that, if a type alias contains any then its bounds have to be respected and the
+// lint `type_alias_bounds` should not fire.
+//
+// FIXME(inherent_associated_types): In the current implementation that is. We might move the
+// selection phase of IATs from hir_typeck to trait_selection resulting in us not requiring the
+// ParamEnv that early allowing us to ignore bounds on type aliases again.
+// Triage this before stabilization.
+
+#![deny(type_alias_bounds)]
+
+pub type Alias<T: Bound> = (Source<T>::Assoc,);
+
+
+pub struct Source<T>(T);
+pub trait Bound {}
+
+impl<T: Bound> Source<T> {
+    pub type Assoc = ();
+}
diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs
new file mode 100644
index 00000000000..d081c4d5b78
--- /dev/null
+++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.rs
@@ -0,0 +1,12 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<T>(T);
+
+impl<T: Copy> S<T> {
+    type T = T;
+}
+
+fn main() {
+    let _: S<_>::T = String::new(); //~ ERROR the trait bound `String: Copy` is not satisfied
+}
diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr
new file mode 100644
index 00000000000..ecf30f4cdec
--- /dev/null
+++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-inferred-type.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/unsatisfied-bounds-inferred-type.rs:11:12
+   |
+LL |     let _: S<_>::T = String::new();
+   |            ^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `S<T>::T`
+  --> $DIR/unsatisfied-bounds-inferred-type.rs:6:9
+   |
+LL | impl<T: Copy> S<T> {
+   |         ^^^^ required by this bound in `S<T>::T`
+LL |     type T = T;
+   |          - required by a bound in this associated type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs
new file mode 100644
index 00000000000..97bd2c42160
--- /dev/null
+++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.rs
@@ -0,0 +1,14 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<T>(T);
+
+impl<T> S<T> {
+    type X = ()
+    where
+        T: Copy;
+}
+
+fn main() {
+    let _: S::<String>::X; //~ ERROR the trait bound `String: Copy` is not satisfied
+}
diff --git a/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr
new file mode 100644
index 00000000000..d4968cd05dc
--- /dev/null
+++ b/tests/ui/associated-inherent-types/unsatisfied-bounds-where-clause-on-assoc-ty.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/unsatisfied-bounds-where-clause-on-assoc-ty.rs:13:12
+   |
+LL |     let _: S::<String>::X;
+   |            ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `S<T>::X`
+  --> $DIR/unsatisfied-bounds-where-clause-on-assoc-ty.rs:9:12
+   |
+LL |     type X = ()
+   |          - required by a bound in this associated type
+LL |     where
+LL |         T: Copy;
+   |            ^^^^ required by this bound in `S<T>::X`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.