about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-20 00:37:20 +0000
committerbors <bors@rust-lang.org>2023-02-20 00:37:20 +0000
commit7b552967b8ed122aa949cddac8c2270b6490e64e (patch)
treecb26096ffe2091b5051496051ec18df2d7345d12
parent21e5b941e0fb23e3db53926dae0d47fd639fbe61 (diff)
parentf2253dad24ad541618c939f363688d4e90fca72a (diff)
downloadrust-7b552967b8ed122aa949cddac8c2270b6490e64e.tar.gz
rust-7b552967b8ed122aa949cddac8c2270b6490e64e.zip
Auto merge of #105961 - fmease:iat-type-directed-probing, r=jackh726
Type-directed probing for inherent associated types

When probing for inherent associated types (IATs), equate the Self-type found in the projection with the Self-type of the relevant inherent impl blocks and check if all predicates are satisfied.
Previously, we didn't look at the Self-type or at the bounds and just picked the first inherent impl block containing an associated type with the name we were searching for which is obviously incorrect.

Regarding the implementation, I basically copied what we do during method probing (`assemble_inherent_impl_probe`, `consider_probe`). Unfortunately, I had to duplicate a lot of the diagnostic code found in `rustc_hir_typeck::method::suggest` which we don't have access to in `rustc_hir_analysis`. Not sure if there is a simple way to unify the error handling. Note that in the future, `rustc_hir_analysis::astconv` might not actually be the place where we resolve inherent associated types (see https://github.com/rust-lang/rust/pull/103621#issuecomment-1304309565) but `rustc_hir_typeck` (?) in which case the duplication may naturally just disappear. While inherent associated *constants* are currently resolved during "method" probing, I did not find a straightforward way to incorporate IAT lookup into it as types and values (functions & constants) are two separate entities for which distinct code paths are taken.

Fixes #104251 (incl. https://github.com/rust-lang/rust/issues/104251#issuecomment-1338501171).
Fixes #105305.
Fixes #107468.

`@rustbot` label T-types F-inherent_associated_types
r? types
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs230
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs266
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs30
-rw-r--r--tests/ui/associated-inherent-types/ambiguity.rs16
-rw-r--r--tests/ui/associated-inherent-types/ambiguity.stderr20
-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/inference-fail.stderr9
-rw-r--r--tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs19
-rw-r--r--tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs41
-rw-r--r--tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs39
-rw-r--r--tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs17
-rw-r--r--tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr19
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs31
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr15
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr9
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr16
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr16
-rw-r--r--tests/ui/associated-inherent-types/not-found-self-type-differs.rs22
-rw-r--r--tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs21
-rw-r--r--tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr27
-rw-r--r--tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs18
-rw-r--r--tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr14
-rw-r--r--tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs20
-rw-r--r--tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr20
-rw-r--r--tests/ui/associated-inherent-types/substitute-params-bad.rs23
-rw-r--r--tests/ui/associated-inherent-types/substitute-params-bad.stderr20
-rw-r--r--tests/ui/associated-inherent-types/substitute-params.rs (renamed from tests/ui/associated-inherent-types/struct-generics.rs)8
29 files changed, 946 insertions, 84 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index 232ef2079d6..a9c2886b414 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -1,10 +1,11 @@
 use crate::astconv::AstConv;
 use crate::errors::{ManualImplementation, MissingTypeParams};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
+use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty;
+use rustc_infer::traits::FulfillmentError;
+use rustc_middle::ty::{self, Ty};
 use rustc_session::parse::feature_err;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{sym, Ident};
@@ -221,6 +222,231 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         err.emit()
     }
 
+    pub(crate) fn complain_about_ambiguous_inherent_assoc_type(
+        &self,
+        name: Ident,
+        candidates: Vec<DefId>,
+        span: Span,
+    ) -> ErrorGuaranteed {
+        let mut err = struct_span_err!(
+            self.tcx().sess,
+            name.span,
+            E0034,
+            "multiple applicable items in scope"
+        );
+        err.span_label(name.span, format!("multiple `{name}` found"));
+        self.note_ambiguous_inherent_assoc_type(&mut err, candidates, span);
+        err.emit()
+    }
+
+    // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
+    fn note_ambiguous_inherent_assoc_type(
+        &self,
+        err: &mut Diagnostic,
+        candidates: Vec<DefId>,
+        span: Span,
+    ) {
+        let tcx = self.tcx();
+
+        // Dynamic limit to avoid hiding just one candidate, which is silly.
+        let limit = if candidates.len() == 5 { 5 } else { 4 };
+
+        for (index, &item) in candidates.iter().take(limit).enumerate() {
+            let impl_ = tcx.impl_of_method(item).unwrap();
+
+            let note_span = if item.is_local() {
+                Some(tcx.def_span(item))
+            } else if impl_.is_local() {
+                Some(tcx.def_span(impl_))
+            } else {
+                None
+            };
+
+            let title = if candidates.len() > 1 {
+                format!("candidate #{}", index + 1)
+            } else {
+                "the candidate".into()
+            };
+
+            let impl_ty = tcx.at(span).type_of(impl_).subst_identity();
+            let note = format!("{title} is defined in an impl for the type `{impl_ty}`");
+
+            if let Some(span) = note_span {
+                err.span_note(span, &note);
+            } else {
+                err.note(&note);
+            }
+        }
+        if candidates.len() > limit {
+            err.note(&format!("and {} others", candidates.len() - limit));
+        }
+    }
+
+    // FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
+    pub(crate) fn complain_about_inherent_assoc_type_not_found(
+        &self,
+        name: Ident,
+        self_ty: Ty<'tcx>,
+        candidates: Vec<(DefId, (DefId, DefId))>,
+        fulfillment_errors: Vec<FulfillmentError<'tcx>>,
+        span: Span,
+    ) -> ErrorGuaranteed {
+        // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`.
+        // Either
+        // * update this code by applying changes similar to #106702 or by taking a
+        //   Vec<(DefId, (DefId, DefId), Option<Vec<FulfillmentError<'tcx>>>)> or
+        // * deduplicate this code across the two crates.
+
+        let tcx = self.tcx();
+
+        let adt_did = self_ty.ty_adt_def().map(|def| def.did());
+        let add_def_label = |err: &mut Diagnostic| {
+            if let Some(did) = adt_did {
+                err.span_label(
+                    tcx.def_span(did),
+                    format!(
+                        "associated item `{name}` not found for this {}",
+                        tcx.def_kind(did).descr(did)
+                    ),
+                );
+            }
+        };
+
+        if fulfillment_errors.is_empty() {
+            // FIXME(fmease): Copied from `rustc_hir_typeck::method::probe`. Deduplicate.
+
+            let limit = if candidates.len() == 5 { 5 } else { 4 };
+            let type_candidates = candidates
+                .iter()
+                .take(limit)
+                .map(|&(impl_, _)| format!("- `{}`", tcx.at(span).type_of(impl_).subst_identity()))
+                .collect::<Vec<_>>()
+                .join("\n");
+            let additional_types = if candidates.len() > limit {
+                format!("\nand {} more types", candidates.len() - limit)
+            } else {
+                String::new()
+            };
+
+            let mut err = struct_span_err!(
+                tcx.sess,
+                name.span,
+                E0220,
+                "associated type `{name}` not found for `{self_ty}` in the current scope"
+            );
+            err.span_label(name.span, format!("associated item not found in `{self_ty}`"));
+            err.note(&format!(
+                "the associated type was found for\n{type_candidates}{additional_types}",
+            ));
+            add_def_label(&mut err);
+            return err.emit();
+        }
+
+        let mut bound_spans = Vec::new();
+
+        let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
+            let msg = format!(
+                "doesn't satisfy `{}`",
+                if obligation.len() > 50 { quiet } else { obligation }
+            );
+            match &self_ty.kind() {
+                // Point at the type that couldn't satisfy the bound.
+                ty::Adt(def, _) => bound_spans.push((tcx.def_span(def.did()), msg)),
+                // Point at the trait object that couldn't satisfy the bound.
+                ty::Dynamic(preds, _, _) => {
+                    for pred in preds.iter() {
+                        match pred.skip_binder() {
+                            ty::ExistentialPredicate::Trait(tr) => {
+                                bound_spans.push((tcx.def_span(tr.def_id), msg.clone()))
+                            }
+                            ty::ExistentialPredicate::Projection(_)
+                            | ty::ExistentialPredicate::AutoTrait(_) => {}
+                        }
+                    }
+                }
+                // Point at the closure that couldn't satisfy the bound.
+                ty::Closure(def_id, _) => {
+                    bound_spans.push((tcx.def_span(*def_id), format!("doesn't satisfy `{quiet}`")))
+                }
+                _ => {}
+            }
+        };
+
+        let format_pred = |pred: ty::Predicate<'tcx>| {
+            let bound_predicate = pred.kind();
+            match bound_predicate.skip_binder() {
+                ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
+                    let pred = bound_predicate.rebind(pred);
+                    // `<Foo as Iterator>::Item = String`.
+                    let projection_ty = pred.skip_binder().projection_ty;
+
+                    let substs_with_infer_self = tcx.mk_substs(
+                        std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+                            .chain(projection_ty.substs.iter().skip(1)),
+                    );
+
+                    let quiet_projection_ty =
+                        tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self);
+
+                    let term = pred.skip_binder().term;
+
+                    let obligation = format!("{projection_ty} = {term}");
+                    let quiet = format!("{quiet_projection_ty} = {term}");
+
+                    bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
+                    Some((obligation, projection_ty.self_ty()))
+                }
+                ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
+                    let p = poly_trait_ref.trait_ref;
+                    let self_ty = p.self_ty();
+                    let path = p.print_only_trait_path();
+                    let obligation = format!("{self_ty}: {path}");
+                    let quiet = format!("_: {path}");
+                    bound_span_label(self_ty, &obligation, &quiet);
+                    Some((obligation, self_ty))
+                }
+                _ => None,
+            }
+        };
+
+        // FIXME(fmease): `rustc_hir_typeck::method::suggest` uses a `skip_list` to filter out some bounds.
+        // I would do the same here if it didn't mean more code duplication.
+        let mut bounds: Vec<_> = fulfillment_errors
+            .into_iter()
+            .map(|error| error.root_obligation.predicate)
+            .filter_map(format_pred)
+            .map(|(p, _)| format!("`{}`", p))
+            .collect();
+        bounds.sort();
+        bounds.dedup();
+
+        let mut err = tcx.sess.struct_span_err(
+            name.span,
+            &format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
+        );
+        if !bounds.is_empty() {
+            err.note(&format!(
+                "the following trait bounds were not satisfied:\n{}",
+                bounds.join("\n")
+            ));
+        }
+        err.span_label(
+            name.span,
+            format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds")
+        );
+
+        bound_spans.sort();
+        bound_spans.dedup();
+        for (span, msg) in bound_spans {
+            if !tcx.sess.source_map().is_span_accessible(span) {
+                continue;
+            }
+            err.span_label(span, &msg);
+        }
+        add_def_label(&mut err);
+        err.emit()
+    }
+
     /// When there are any missing associated types, emit an E0191 error and attempt to supply a
     /// reasonable suggestion on how to write it. For the case of multiple associated types in the
     /// same trait bound have the same name (as they come from different supertraits), we instead
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 2ff47237b1b..716b4fc6ae3 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -27,7 +27,10 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{walk_generics, Visitor as _};
 use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_infer::traits::ObligationCause;
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::middle::stability::AllowUnstable;
 use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
 use rustc_middle::ty::DynKind;
@@ -39,12 +42,11 @@ use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::{sym, Span, DUMMY_SP};
 use rustc_target::spec::abi;
-use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::astconv_object_safety_violations;
 use rustc_trait_selection::traits::error_reporting::{
     report_object_safety_error, suggestions::NextTypeParamName,
 };
 use rustc_trait_selection::traits::wf::object_region_bounds;
+use rustc_trait_selection::traits::{self, astconv_object_safety_violations, ObligationCtxt};
 
 use smallvec::{smallvec, SmallVec};
 use std::collections::BTreeSet;
@@ -1944,7 +1946,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             Res::Err
         };
 
-        // Check if we have an enum variant.
+        // Check if we have an enum variant or an inherent associated type.
         let mut variant_resolution = None;
         if let Some(adt_def) = self.probe_adt(span, qself_ty) {
             if adt_def.is_enum() {
@@ -2043,23 +2045,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 }
             }
 
-            // see if we can satisfy using an inherent associated type
-            for &impl_ in tcx.inherent_impls(adt_def.did()) {
-                let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else {
-                    continue;
-                };
-                let ty::Adt(_, adt_substs) = qself_ty.kind() else {
-                    // FIXME(inherent_associated_types)
-                    bug!("unimplemented: non-adt self of inherent assoc ty");
-                };
-                let item_substs = self.create_substs_for_associated_item(
-                    span,
-                    assoc_ty_did,
-                    assoc_segment,
-                    adt_substs,
-                );
-                let ty = tcx.type_of(assoc_ty_did).subst(tcx, item_substs);
-                return Ok((ty, DefKind::AssocTy, assoc_ty_did));
+            if let Some((ty, did)) = self.lookup_inherent_assoc_ty(
+                assoc_ident,
+                assoc_segment,
+                adt_def.did(),
+                qself_ty,
+                hir_ref_id,
+                span,
+            )? {
+                return Ok((ty, DefKind::AssocTy, did));
             }
         }
 
@@ -2202,6 +2196,172 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         Ok((ty, DefKind::AssocTy, assoc_ty_did))
     }
 
+    fn lookup_inherent_assoc_ty(
+        &self,
+        name: Ident,
+        segment: &hir::PathSegment<'_>,
+        adt_did: DefId,
+        self_ty: Ty<'tcx>,
+        block: hir::HirId,
+        span: Span,
+    ) -> Result<Option<(Ty<'tcx>, DefId)>, ErrorGuaranteed> {
+        let tcx = self.tcx();
+
+        let candidates: Vec<_> = tcx
+            .inherent_impls(adt_did)
+            .iter()
+            .filter_map(|&impl_| Some((impl_, self.lookup_assoc_ty_unchecked(name, block, impl_)?)))
+            .collect();
+
+        if candidates.is_empty() {
+            return Ok(None);
+        }
+
+        // In contexts that have no inference context, just make a new one.
+        // We do need a local variable to store it, though.
+        let infcx_;
+        let infcx = match self.infcx() {
+            Some(infcx) => infcx,
+            None => {
+                assert!(!self_ty.needs_infer());
+                infcx_ = tcx.infer_ctxt().ignoring_regions().build();
+                &infcx_
+            }
+        };
+
+        let param_env = tcx.param_env(block.owner.to_def_id());
+        let cause = ObligationCause::misc(span, block.owner.def_id);
+        let mut fulfillment_errors = Vec::new();
+        let mut applicable_candidates: Vec<_> = candidates
+            .iter()
+            .filter_map(|&(impl_, (assoc_item, def_scope))| {
+                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 = 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 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 = 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;
+                    }
+
+                    // FIXME(fmease): Unsolved vars can escape this InferCtxt snapshot.
+                    Some((assoc_item, def_scope, infcx.resolve_vars_if_possible(impl_substs)))
+                })
+            })
+            .collect();
+
+        if applicable_candidates.len() > 1 {
+            return Err(self.complain_about_ambiguous_inherent_assoc_type(
+                name,
+                applicable_candidates.into_iter().map(|(candidate, ..)| candidate).collect(),
+                span,
+            ));
+        }
+
+        if let Some((assoc_item, def_scope, impl_substs)) = 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, #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);
+
+            return Ok(Some((ty, assoc_item)));
+        }
+
+        Err(self.complain_about_inherent_assoc_type_not_found(
+            name,
+            self_ty,
+            candidates,
+            fulfillment_errors,
+            span,
+        ))
+    }
+
+    fn lookup_assoc_ty(
+        &self,
+        name: Ident,
+        block: hir::HirId,
+        span: Span,
+        scope: DefId,
+    ) -> Option<DefId> {
+        let (item, def_scope) = self.lookup_assoc_ty_unchecked(name, block, scope)?;
+        self.check_assoc_ty(item, name, def_scope, block, span);
+        Some(item)
+    }
+
+    fn lookup_assoc_ty_unchecked(
+        &self,
+        name: Ident,
+        block: hir::HirId,
+        scope: DefId,
+    ) -> Option<(DefId, DefId)> {
+        let tcx = self.tcx();
+        let (ident, def_scope) = tcx.adjust_ident_and_get_scope(name, scope, block);
+
+        // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
+        // of calling `find_by_name_and_kind`.
+        let item = tcx.associated_items(scope).in_definition_order().find(|i| {
+            i.kind.namespace() == Namespace::TypeNS
+                && i.ident(tcx).normalize_to_macros_2_0() == ident
+        })?;
+
+        Some((item.def_id, def_scope))
+    }
+
+    fn check_assoc_ty(
+        &self,
+        item: DefId,
+        name: Ident,
+        def_scope: DefId,
+        block: hir::HirId,
+        span: Span,
+    ) {
+        let tcx = self.tcx();
+        let kind = DefKind::AssocTy;
+
+        if !tcx.visibility(item).is_accessible_from(def_scope, tcx) {
+            let kind = kind.descr(item);
+            let msg = format!("{kind} `{name}` is private");
+            let def_span = tcx.def_span(item);
+            tcx.sess
+                .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624))
+                .span_label(span, &format!("private {kind}"))
+                .span_label(def_span, &format!("{kind} defined here"))
+                .emit();
+        }
+        tcx.check_stability(item, Some(block), span, None);
+    }
+
     fn probe_traits_that_match_assoc_ty(
         &self,
         qself_ty: Ty<'tcx>,
@@ -2255,39 +2415,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             .collect()
     }
 
-    fn lookup_assoc_ty(
-        &self,
-        ident: Ident,
-        block: hir::HirId,
-        span: Span,
-        scope: DefId,
-    ) -> Option<DefId> {
-        let tcx = self.tcx();
-        let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block);
-
-        // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
-        // of calling `find_by_name_and_kind`.
-        let item = tcx.associated_items(scope).in_definition_order().find(|i| {
-            i.kind.namespace() == Namespace::TypeNS
-                && i.ident(tcx).normalize_to_macros_2_0() == ident
-        })?;
-
-        let kind = DefKind::AssocTy;
-        if !item.visibility(tcx).is_accessible_from(def_scope, tcx) {
-            let kind = kind.descr(item.def_id);
-            let msg = format!("{kind} `{ident}` is private");
-            let def_span = self.tcx().def_span(item.def_id);
-            tcx.sess
-                .struct_span_err_with_code(span, &msg, rustc_errors::error_code!(E0624))
-                .span_label(span, &format!("private {kind}"))
-                .span_label(def_span, &format!("{kind} defined here"))
-                .emit();
-        }
-        tcx.check_stability(item.def_id, Some(block), span, None);
-
-        Some(item.def_id)
-    }
-
     fn qpath_to_ty(
         &self,
         span: Span,
@@ -3375,3 +3502,36 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         }
     }
 }
+
+pub trait InferCtxtExt<'tcx> {
+    fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx>;
+}
+
+impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
+    fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
+        InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind {
+            GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(),
+            GenericParamDefKind::Type { .. } => self
+                .next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::SubstitutionPlaceholder,
+                    span: self.tcx.def_span(def_id),
+                })
+                .into(),
+            GenericParamDefKind::Const { .. } => {
+                let span = self.tcx.def_span(def_id);
+                let origin = ConstVariableOrigin {
+                    kind: ConstVariableOriginKind::SubstitutionPlaceholder,
+                    span,
+                };
+                self.next_const_var(
+                    self.tcx
+                        .type_of(param.def_id)
+                        .no_bound_vars()
+                        .expect("const parameter types cannot be generic"),
+                    origin,
+                )
+                .into()
+            }
+        })
+    }
+}
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 0b9226802cf..eb6c0b7686b 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -9,12 +9,11 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
+use rustc_hir_analysis::astconv::InferCtxtExt as _;
 use rustc_hir_analysis::autoderef::{self, Autoderef};
 use rustc_infer::infer::canonical::OriginalQueryValues;
 use rustc_infer::infer::canonical::{Canonical, QueryResponse};
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
-use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
 use rustc_middle::ty::AssocItem;
@@ -1941,33 +1940,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
     }
 
-    fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
-        InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind {
-            GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(),
-            GenericParamDefKind::Type { .. } => self
-                .next_ty_var(TypeVariableOrigin {
-                    kind: TypeVariableOriginKind::SubstitutionPlaceholder,
-                    span: self.tcx.def_span(def_id),
-                })
-                .into(),
-            GenericParamDefKind::Const { .. } => {
-                let span = self.tcx.def_span(def_id);
-                let origin = ConstVariableOrigin {
-                    kind: ConstVariableOriginKind::SubstitutionPlaceholder,
-                    span,
-                };
-                self.next_const_var(
-                    self.tcx
-                        .type_of(param.def_id)
-                        .no_bound_vars()
-                        .expect("const parameter types cannot be generic"),
-                    origin,
-                )
-                .into()
-            }
-        })
-    }
-
     /// Replaces late-bound-regions bound by `value` with `'static` using
     /// `ty::erase_late_bound_regions`.
     ///
diff --git a/tests/ui/associated-inherent-types/ambiguity.rs b/tests/ui/associated-inherent-types/ambiguity.rs
new file mode 100644
index 00000000000..73920555b3e
--- /dev/null
+++ b/tests/ui/associated-inherent-types/ambiguity.rs
@@ -0,0 +1,16 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct Wrapper<T>(T);
+
+impl Wrapper<i32> {
+    type Foo = i32;
+}
+
+impl Wrapper<()> {
+    type Foo = ();
+}
+
+fn main() {
+    let _: Wrapper<_>::Foo = (); //~ ERROR multiple applicable items in scope
+}
diff --git a/tests/ui/associated-inherent-types/ambiguity.stderr b/tests/ui/associated-inherent-types/ambiguity.stderr
new file mode 100644
index 00000000000..155c296cbb3
--- /dev/null
+++ b/tests/ui/associated-inherent-types/ambiguity.stderr
@@ -0,0 +1,20 @@
+error[E0034]: multiple applicable items in scope
+  --> $DIR/ambiguity.rs:15:24
+   |
+LL |     let _: Wrapper<_>::Foo = ();
+   |                        ^^^ multiple `Foo` found
+   |
+note: candidate #1 is defined in an impl for the type `Wrapper<i32>`
+  --> $DIR/ambiguity.rs:7:5
+   |
+LL |     type Foo = i32;
+   |     ^^^^^^^^
+note: candidate #2 is defined in an impl for the type `Wrapper<()>`
+  --> $DIR/ambiguity.rs:11:5
+   |
+LL |     type Foo = ();
+   |     ^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0034`.
diff --git a/tests/ui/associated-inherent-types/bugs/ice-substitution.rs b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs
new file mode 100644
index 00000000000..53ac79e0561
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/ice-substitution.rs
@@ -0,0 +1,23 @@
+// 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
new file mode 100644
index 00000000000..7b0d1c50516
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/ice-substitution.stderr
@@ -0,0 +1,6 @@
+error: the compiler unexpectedly panicked. this is a bug.
+
+query stack during panic:
+#0 [typeck] type-checking `weird`
+#1 [typeck_item_bodies] type-checking all item bodies
+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
new file mode 100644
index 00000000000..a920b412b1a
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/inference-fail.rs
@@ -0,0 +1,15 @@
+// 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/inference-fail.stderr b/tests/ui/associated-inherent-types/bugs/inference-fail.stderr
new file mode 100644
index 00000000000..425691bd6c4
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/inference-fail.stderr
@@ -0,0 +1,9 @@
+error[E0282]: type annotations needed
+  --> $DIR/inference-fail.rs:14:14
+   |
+LL |     let _: S<_>::P;
+   |              ^ cannot infer type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs
new file mode 100644
index 00000000000..632dbf3854b
--- /dev/null
+++ b/tests/ui/associated-inherent-types/bugs/lack-of-regionck.rs
@@ -0,0 +1,19 @@
+// 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/dispatch-on-self-type-0.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs
new file mode 100644
index 00000000000..f846bfa4168
--- /dev/null
+++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-0.rs
@@ -0,0 +1,41 @@
+// check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// Check that inherent associated types are dispatched on the concrete Self type.
+
+struct Select<T>(T);
+
+impl Select<u8> {
+    type Projection = ();
+}
+
+impl Select<String> {
+    type Projection = bool;
+}
+
+struct Choose<T>(T);
+struct NonCopy;
+
+impl<T: Copy> Choose<T> {
+    type Result = Vec<T>;
+}
+
+impl Choose<NonCopy> {
+    type Result = ();
+}
+
+fn main() {
+    let _: Select<String>::Projection = false;
+    let _: Select<u8>::Projection = ();
+
+    let _: Choose<NonCopy>::Result = ();
+    let _: Choose<bool>::Result = vec![true];
+}
+
+// Test if we use the correct `ParamEnv` when proving obligations.
+
+pub fn parameterized<T: Copy>(x: T) {
+    let _: Choose<T>::Result = vec![x];
+}
diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs
new file mode 100644
index 00000000000..9b0fa8dc6f3
--- /dev/null
+++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-1.rs
@@ -0,0 +1,39 @@
+// check-pass
+
+#![feature(inherent_associated_types, auto_traits, negative_impls)]
+#![allow(incomplete_features)]
+
+use std::cmp::Ordering;
+
+// Check that inherent associated types are dispatched on the concrete Self type.
+
+struct Select<T, U>(T, U);
+
+impl<T: Ordinary, U: Ordinary> Select<T, U> {
+    type Type = ();
+}
+
+impl<T: Ordinary> Select<T, Special> {
+    type Type = bool;
+}
+
+impl<T: Ordinary> Select<Special, T> {
+    type Type = Ordering;
+}
+
+impl Select<Special, Special> {
+    type Type = (bool, bool);
+}
+
+fn main() {
+    let _: Select<String, Special>::Type = false;
+    let _: Select<Special, Special>::Type = (true, false);
+    let _: Select<Special, u8>::Type = Ordering::Equal;
+    let _: Select<i128, ()>::Type = ();
+}
+
+enum Special {}
+
+impl !Ordinary for Special {}
+
+auto trait Ordinary {}
diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs
new file mode 100644
index 00000000000..7b205952f52
--- /dev/null
+++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.rs
@@ -0,0 +1,17 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct Parameterized<T, U>(T, U);
+
+impl Parameterized<(), ()> {
+    type Output = bool;
+}
+
+impl<T> Parameterized<bool, T> {
+    type Result = T;
+}
+
+fn main() {
+    let _: Parameterized<(), ()>::Output = String::new(); //~ ERROR mismatched types
+    let _: Parameterized<bool, u32>::Result = (); //~ ERROR mismatched types
+}
diff --git a/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr
new file mode 100644
index 00000000000..c9a48872af4
--- /dev/null
+++ b/tests/ui/associated-inherent-types/dispatch-on-self-type-2.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/dispatch-on-self-type-2.rs:15:44
+   |
+LL |     let _: Parameterized<(), ()>::Output = String::new();
+   |            -----------------------------   ^^^^^^^^^^^^^ expected `bool`, found `String`
+   |            |
+   |            expected due to this
+
+error[E0308]: mismatched types
+  --> $DIR/dispatch-on-self-type-2.rs:16:47
+   |
+LL |     let _: Parameterized<bool, u32>::Result = ();
+   |            --------------------------------   ^^ expected `u32`, found `()`
+   |            |
+   |            expected due to this
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs
new file mode 100644
index 00000000000..d2efb24c666
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.rs
@@ -0,0 +1,31 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// Check that it's okay to report “[inherent] associated type […] not found” for inherent associated
+// type candidates that are not applicable (due to unsuitable Self type) even if there exists a
+// “shadowed” associated type from a trait with the same name since its use would be ambiguous
+// anyway if the IAT didn't exist.
+// FIXME(inherent_associated_types): Figure out which error would be more helpful here.
+
+// revisions: shadowed uncovered
+
+struct S<T>(T);
+
+trait Tr {
+    type Pr;
+}
+
+impl<T> Tr for S<T> {
+    type Pr = ();
+}
+
+#[cfg(shadowed)]
+impl S<()> {
+    type Pr = i32;
+}
+
+fn main() {
+    let _: S::<bool>::Pr = ();
+    //[shadowed]~^ ERROR associated type `Pr` not found
+    //[uncovered]~^^ ERROR ambiguous associated type
+}
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr
new file mode 100644
index 00000000000..3561db354c0
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.shadowed.stderr
@@ -0,0 +1,15 @@
+error[E0220]: associated type `Pr` not found for `S<bool>` in the current scope
+  --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:23
+   |
+LL | struct S<T>(T);
+   | ----------- associated item `Pr` not found for this struct
+...
+LL |     let _: S::<bool>::Pr = ();
+   |                       ^^ associated item not found in `S<bool>`
+   |
+   = note: the associated type was found for
+           - `S<()>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr
new file mode 100644
index 00000000000..88c72042ce2
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs-shadowing-trait-item.uncovered.stderr
@@ -0,0 +1,9 @@
+error[E0223]: ambiguous associated type
+  --> $DIR/not-found-self-type-differs-shadowing-trait-item.rs:28:12
+   |
+LL |     let _: S::<bool>::Pr = ();
+   |            ^^^^^^^^^^^^^ help: use the fully-qualified path: `<S<bool> as Tr>::Pr`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0223`.
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr
new file mode 100644
index 00000000000..4396435a6dd
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr
@@ -0,0 +1,16 @@
+error[E0220]: associated type `Proj` not found for `Family<Option<()>>` in the current scope
+  --> $DIR/not-found-self-type-differs.rs:17:34
+   |
+LL | struct Family<T>(T);
+   | ---------------- associated item `Proj` not found for this struct
+...
+LL | type Alias = Family<Option<()>>::Proj;
+   |                                  ^^^^ associated item not found in `Family<Option<()>>`
+   |
+   = note: the associated type was found for
+           - `Family<()>`
+           - `Family<Result<T, ()>>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr
new file mode 100644
index 00000000000..d527db02217
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr
@@ -0,0 +1,16 @@
+error[E0220]: associated type `Proj` not found for `Family<PathBuf>` in the current scope
+  --> $DIR/not-found-self-type-differs.rs:21:40
+   |
+LL | struct Family<T>(T);
+   | ---------------- associated item `Proj` not found for this struct
+...
+LL |     let _: Family<std::path::PathBuf>::Proj = ();
+   |                                        ^^^^ associated item not found in `Family<PathBuf>`
+   |
+   = note: the associated type was found for
+           - `Family<()>`
+           - `Family<Result<T, ()>>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs
new file mode 100644
index 00000000000..93f58dcb6e6
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs
@@ -0,0 +1,22 @@
+// revisions: local alias
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct Family<T>(T);
+
+impl Family<()> {
+    type Proj = ();
+}
+
+impl<T> Family<Result<T, ()>> {
+    type Proj = Self;
+}
+
+#[cfg(alias)]
+type Alias = Family<Option<()>>::Proj; //[alias]~ ERROR associated type `Proj` not found for `Family<Option<()>>`
+
+fn main() {
+    #[cfg(local)]
+    let _: Family<std::path::PathBuf>::Proj = (); //[local]~ ERROR associated type `Proj` not found for `Family<PathBuf>`
+}
diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs
new file mode 100644
index 00000000000..b00830fa1c1
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.rs
@@ -0,0 +1,21 @@
+// Regression test for issue #104251.
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct Container<T: ?Sized>(T);
+
+impl<T> Container<T> {
+    type Yield = i32;
+}
+
+struct Duple<T, U>(T, U);
+
+impl<T: Copy, U: Send> Duple<T, U> {
+    type Combination = (T, U);
+}
+
+fn main() {
+    let _: Container<[u8]>::Yield = 1; //~ ERROR the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied
+    let _: Duple<String, std::rc::Rc<str>>::Combination; //~ ERROR the associated type `Combination` exists for `Duple<String, Rc<str>>`, but its trait bounds were not satisfied
+}
diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr
new file mode 100644
index 00000000000..73657906761
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-0.stderr
@@ -0,0 +1,27 @@
+error: the associated type `Yield` exists for `Container<[u8]>`, but its trait bounds were not satisfied
+  --> $DIR/not-found-unsatisfied-bounds-0.rs:19:29
+   |
+LL | struct Container<T: ?Sized>(T);
+   | --------------------------- associated item `Yield` not found for this struct
+...
+LL |     let _: Container<[u8]>::Yield = 1;
+   |                             ^^^^^ associated type cannot be referenced on `Container<[u8]>` due to unsatisfied trait bounds
+   |
+   = note: the following trait bounds were not satisfied:
+           `[u8]: Sized`
+
+error: the associated type `Combination` exists for `Duple<String, Rc<str>>`, but its trait bounds were not satisfied
+  --> $DIR/not-found-unsatisfied-bounds-0.rs:20:45
+   |
+LL | struct Duple<T, U>(T, U);
+   | ------------------ associated item `Combination` not found for this struct
+...
+LL |     let _: Duple<String, std::rc::Rc<str>>::Combination;
+   |                                             ^^^^^^^^^^^ associated type cannot be referenced on `Duple<String, Rc<str>>` due to unsatisfied trait bounds
+   |
+   = note: the following trait bounds were not satisfied:
+           `Rc<str>: Send`
+           `String: Copy`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs
new file mode 100644
index 00000000000..c80b1364ae3
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.rs
@@ -0,0 +1,18 @@
+// fail-check
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// Test if we use the correct `ParamEnv` when proving obligations.
+
+fn parameterized<T>() {
+    let _: Container<T>::Proj = String::new(); //~ ERROR the associated type `Proj` exists for `Container<T>`, but its trait bounds were not satisfied
+}
+
+struct Container<T>(T);
+
+impl<T: Clone> Container<T> {
+    type Proj = String;
+}
+
+fn main() {}
diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr
new file mode 100644
index 00000000000..230bfa538b4
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-1.stderr
@@ -0,0 +1,14 @@
+error: the associated type `Proj` exists for `Container<T>`, but its trait bounds were not satisfied
+  --> $DIR/not-found-unsatisfied-bounds-1.rs:9:26
+   |
+LL |     let _: Container<T>::Proj = String::new();
+   |                          ^^^^ associated type cannot be referenced on `Container<T>` due to unsatisfied trait bounds
+...
+LL | struct Container<T>(T);
+   | ------------------- associated item `Proj` not found for this struct
+   |
+   = note: the following trait bounds were not satisfied:
+           `T: Clone`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs
new file mode 100644
index 00000000000..5b0e8de9c58
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.rs
@@ -0,0 +1,20 @@
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<A, B>(A, B);
+struct Featureless;
+
+trait One {}
+trait Two {}
+
+impl<T: One> S<Featureless, T> {
+    type X = ();
+}
+
+impl<T: Two> S<T, Featureless> {
+    type X = String;
+}
+
+fn main() {
+    let _: S::<Featureless, Featureless>::X; //~ ERROR the associated type `X` exists for `S<Featureless, Featureless>`, but its trait bounds were not satisfied
+}
diff --git a/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr
new file mode 100644
index 00000000000..3ddab25deb5
--- /dev/null
+++ b/tests/ui/associated-inherent-types/not-found-unsatisfied-bounds-in-multiple-impls.stderr
@@ -0,0 +1,20 @@
+error: the associated type `X` exists for `S<Featureless, Featureless>`, but its trait bounds were not satisfied
+  --> $DIR/not-found-unsatisfied-bounds-in-multiple-impls.rs:19:43
+   |
+LL | struct S<A, B>(A, B);
+   | -------------- associated item `X` not found for this struct
+LL | struct Featureless;
+   | ------------------
+   | |
+   | doesn't satisfy `Featureless: One`
+   | doesn't satisfy `Featureless: Two`
+...
+LL |     let _: S::<Featureless, Featureless>::X;
+   |                                           ^ associated type cannot be referenced on `S<Featureless, Featureless>` due to unsatisfied trait bounds
+   |
+   = note: the following trait bounds were not satisfied:
+           `Featureless: One`
+           `Featureless: Two`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/associated-inherent-types/substitute-params-bad.rs b/tests/ui/associated-inherent-types/substitute-params-bad.rs
new file mode 100644
index 00000000000..00eb1a14da4
--- /dev/null
+++ b/tests/ui/associated-inherent-types/substitute-params-bad.rs
@@ -0,0 +1,23 @@
+// Regression test for issue #105305 and for
+// https://github.com/rust-lang/rust/issues/107468#issuecomment-1409096700
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+struct S<T>(T);
+
+impl<T, 'a> S<T> { //~ ERROR lifetime parameters must be declared prior to type and const parameters
+    type P = T;
+}
+
+struct Subj<T>(T);
+
+impl<T, S> Subj<(T, S)> {
+    type Un = (T, S);
+}
+
+fn main() {
+    type A = S<()>::P;
+
+    let _: Subj<(i32, i32)>::Un = 0i32; //~ ERROR mismatched types
+}
diff --git a/tests/ui/associated-inherent-types/substitute-params-bad.stderr b/tests/ui/associated-inherent-types/substitute-params-bad.stderr
new file mode 100644
index 00000000000..7a7808ba67b
--- /dev/null
+++ b/tests/ui/associated-inherent-types/substitute-params-bad.stderr
@@ -0,0 +1,20 @@
+error: lifetime parameters must be declared prior to type and const parameters
+  --> $DIR/substitute-params-bad.rs:9:9
+   |
+LL | impl<T, 'a> S<T> {
+   |     ----^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, T>`
+
+error[E0308]: mismatched types
+  --> $DIR/substitute-params-bad.rs:22:35
+   |
+LL |     let _: Subj<(i32, i32)>::Un = 0i32;
+   |            --------------------   ^^^^ expected `(i32, i32)`, found `i32`
+   |            |
+   |            expected due to this
+   |
+   = note: expected tuple `(i32, i32)`
+               found type `i32`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/associated-inherent-types/struct-generics.rs b/tests/ui/associated-inherent-types/substitute-params.rs
index 8952b379173..e94d6833159 100644
--- a/tests/ui/associated-inherent-types/struct-generics.rs
+++ b/tests/ui/associated-inherent-types/substitute-params.rs
@@ -9,7 +9,15 @@ impl<T> S<T> {
     type P = T;
 }
 
+impl<T> S<(T,)> {
+    type Un = T;
+}
+
 fn main() {
+    // Regression test for issue #104240.
     type A = S<()>::P;
     let _: A = ();
+
+    // Regression test for issue #107468.
+    let _: S<(i32,)>::Un = 0i32;
 }