about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-05-22 16:02:28 +0200
committerGitHub <noreply@github.com>2025-05-22 16:02:28 +0200
commit3216098e5344ee0f2e387e3901c4ff24ba40bf2c (patch)
treec9449cedf11c46aeef00c8e2a37971eb754b5313 /compiler/rustc_hir_analysis
parent706dc7091628735e29c9061df6ecfdb38c55b651 (diff)
parent8c37c8c3e6133ae431bc4a5aec1c7b21a3134969 (diff)
downloadrust-3216098e5344ee0f2e387e3901c4ff24ba40bf2c.tar.gz
rust-3216098e5344ee0f2e387e3901c4ff24ba40bf2c.zip
Rollup merge of #140218 - fmease:hirtylo-clean-up-path-low, r=compiler-errors
HIR ty lowering: Clean up & refactor the lowering of type-relative paths

While rebasing #126651 I realized that HIR ty lowering could benefit from some *spring cleaning* now that it's been extended to handle RTN and mGCA paths.

More seriously, similar to my merged PR #118668 which unified the handling of all *associated item constraints* (assoc ty, const (ACE) & fn (RTN)), this PR (commit https://github.com/rust-lang/rust/pull/140218/commits/695fcf517d8864b4812225643ef8cfc036ba9f61) partially[^1] deduplicates the resolution code for all *type-relative paths* (assoc ty, const (mGCA) & fn (RTN)).

**Why**? DRY'ing that part of the code means PR #126651 will automatically support RTN paths like `Ty::AssocTy::assoc_fn(..)` and it also implies shared diagnostic code and thus better diagnostics for RTN.

---

The other commits represent cleanups, renamings, moves. More notably, I've renamed path lowering methods to be a lot more descriptive, so ones lowering `QPath(Resolved)` paths now have `_resolved_` in their name and ones lowering `QPath(TypeRelative)` paths now have `_type_relative_` in their name. This should make it stupidly obvious what their purpose is.

---

Best reviewed commit by commit. The changes are close to trivial but the diff might make it look hairier.
r? compiler-errors

[^1]: Sadly, I couldn't unify as much compared to the other PR without introducing unnecessary `unreachable!()`s or rendering the code otherwise illegible with flags and micro helper traits.
Diffstat (limited to 'compiler/rustc_hir_analysis')
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs147
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs242
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs560
8 files changed, 483 insertions, 490 deletions
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index f7f2b78f052..a3a0e276f74 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -37,7 +37,7 @@ hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
 
 hir_analysis_assoc_kind_mismatch_wrap_in_braces_sugg = consider adding braces here
 
-hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the associated {$what} of a trait with uninferred generic parameters
+hir_analysis_associated_type_trait_uninferred_generic_params = cannot use the {$what} of a trait with uninferred generic parameters
     .suggestion = use a fully qualified path with inferred lifetimes
 
 hir_analysis_associated_type_trait_uninferred_generic_params_multipart_suggestion = use a fully qualified path with explicit lifetimes
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 4520fbe352c..350bdc7821d 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -44,7 +44,6 @@ use rustc_trait_selection::traits::ObligationCtxt;
 use tracing::{debug, instrument};
 
 use crate::errors;
-use crate::hir_ty_lowering::errors::assoc_tag_str;
 use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
 
 pub(crate) mod dump;
@@ -444,13 +443,12 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
         self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_ident))
     }
 
-    fn lower_assoc_shared(
+    fn lower_assoc_item_path(
         &self,
         span: Span,
         item_def_id: DefId,
         item_segment: &rustc_hir::PathSegment<'tcx>,
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
-        assoc_tag: ty::AssocTag,
     ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
         if let Some(trait_ref) = poly_trait_ref.no_bound_vars() {
             let item_args = self.lowerer().lower_generic_args_of_assoc_item(
@@ -525,7 +523,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
                 inferred_sugg,
                 bound,
                 mpart_sugg,
-                what: assoc_tag_str(assoc_tag),
+                what: self.tcx.def_descr(item_def_id),
             }))
         }
     }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 4419d5dc7d6..bdc42c7a2d9 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -4,9 +4,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::codes::*;
 use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
+use rustc_hir::AmbigArg;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{AmbigArg, HirId};
 use rustc_middle::bug;
 use rustc_middle::ty::{
     self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
@@ -309,7 +309,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     false => "`?Sized`",
                 };
                 // There was a `?Trait` bound, but it was neither `?Sized` nor `experimental_default_bounds`.
-                tcx.dcx().span_err(
+                self.dcx().span_err(
                     unbound.span,
                     format!(
                         "relaxing a default bound only does something for {}; \
@@ -675,7 +675,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
                 // Good error for `where Trait::method(..): Send`.
                 let Some(self_ty) = opt_self_ty else {
-                    let guar = self.error_missing_qpath_self_ty(
+                    let guar = self.report_missing_self_ty_for_resolved_path(
                         trait_def_id,
                         hir_ty.span,
                         item_segment,
@@ -713,120 +713,51 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     Err(guar) => Ty::new_error(tcx, guar),
                 }
             }
-            hir::QPath::TypeRelative(qself, item_segment)
-                if item_segment.args.is_some_and(|args| {
+            hir::QPath::TypeRelative(hir_self_ty, segment)
+                if segment.args.is_some_and(|args| {
                     matches!(args.parenthesized, hir::GenericArgsParentheses::ReturnTypeNotation)
                 }) =>
             {
-                match self
-                    .resolve_type_relative_return_type_notation(
-                        qself,
-                        item_segment,
-                        hir_ty.hir_id,
-                        hir_ty.span,
-                    )
-                    .and_then(|(candidate, item_def_id)| {
-                        self.lower_return_type_notation_ty(candidate, item_def_id, hir_ty.span)
-                    }) {
-                    Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty),
-                    Err(guar) => Ty::new_error(tcx, guar),
-                }
-            }
-            _ => self.lower_ty(hir_ty),
-        }
-    }
-
-    /// Perform type-dependent lookup for a *method* for return type notation.
-    /// This generally mirrors `<dyn HirTyLowerer>::lower_assoc_path`.
-    fn resolve_type_relative_return_type_notation(
-        &self,
-        qself: &'tcx hir::Ty<'tcx>,
-        item_segment: &'tcx hir::PathSegment<'tcx>,
-        qpath_hir_id: HirId,
-        span: Span,
-    ) -> Result<(ty::PolyTraitRef<'tcx>, DefId), ErrorGuaranteed> {
-        let tcx = self.tcx();
-        let qself_ty = self.lower_ty(qself);
-        let assoc_ident = item_segment.ident;
-        let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
-            path.res
-        } else {
-            Res::Err
-        };
-
-        let bound = match (qself_ty.kind(), qself_res) {
-            (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => {
-                // `Self` in an impl of a trait -- we have a concrete self type and a
-                // trait reference.
-                let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
-                    // A cycle error occurred, most likely.
-                    self.dcx().span_bug(span, "expected cycle error");
-                };
-
-                self.probe_single_bound_for_assoc_item(
-                    || {
-                        traits::supertraits(
-                            tcx,
-                            ty::Binder::dummy(trait_ref.instantiate_identity()),
-                        )
-                    },
-                    AssocItemQSelf::SelfTyAlias,
+                let self_ty = self.lower_ty(hir_self_ty);
+                let (item_def_id, bound) = match self.resolve_type_relative_path(
+                    self_ty,
+                    hir_self_ty,
                     ty::AssocTag::Fn,
-                    assoc_ident,
-                    span,
+                    segment,
+                    hir_ty.hir_id,
+                    hir_ty.span,
                     None,
-                )?
-            }
-            (
-                &ty::Param(_),
-                Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
-            ) => self.probe_single_ty_param_bound_for_assoc_item(
-                param_did.expect_local(),
-                qself.span,
-                ty::AssocTag::Fn,
-                assoc_ident,
-                span,
-            )?,
-            _ => {
-                if let Err(reported) = qself_ty.error_reported() {
-                    return Err(reported);
-                } else {
-                    // FIXME(return_type_notation): Provide some structured suggestion here.
-                    let err = struct_span_code_err!(
-                        self.dcx(),
-                        span,
-                        E0223,
-                        "ambiguous associated function"
+                ) {
+                    Ok(result) => result,
+                    Err(guar) => return Ty::new_error(tcx, guar),
+                };
+
+                // Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`,
+                // which may happen via a higher-ranked where clause or supertrait.
+                // This is the same restrictions as associated types; even though we could
+                // support it, it just makes things a lot more difficult to support in
+                // `resolve_bound_vars`, since we'd need to introduce those as elided
+                // bound vars on the where clause too.
+                if bound.has_bound_vars() {
+                    return Ty::new_error(
+                        tcx,
+                        self.dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
+                            span: hir_ty.span,
+                            inferred_sugg: Some(hir_ty.span.with_hi(segment.ident.span.lo())),
+                            bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder()),
+                            mpart_sugg: None,
+                            what: tcx.def_descr(item_def_id),
+                        }),
                     );
-                    return Err(err.emit());
                 }
-            }
-        };
 
-        // Don't let `T::method` resolve to some `for<'a> <T as Tr<'a>>::method`,
-        // which may happen via a higher-ranked where clause or supertrait.
-        // This is the same restrictions as associated types; even though we could
-        // support it, it just makes things a lot more difficult to support in
-        // `resolve_bound_vars`, since we'd need to introduce those as elided
-        // bound vars on the where clause too.
-        if bound.has_bound_vars() {
-            return Err(self.tcx().dcx().emit_err(
-                errors::AssociatedItemTraitUninferredGenericParams {
-                    span,
-                    inferred_sugg: Some(span.with_hi(item_segment.ident.span.lo())),
-                    bound: format!("{}::", tcx.anonymize_bound_vars(bound).skip_binder(),),
-                    mpart_sugg: None,
-                    what: "function",
-                },
-            ));
+                match self.lower_return_type_notation_ty(bound, item_def_id, hir_ty.span) {
+                    Ok(ty) => Ty::new_alias(tcx, ty::Projection, ty),
+                    Err(guar) => Ty::new_error(tcx, guar),
+                }
+            }
+            _ => self.lower_ty(hir_ty),
         }
-
-        let trait_def_id = bound.def_id();
-        let assoc_ty = self
-            .probe_assoc_item(assoc_ident, ty::AssocTag::Fn, qpath_hir_id, span, trait_def_id)
-            .expect("failed to find associated type");
-
-        Ok((bound, assoc_ty.def_id))
     }
 
     /// Do the common parts of lowering an RTN type. This involves extending the
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
index d1ee5a5494c..ebeb3b58208 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
@@ -35,7 +35,7 @@ pub(crate) fn validate_cmse_abi<'tcx>(
                     _ => tcx.hir_span(hir_id),
                 };
                 struct_span_code_err!(
-                    tcx.dcx(),
+                    dcx,
                     span,
                     E0781,
                     "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
index f6e5149bd2b..9e44b645aca 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
@@ -78,15 +78,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         // We  don't support empty trait objects.
         if regular_traits.is_empty() && auto_traits.is_empty() {
-            let guar = self.report_trait_object_with_no_traits_error(
-                span,
-                user_written_bounds.iter().copied(),
-            );
+            let guar =
+                self.report_trait_object_with_no_traits(span, user_written_bounds.iter().copied());
             return Ty::new_error(tcx, guar);
         }
         // We don't support >1 principal
         if regular_traits.len() > 1 {
-            let guar = self.report_trait_object_addition_traits_error(&regular_traits);
+            let guar = self.report_trait_object_addition_traits(&regular_traits);
             return Ty::new_error(tcx, guar);
         }
         // Don't create a dyn trait if we have errors in the principal.
@@ -132,7 +130,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     if references_self {
                         // With trait alias and type alias combined, type resolver
                         // may not be able to catch all illegal `Self` usages (issue 139082)
-                        let guar = tcx.dcx().emit_err(SelfInTypeAlias { span });
+                        let guar = self.dcx().emit_err(SelfInTypeAlias { span });
                         b.term = replace_dummy_self_with_error(tcx, b.term, guar);
                     }
                 }
@@ -360,7 +358,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
                         && hir_bound.span.contains(span)
                 });
-                self.complain_about_missing_type_params(
+                self.report_missing_type_params(
                     missing_type_params,
                     trait_ref.def_id,
                     span,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 3759a224ff7..45fee0fa402 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -3,11 +3,12 @@ use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordMap;
 use rustc_errors::codes::*;
 use rustc_errors::{
-    Applicability, Diag, ErrorGuaranteed, MultiSpan, listify, pluralize, struct_span_code_err,
+    Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, listify, pluralize,
+    struct_span_code_err,
 };
-use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, HirId};
 use rustc_middle::bug;
 use rustc_middle::ty::fast_reject::{TreatParams, simplify_type};
 use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@@ -23,6 +24,7 @@ use rustc_trait_selection::traits::{
     FulfillmentError, dyn_compatibility_violations_for_assoc_item,
 };
 use smallvec::SmallVec;
+use tracing::debug;
 
 use crate::errors::{
     self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams,
@@ -34,7 +36,7 @@ use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// On missing type parameters, emit an E0393 error and provide a structured suggestion using
     /// the type parameter's name as a placeholder.
-    pub(crate) fn complain_about_missing_type_params(
+    pub(crate) fn report_missing_type_params(
         &self,
         missing_type_params: Vec<Symbol>,
         def_id: DefId,
@@ -56,7 +58,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
     /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
     /// an error and attempt to build a reasonable structured suggestion.
-    pub(crate) fn complain_about_internal_fn_trait(
+    pub(crate) fn report_internal_fn_trait(
         &self,
         span: Span,
         trait_def_id: DefId,
@@ -112,7 +114,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
-    pub(super) fn complain_about_assoc_item_not_found<I>(
+    pub(super) fn report_unresolved_assoc_item<I>(
         &self,
         all_candidates: impl Fn() -> I,
         qself: AssocItemQSelf,
@@ -132,7 +134,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 .filter_by_name_unhygienic(assoc_ident.name)
                 .find(|item| tcx.hygienic_eq(assoc_ident, item.ident(tcx), r.def_id()))
         }) {
-            return self.complain_about_assoc_kind_mismatch(
+            return self.report_assoc_kind_mismatch(
                 assoc_item,
                 assoc_tag,
                 assoc_ident,
@@ -331,7 +333,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         self.dcx().emit_err(err)
     }
 
-    fn complain_about_assoc_kind_mismatch(
+    fn report_assoc_kind_mismatch(
         &self,
         assoc_item: &ty::AssocItem,
         assoc_tag: ty::AssocTag,
@@ -396,12 +398,177 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         })
     }
 
-    pub(super) fn report_ambiguous_assoc(
+    pub(crate) fn report_missing_self_ty_for_resolved_path(
+        &self,
+        trait_def_id: DefId,
+        span: Span,
+        item_segment: &hir::PathSegment<'tcx>,
+        assoc_tag: ty::AssocTag,
+    ) -> ErrorGuaranteed {
+        let tcx = self.tcx();
+        let path_str = tcx.def_path_str(trait_def_id);
+
+        let def_id = self.item_def_id();
+        debug!(item_def_id = ?def_id);
+
+        // FIXME: document why/how this is different from `tcx.local_parent(def_id)`
+        let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
+        debug!(?parent_def_id);
+
+        // If the trait in segment is the same as the trait defining the item,
+        // use the `<Self as ..>` syntax in the error.
+        let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
+        let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
+
+        let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
+            vec!["Self".to_string()]
+        } else {
+            // Find all the types that have an `impl` for the trait.
+            tcx.all_impls(trait_def_id)
+                .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
+                .filter(|header| {
+                    // Consider only accessible traits
+                    tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
+                        && header.polarity != ty::ImplPolarity::Negative
+                })
+                .map(|header| header.trait_ref.instantiate_identity().self_ty())
+                // We don't care about blanket impls.
+                .filter(|self_ty| !self_ty.has_non_region_param())
+                .map(|self_ty| tcx.erase_regions(self_ty).to_string())
+                .collect()
+        };
+        // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
+        // references the trait. Relevant for the first case in
+        // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
+        self.report_ambiguous_assoc_item_path(
+            span,
+            &type_names,
+            &[path_str],
+            item_segment.ident,
+            assoc_tag,
+        )
+    }
+
+    pub(super) fn report_unresolved_type_relative_path(
+        &self,
+        self_ty: Ty<'tcx>,
+        hir_self_ty: &hir::Ty<'_>,
+        assoc_tag: ty::AssocTag,
+        ident: Ident,
+        qpath_hir_id: HirId,
+        span: Span,
+        variant_def_id: Option<DefId>,
+    ) -> ErrorGuaranteed {
+        let tcx = self.tcx();
+        let kind_str = assoc_tag_str(assoc_tag);
+        if variant_def_id.is_some() {
+            // Variant in type position
+            let msg = format!("expected {kind_str}, found variant `{ident}`");
+            self.dcx().span_err(span, msg)
+        } else if self_ty.is_enum() {
+            let mut err = self.dcx().create_err(errors::NoVariantNamed {
+                span: ident.span,
+                ident,
+                ty: self_ty,
+            });
+
+            let adt_def = self_ty.ty_adt_def().expect("enum is not an ADT");
+            if let Some(variant_name) = find_best_match_for_name(
+                &adt_def.variants().iter().map(|variant| variant.name).collect::<Vec<Symbol>>(),
+                ident.name,
+                None,
+            ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == variant_name)
+            {
+                let mut suggestion = vec![(ident.span, variant_name.to_string())];
+                if let hir::Node::Stmt(&hir::Stmt { kind: hir::StmtKind::Semi(expr), .. })
+                | hir::Node::Expr(expr) = tcx.parent_hir_node(qpath_hir_id)
+                    && let hir::ExprKind::Struct(..) = expr.kind
+                {
+                    match variant.ctor {
+                        None => {
+                            // struct
+                            suggestion = vec![(
+                                ident.span.with_hi(expr.span.hi()),
+                                if variant.fields.is_empty() {
+                                    format!("{variant_name} {{}}")
+                                } else {
+                                    format!(
+                                        "{variant_name} {{ {} }}",
+                                        variant
+                                            .fields
+                                            .iter()
+                                            .map(|f| format!("{}: /* value */", f.name))
+                                            .collect::<Vec<_>>()
+                                            .join(", ")
+                                    )
+                                },
+                            )];
+                        }
+                        Some((hir::def::CtorKind::Fn, def_id)) => {
+                            // tuple
+                            let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
+                            let inputs = fn_sig.inputs().skip_binder();
+                            suggestion = vec![(
+                                ident.span.with_hi(expr.span.hi()),
+                                format!(
+                                    "{variant_name}({})",
+                                    inputs
+                                        .iter()
+                                        .map(|i| format!("/* {i} */"))
+                                        .collect::<Vec<_>>()
+                                        .join(", ")
+                                ),
+                            )];
+                        }
+                        Some((hir::def::CtorKind::Const, _)) => {
+                            // unit
+                            suggestion = vec![(
+                                ident.span.with_hi(expr.span.hi()),
+                                variant_name.to_string(),
+                            )];
+                        }
+                    }
+                }
+                err.multipart_suggestion_verbose(
+                    "there is a variant with a similar name",
+                    suggestion,
+                    Applicability::HasPlaceholders,
+                );
+            } else {
+                err.span_label(ident.span, format!("variant not found in `{self_ty}`"));
+            }
+
+            if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
+                err.span_label(sp, format!("variant `{ident}` not found here"));
+            }
+
+            err.emit()
+        } else if let Err(reported) = self_ty.error_reported() {
+            reported
+        } else {
+            match self.maybe_report_similar_assoc_fn(span, self_ty, hir_self_ty) {
+                Ok(()) => {}
+                Err(reported) => return reported,
+            }
+
+            let traits: Vec<_> = self.probe_traits_that_match_assoc_ty(self_ty, ident);
+
+            self.report_ambiguous_assoc_item_path(
+                span,
+                &[self_ty.to_string()],
+                &traits,
+                ident,
+                assoc_tag,
+            )
+        }
+    }
+
+    pub(super) fn report_ambiguous_assoc_item_path(
         &self,
         span: Span,
         types: &[String],
         traits: &[String],
-        name: Symbol,
+        ident: Ident,
         assoc_tag: ty::AssocTag,
     ) -> ErrorGuaranteed {
         let kind_str = assoc_tag_str(assoc_tag);
@@ -421,6 +588,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 Applicability::MachineApplicable,
             );
         } else {
+            let sugg_sp = span.until(ident.span);
+
             let mut types = types.to_vec();
             types.sort();
             let mut traits = traits.to_vec();
@@ -428,76 +597,79 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             match (&types[..], &traits[..]) {
                 ([], []) => {
                     err.span_suggestion_verbose(
-                        span,
+                        sugg_sp,
                         format!(
                             "if there were a type named `Type` that implements a trait named \
-                             `Trait` with associated {kind_str} `{name}`, you could use the \
+                             `Trait` with associated {kind_str} `{ident}`, you could use the \
                              fully-qualified path",
                         ),
-                        format!("<Type as Trait>::{name}"),
+                        "<Type as Trait>::",
                         Applicability::HasPlaceholders,
                     );
                 }
                 ([], [trait_str]) => {
                     err.span_suggestion_verbose(
-                        span,
+                        sugg_sp,
                         format!(
                             "if there were a type named `Example` that implemented `{trait_str}`, \
                              you could use the fully-qualified path",
                         ),
-                        format!("<Example as {trait_str}>::{name}"),
+                        format!("<Example as {trait_str}>::"),
                         Applicability::HasPlaceholders,
                     );
                 }
                 ([], traits) => {
-                    err.span_suggestions(
-                        span,
+                    err.span_suggestions_with_style(
+                        sugg_sp,
                         format!(
                             "if there were a type named `Example` that implemented one of the \
-                             traits with associated {kind_str} `{name}`, you could use the \
+                             traits with associated {kind_str} `{ident}`, you could use the \
                              fully-qualified path",
                         ),
-                        traits.iter().map(|trait_str| format!("<Example as {trait_str}>::{name}")),
+                        traits.iter().map(|trait_str| format!("<Example as {trait_str}>::")),
                         Applicability::HasPlaceholders,
+                        SuggestionStyle::ShowAlways,
                     );
                 }
                 ([type_str], []) => {
                     err.span_suggestion_verbose(
-                        span,
+                        sugg_sp,
                         format!(
-                            "if there were a trait named `Example` with associated {kind_str} `{name}` \
+                            "if there were a trait named `Example` with associated {kind_str} `{ident}` \
                              implemented for `{type_str}`, you could use the fully-qualified path",
                         ),
-                        format!("<{type_str} as Example>::{name}"),
+                        format!("<{type_str} as Example>::"),
                         Applicability::HasPlaceholders,
                     );
                 }
                 (types, []) => {
-                    err.span_suggestions(
-                        span,
+                    err.span_suggestions_with_style(
+                        sugg_sp,
                         format!(
-                            "if there were a trait named `Example` with associated {kind_str} `{name}` \
+                            "if there were a trait named `Example` with associated {kind_str} `{ident}` \
                              implemented for one of the types, you could use the fully-qualified \
                              path",
                         ),
                         types
                             .into_iter()
-                            .map(|type_str| format!("<{type_str} as Example>::{name}")),
+                            .map(|type_str| format!("<{type_str} as Example>::")),
                         Applicability::HasPlaceholders,
+                        SuggestionStyle::ShowAlways,
                     );
                 }
                 (types, traits) => {
                     let mut suggestions = vec![];
                     for type_str in types {
                         for trait_str in traits {
-                            suggestions.push(format!("<{type_str} as {trait_str}>::{name}"));
+                            suggestions.push(format!("<{type_str} as {trait_str}>::"));
                         }
                     }
-                    err.span_suggestions(
-                        span,
+                    err.span_suggestions_with_style(
+                        sugg_sp,
                         "use fully-qualified syntax",
                         suggestions,
                         Applicability::MachineApplicable,
+                        SuggestionStyle::ShowAlways,
                     );
                 }
             }
@@ -505,7 +677,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         err.emit()
     }
 
-    pub(crate) fn complain_about_ambiguous_inherent_assoc(
+    pub(crate) fn report_ambiguous_inherent_assoc_item(
         &self,
         name: Ident,
         candidates: Vec<DefId>,
@@ -518,12 +690,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             "multiple applicable items in scope"
         );
         err.span_label(name.span, format!("multiple `{name}` found"));
-        self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span);
+        self.note_ambiguous_inherent_assoc_item(&mut err, candidates, span);
         err.emit()
     }
 
     // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
-    fn note_ambiguous_inherent_assoc_ty(
+    fn note_ambiguous_inherent_assoc_item(
         &self,
         err: &mut Diag<'_>,
         candidates: Vec<DefId>,
@@ -566,7 +738,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     }
 
     // FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
-    pub(crate) fn complain_about_inherent_assoc_not_found(
+    pub(crate) fn report_unresolved_inherent_assoc_item(
         &self,
         name: Ident,
         self_ty: Ty<'tcx>,
@@ -1046,7 +1218,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
-    pub fn report_prohibit_generics_error<'a>(
+    pub fn report_prohibited_generic_args<'a>(
         &self,
         segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
         args_visitors: impl Iterator<Item = &'a hir::GenericArg<'a>> + Clone,
@@ -1128,7 +1300,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         err.emit()
     }
 
-    pub fn report_trait_object_addition_traits_error(
+    pub fn report_trait_object_addition_traits(
         &self,
         regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>,
     ) -> ErrorGuaranteed {
@@ -1171,7 +1343,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         err.emit()
     }
 
-    pub fn report_trait_object_with_no_traits_error(
+    pub fn report_trait_object_with_no_traits(
         &self,
         span: Span,
         user_written_clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 483b61add33..1f4692b19f1 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -103,7 +103,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // In case there is an associated type with the same name
             // Add the suggestion to this error
             if let Some(mut sugg) =
-                tcx.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion)
+                self.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion)
                 && let Suggestions::Enabled(ref mut s1) = diag.suggestions
                 && let Suggestions::Enabled(ref mut s2) = sugg.suggestions
             {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 5e79e932015..6b21bbbfcd8 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -38,22 +38,20 @@ use rustc_middle::middle::stability::AllowUnstable;
 use rustc_middle::mir::interpret::LitToConstInput;
 use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
 use rustc_middle::ty::{
-    self, AssocTag, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty,
-    TyCtxt, TypeVisitableExt, TypingMode, Upcast, fold_regions,
+    self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt,
+    TypeVisitableExt, TypingMode, Upcast, fold_regions,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
 use rustc_session::parse::feature_err;
-use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
+use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 use tracing::{debug, instrument};
 
-use self::errors::assoc_tag_str;
 use crate::check::check_abi_fn_ptr;
-use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, NoVariantNamed};
+use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation};
 use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
 use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
 use crate::middle::resolve_bound_vars as rbv;
@@ -150,7 +148,7 @@ pub trait HirTyLowerer<'tcx> {
         assoc_ident: Ident,
     ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>;
 
-    /// Lower an associated type/const (from a trait) to a projection.
+    /// Lower a path to an associated item (of a trait) to a projection.
     ///
     /// This method has to be defined by the concrete lowering context because
     /// dealing with higher-ranked trait references depends on its capabilities:
@@ -162,13 +160,12 @@ pub trait HirTyLowerer<'tcx> {
     ///
     /// The canonical example of this is associated type `T::P` where `T` is a type
     /// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`.
-    fn lower_assoc_shared(
+    fn lower_assoc_item_path(
         &self,
         span: Span,
         item_def_id: DefId,
         item_segment: &hir::PathSegment<'tcx>,
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
-        assoc_tag: ty::AssocTag,
     ) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed>;
 
     fn lower_fn_sig(
@@ -245,39 +242,46 @@ pub enum FeedConstTy<'a, 'tcx> {
 }
 
 #[derive(Debug, Clone, Copy)]
-enum LowerAssocMode {
-    Type { permit_variants: bool },
+enum LowerTypeRelativePathMode {
+    Type(PermitVariants),
     Const,
 }
 
-impl LowerAssocMode {
+impl LowerTypeRelativePathMode {
     fn assoc_tag(self) -> ty::AssocTag {
         match self {
-            LowerAssocMode::Type { .. } => ty::AssocTag::Type,
-            LowerAssocMode::Const => ty::AssocTag::Const,
+            Self::Type(_) => ty::AssocTag::Type,
+            Self::Const => ty::AssocTag::Const,
         }
     }
 
     fn def_kind(self) -> DefKind {
         match self {
-            LowerAssocMode::Type { .. } => DefKind::AssocTy,
-            LowerAssocMode::Const => DefKind::AssocConst,
+            Self::Type(_) => DefKind::AssocTy,
+            Self::Const => DefKind::AssocConst,
         }
     }
 
-    fn permit_variants(self) -> bool {
+    fn permit_variants(self) -> PermitVariants {
         match self {
-            LowerAssocMode::Type { permit_variants } => permit_variants,
+            Self::Type(permit_variants) => permit_variants,
             // FIXME(mgca): Support paths like `Option::<T>::None` or `Option::<T>::Some` which
             // resolve to const ctors/fn items respectively.
-            LowerAssocMode::Const => false,
+            Self::Const => PermitVariants::No,
         }
     }
 }
 
+/// Whether to permit a path to resolve to an enum variant.
 #[derive(Debug, Clone, Copy)]
-enum LoweredAssoc<'tcx> {
-    Term(DefId, GenericArgsRef<'tcx>),
+pub enum PermitVariants {
+    Yes,
+    No,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum TypeRelativePath<'tcx> {
+    AssocItem(DefId, GenericArgsRef<'tcx>),
     Variant { adt: Ty<'tcx>, variant_did: DefId },
 }
 
@@ -745,7 +749,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             trait_ref.path.segments.split_last().unwrap().1.iter(),
             GenericsArgsErrExtend::None,
         );
-        self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
+        self.report_internal_fn_trait(span, trait_def_id, trait_segment, false);
 
         let (generic_args, arg_count) = self.lower_generic_args_of_path(
             trait_ref.path.span,
@@ -920,7 +924,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_segment: &hir::PathSegment<'tcx>,
         is_impl: bool,
     ) -> ty::TraitRef<'tcx> {
-        self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
+        self.report_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
 
         let (generic_args, _) =
             self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty));
@@ -933,7 +937,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     fn probe_trait_that_defines_assoc_item(
         &self,
         trait_def_id: DefId,
-        assoc_tag: AssocTag,
+        assoc_tag: ty::AssocTag,
         assoc_ident: Ident,
     ) -> bool {
         self.tcx()
@@ -976,7 +980,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         ty_param_def_id: LocalDefId,
         ty_param_span: Span,
-        assoc_tag: AssocTag,
+        assoc_tag: ty::AssocTag,
         assoc_ident: Ident,
         span: Span,
     ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
@@ -1011,7 +1015,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         &self,
         all_candidates: impl Fn() -> I,
         qself: AssocItemQSelf,
-        assoc_tag: AssocTag,
+        assoc_tag: ty::AssocTag,
         assoc_ident: Ident,
         span: Span,
         constraint: Option<&hir::AssocItemConstraint<'tcx>>,
@@ -1026,15 +1030,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         });
 
         let Some(bound) = matching_candidates.next() else {
-            let reported = self.complain_about_assoc_item_not_found(
+            return Err(self.report_unresolved_assoc_item(
                 all_candidates,
                 qself,
                 assoc_tag,
                 assoc_ident,
                 span,
                 constraint,
-            );
-            return Err(reported);
+            ));
         };
         debug!(?bound);
 
@@ -1127,7 +1130,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         Ok(bound)
     }
 
-    /// Lower a [type-relative] path referring to an associated type or to an enum variant.
+    /// Lower a [type-relative](hir::QPath::TypeRelative) path in type position to a type.
     ///
     /// If the path refers to an enum variant and `permit_variants` holds,
     /// the returned type is simply the provided self type `qself_ty`.
@@ -1148,61 +1151,63 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// described in the previous paragraph and their modeling of projections would likely be
     /// very similar in nature.
     ///
-    /// [type-relative]: hir::QPath::TypeRelative
     /// [#22519]: https://github.com/rust-lang/rust/issues/22519
     /// [iat]: https://github.com/rust-lang/rust/issues/8995#issuecomment-1569208403
     //
     // NOTE: When this function starts resolving `Trait::AssocTy` successfully
     // it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
     #[instrument(level = "debug", skip_all, ret)]
-    pub fn lower_assoc_path_ty(
+    pub fn lower_type_relative_ty_path(
         &self,
-        hir_ref_id: HirId,
+        self_ty: Ty<'tcx>,
+        hir_self_ty: &'tcx hir::Ty<'tcx>,
+        segment: &'tcx hir::PathSegment<'tcx>,
+        qpath_hir_id: HirId,
         span: Span,
-        qself_ty: Ty<'tcx>,
-        qself: &'tcx hir::Ty<'tcx>,
-        assoc_segment: &'tcx hir::PathSegment<'tcx>,
-        permit_variants: bool,
+        permit_variants: PermitVariants,
     ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
         let tcx = self.tcx();
-        match self.lower_assoc_path_shared(
-            hir_ref_id,
+        match self.lower_type_relative_path(
+            self_ty,
+            hir_self_ty,
+            segment,
+            qpath_hir_id,
             span,
-            qself_ty,
-            qself,
-            assoc_segment,
-            LowerAssocMode::Type { permit_variants },
+            LowerTypeRelativePathMode::Type(permit_variants),
         )? {
-            LoweredAssoc::Term(def_id, args) => {
+            TypeRelativePath::AssocItem(def_id, args) => {
                 let alias_ty = ty::AliasTy::new_from_args(tcx, def_id, args);
                 let ty = Ty::new_alias(tcx, alias_ty.kind(tcx), alias_ty);
                 Ok((ty, tcx.def_kind(def_id), def_id))
             }
-            LoweredAssoc::Variant { adt, variant_did } => Ok((adt, DefKind::Variant, variant_did)),
+            TypeRelativePath::Variant { adt, variant_did } => {
+                Ok((adt, DefKind::Variant, variant_did))
+            }
         }
     }
 
+    /// Lower a [type-relative][hir::QPath::TypeRelative] path to a (type-level) constant.
     #[instrument(level = "debug", skip_all, ret)]
-    fn lower_assoc_path_const(
+    fn lower_type_relative_const_path(
         &self,
-        hir_ref_id: HirId,
+        self_ty: Ty<'tcx>,
+        hir_self_ty: &'tcx hir::Ty<'tcx>,
+        segment: &'tcx hir::PathSegment<'tcx>,
+        qpath_hir_id: HirId,
         span: Span,
-        qself_ty: Ty<'tcx>,
-        qself: &'tcx hir::Ty<'tcx>,
-        assoc_segment: &'tcx hir::PathSegment<'tcx>,
     ) -> Result<Const<'tcx>, ErrorGuaranteed> {
         let tcx = self.tcx();
-        let (def_id, args) = match self.lower_assoc_path_shared(
-            hir_ref_id,
+        let (def_id, args) = match self.lower_type_relative_path(
+            self_ty,
+            hir_self_ty,
+            segment,
+            qpath_hir_id,
             span,
-            qself_ty,
-            qself,
-            assoc_segment,
-            LowerAssocMode::Const,
+            LowerTypeRelativePathMode::Const,
         )? {
-            LoweredAssoc::Term(def_id, args) => {
+            TypeRelativePath::AssocItem(def_id, args) => {
                 if !tcx.associated_item(def_id).is_type_const_capable(tcx) {
-                    let mut err = tcx.dcx().struct_span_err(
+                    let mut err = self.dcx().struct_span_err(
                         span,
                         "use of trait associated const without `#[type_const]`",
                     );
@@ -1213,75 +1218,134 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
             // FIXME(mgca): implement support for this once ready to support all adt ctor expressions,
             // not just const ctors
-            LoweredAssoc::Variant { .. } => {
+            TypeRelativePath::Variant { .. } => {
                 span_bug!(span, "unexpected variant res for type associated const path")
             }
         };
         Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(def_id, args)))
     }
 
+    /// Lower a [type-relative][hir::QPath::TypeRelative] (and type-level) path.
     #[instrument(level = "debug", skip_all, ret)]
-    fn lower_assoc_path_shared(
+    fn lower_type_relative_path(
         &self,
-        hir_ref_id: HirId,
+        self_ty: Ty<'tcx>,
+        hir_self_ty: &'tcx hir::Ty<'tcx>,
+        segment: &'tcx hir::PathSegment<'tcx>,
+        qpath_hir_id: HirId,
         span: Span,
-        qself_ty: Ty<'tcx>,
-        qself: &'tcx hir::Ty<'tcx>,
-        assoc_segment: &'tcx hir::PathSegment<'tcx>,
-        mode: LowerAssocMode,
-    ) -> Result<LoweredAssoc<'tcx>, ErrorGuaranteed> {
-        debug!(%qself_ty, ?assoc_segment.ident);
+        mode: LowerTypeRelativePathMode,
+    ) -> Result<TypeRelativePath<'tcx>, ErrorGuaranteed> {
+        debug!(%self_ty, ?segment.ident);
         let tcx = self.tcx();
 
-        let assoc_ident = assoc_segment.ident;
-
         // 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) {
+        let mut variant_def_id = None;
+        if let Some(adt_def) = self.probe_adt(span, self_ty) {
             if adt_def.is_enum() {
                 let variant_def = adt_def
                     .variants()
                     .iter()
-                    .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did()));
+                    .find(|vd| tcx.hygienic_eq(segment.ident, vd.ident(tcx), adt_def.did()));
                 if let Some(variant_def) = variant_def {
-                    if mode.permit_variants() {
-                        tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
+                    if let PermitVariants::Yes = mode.permit_variants() {
+                        tcx.check_stability(variant_def.def_id, Some(qpath_hir_id), span, None);
                         let _ = self.prohibit_generic_args(
-                            slice::from_ref(assoc_segment).iter(),
-                            GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def },
+                            slice::from_ref(segment).iter(),
+                            GenericsArgsErrExtend::EnumVariant {
+                                qself: hir_self_ty,
+                                assoc_segment: segment,
+                                adt_def,
+                            },
                         );
-                        return Ok(LoweredAssoc::Variant {
-                            adt: qself_ty,
+                        return Ok(TypeRelativePath::Variant {
+                            adt: self_ty,
                             variant_did: variant_def.def_id,
                         });
                     } else {
-                        variant_resolution = Some(variant_def.def_id);
+                        variant_def_id = Some(variant_def.def_id);
                     }
                 }
             }
 
             // FIXME(inherent_associated_types, #106719): Support self types other than ADTs.
-            if let Some((did, args)) = self.probe_inherent_assoc_shared(
-                assoc_segment,
+            if let Some((did, args)) = self.probe_inherent_assoc_item(
+                segment,
                 adt_def.did(),
-                qself_ty,
-                hir_ref_id,
+                self_ty,
+                qpath_hir_id,
                 span,
                 mode.assoc_tag(),
             )? {
-                return Ok(LoweredAssoc::Term(did, args));
+                return Ok(TypeRelativePath::AssocItem(did, args));
             }
         }
 
-        let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
-            path.res
-        } else {
-            Res::Err
+        let (item_def_id, bound) = self.resolve_type_relative_path(
+            self_ty,
+            hir_self_ty,
+            mode.assoc_tag(),
+            segment,
+            qpath_hir_id,
+            span,
+            variant_def_id,
+        )?;
+
+        let (item_def_id, args) = self.lower_assoc_item_path(span, item_def_id, segment, bound)?;
+
+        if let Some(variant_def_id) = variant_def_id {
+            tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, qpath_hir_id, span, |lint| {
+                lint.primary_message("ambiguous associated item");
+                let mut could_refer_to = |kind: DefKind, def_id, also| {
+                    let note_msg = format!(
+                        "`{}` could{} refer to the {} defined here",
+                        segment.ident,
+                        also,
+                        tcx.def_kind_descr(kind, def_id)
+                    );
+                    lint.span_note(tcx.def_span(def_id), note_msg);
+                };
+
+                could_refer_to(DefKind::Variant, variant_def_id, "");
+                could_refer_to(mode.def_kind(), item_def_id, " also");
+
+                lint.span_suggestion(
+                    span,
+                    "use fully-qualified syntax",
+                    format!(
+                        "<{} as {}>::{}",
+                        self_ty,
+                        tcx.item_name(bound.def_id()),
+                        segment.ident
+                    ),
+                    Applicability::MachineApplicable,
+                );
+            });
+        }
+
+        Ok(TypeRelativePath::AssocItem(item_def_id, args))
+    }
+
+    /// Resolve a [type-relative](hir::QPath::TypeRelative) (and type-level) path.
+    fn resolve_type_relative_path(
+        &self,
+        self_ty: Ty<'tcx>,
+        hir_self_ty: &'tcx hir::Ty<'tcx>,
+        assoc_tag: ty::AssocTag,
+        segment: &'tcx hir::PathSegment<'tcx>,
+        qpath_hir_id: HirId,
+        span: Span,
+        variant_def_id: Option<DefId>,
+    ) -> Result<(DefId, ty::PolyTraitRef<'tcx>), ErrorGuaranteed> {
+        let tcx = self.tcx();
+
+        let self_ty_res = match hir_self_ty.kind {
+            hir::TyKind::Path(hir::QPath::Resolved(_, path)) => path.res,
+            _ => Res::Err,
         };
 
-        // Find the type of the associated item, and the trait where the associated
-        // item is declared.
-        let bound = match (qself_ty.kind(), qself_res) {
+        // Find the type of the assoc item, and the trait where the associated item is declared.
+        let bound = match (self_ty.kind(), self_ty_res) {
             (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => {
                 // `Self` in an impl of a trait -- we have a concrete self type and a
                 // trait reference.
@@ -1292,14 +1356,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
                 self.probe_single_bound_for_assoc_item(
                     || {
-                        traits::supertraits(
-                            tcx,
-                            ty::Binder::dummy(trait_ref.instantiate_identity()),
-                        )
+                        let trait_ref = ty::Binder::dummy(trait_ref.instantiate_identity());
+                        traits::supertraits(tcx, trait_ref)
                     },
                     AssocItemQSelf::SelfTyAlias,
-                    mode.assoc_tag(),
-                    assoc_ident,
+                    assoc_tag,
+                    segment.ident,
                     span,
                     None,
                 )?
@@ -1309,167 +1371,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
             ) => self.probe_single_ty_param_bound_for_assoc_item(
                 param_did.expect_local(),
-                qself.span,
-                mode.assoc_tag(),
-                assoc_ident,
+                hir_self_ty.span,
+                assoc_tag,
+                segment.ident,
                 span,
             )?,
             _ => {
-                let kind_str = assoc_tag_str(mode.assoc_tag());
-                let reported = if variant_resolution.is_some() {
-                    // Variant in type position
-                    let msg = format!("expected {kind_str}, found variant `{assoc_ident}`");
-                    self.dcx().span_err(span, msg)
-                } else if qself_ty.is_enum() {
-                    let mut err = self.dcx().create_err(NoVariantNamed {
-                        span: assoc_ident.span,
-                        ident: assoc_ident,
-                        ty: qself_ty,
-                    });
-
-                    let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
-                    if let Some(variant_name) = find_best_match_for_name(
-                        &adt_def
-                            .variants()
-                            .iter()
-                            .map(|variant| variant.name)
-                            .collect::<Vec<Symbol>>(),
-                        assoc_ident.name,
-                        None,
-                    ) && let Some(variant) =
-                        adt_def.variants().iter().find(|s| s.name == variant_name)
-                    {
-                        let mut suggestion = vec![(assoc_ident.span, variant_name.to_string())];
-                        if let hir::Node::Stmt(&hir::Stmt {
-                            kind: hir::StmtKind::Semi(expr), ..
-                        })
-                        | hir::Node::Expr(expr) = tcx.parent_hir_node(hir_ref_id)
-                            && let hir::ExprKind::Struct(..) = expr.kind
-                        {
-                            match variant.ctor {
-                                None => {
-                                    // struct
-                                    suggestion = vec![(
-                                        assoc_ident.span.with_hi(expr.span.hi()),
-                                        if variant.fields.is_empty() {
-                                            format!("{variant_name} {{}}")
-                                        } else {
-                                            format!(
-                                                "{variant_name} {{ {} }}",
-                                                variant
-                                                    .fields
-                                                    .iter()
-                                                    .map(|f| format!("{}: /* value */", f.name))
-                                                    .collect::<Vec<_>>()
-                                                    .join(", ")
-                                            )
-                                        },
-                                    )];
-                                }
-                                Some((hir::def::CtorKind::Fn, def_id)) => {
-                                    // tuple
-                                    let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
-                                    let inputs = fn_sig.inputs().skip_binder();
-                                    suggestion = vec![(
-                                        assoc_ident.span.with_hi(expr.span.hi()),
-                                        format!(
-                                            "{variant_name}({})",
-                                            inputs
-                                                .iter()
-                                                .map(|i| format!("/* {i} */"))
-                                                .collect::<Vec<_>>()
-                                                .join(", ")
-                                        ),
-                                    )];
-                                }
-                                Some((hir::def::CtorKind::Const, _)) => {
-                                    // unit
-                                    suggestion = vec![(
-                                        assoc_ident.span.with_hi(expr.span.hi()),
-                                        variant_name.to_string(),
-                                    )];
-                                }
-                            }
-                        }
-                        err.multipart_suggestion_verbose(
-                            "there is a variant with a similar name",
-                            suggestion,
-                            Applicability::HasPlaceholders,
-                        );
-                    } else {
-                        err.span_label(
-                            assoc_ident.span,
-                            format!("variant not found in `{qself_ty}`"),
-                        );
-                    }
-
-                    if let Some(sp) = tcx.hir_span_if_local(adt_def.did()) {
-                        err.span_label(sp, format!("variant `{assoc_ident}` not found here"));
-                    }
-
-                    err.emit()
-                } else if let Err(reported) = qself_ty.error_reported() {
-                    reported
-                } else {
-                    self.maybe_report_similar_assoc_fn(span, qself_ty, qself)?;
-
-                    let traits: Vec<_> =
-                        self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
-
-                    // Don't print `ty::Error` to the user.
-                    self.report_ambiguous_assoc(
-                        span,
-                        &[qself_ty.to_string()],
-                        &traits,
-                        assoc_ident.name,
-                        mode.assoc_tag(),
-                    )
-                };
-                return Err(reported);
+                return Err(self.report_unresolved_type_relative_path(
+                    self_ty,
+                    hir_self_ty,
+                    assoc_tag,
+                    segment.ident,
+                    qpath_hir_id,
+                    span,
+                    variant_def_id,
+                ));
             }
         };
 
-        let trait_did = bound.def_id();
         let assoc_item = self
-            .probe_assoc_item(assoc_ident, mode.assoc_tag(), hir_ref_id, span, trait_did)
+            .probe_assoc_item(segment.ident, assoc_tag, qpath_hir_id, span, bound.def_id())
             .expect("failed to find associated item");
-        let (def_id, args) = self.lower_assoc_shared(
-            span,
-            assoc_item.def_id,
-            assoc_segment,
-            bound,
-            mode.assoc_tag(),
-        )?;
-        let result = LoweredAssoc::Term(def_id, args);
-
-        if let Some(variant_def_id) = variant_resolution {
-            tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
-                lint.primary_message("ambiguous associated item");
-                let mut could_refer_to = |kind: DefKind, def_id, also| {
-                    let note_msg = format!(
-                        "`{}` could{} refer to the {} defined here",
-                        assoc_ident,
-                        also,
-                        tcx.def_kind_descr(kind, def_id)
-                    );
-                    lint.span_note(tcx.def_span(def_id), note_msg);
-                };
 
-                could_refer_to(DefKind::Variant, variant_def_id, "");
-                could_refer_to(mode.def_kind(), assoc_item.def_id, " also");
-
-                lint.span_suggestion(
-                    span,
-                    "use fully-qualified syntax",
-                    format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
-                    Applicability::MachineApplicable,
-                );
-            });
-        }
-        Ok(result)
+        Ok((assoc_item.def_id, bound))
     }
 
-    fn probe_inherent_assoc_shared(
+    /// Search for inherent associated items for use at the type level.
+    fn probe_inherent_assoc_item(
         &self,
         segment: &hir::PathSegment<'tcx>,
         adt_did: DefId,
@@ -1624,7 +1552,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             .collect();
 
         match &applicable_candidates[..] {
-            &[] => Err(self.complain_about_inherent_assoc_not_found(
+            &[] => Err(self.report_unresolved_inherent_assoc_item(
                 name,
                 self_ty,
                 candidates,
@@ -1635,7 +1563,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
             &[applicable_candidate] => Ok(applicable_candidate),
 
-            &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc(
+            &[_, ..] => Err(self.report_ambiguous_inherent_assoc_item(
                 name,
                 applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
                 span,
@@ -1763,9 +1691,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             .collect()
     }
 
-    /// Lower a qualified path to a type.
+    /// Lower a [resolved][hir::QPath::Resolved] associated type path to a projection.
     #[instrument(level = "debug", skip_all)]
-    fn lower_qpath_ty(
+    fn lower_resolved_assoc_ty_path(
         &self,
         span: Span,
         opt_self_ty: Option<Ty<'tcx>>,
@@ -1773,7 +1701,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_segment: Option<&hir::PathSegment<'tcx>>,
         item_segment: &hir::PathSegment<'tcx>,
     ) -> Ty<'tcx> {
-        match self.lower_qpath_shared(
+        match self.lower_resolved_assoc_item_path(
             span,
             opt_self_ty,
             item_def_id,
@@ -1788,9 +1716,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
-    /// Lower a qualified path to a const.
+    /// Lower a [resolved][hir::QPath::Resolved] associated const path to a (type-level) constant.
     #[instrument(level = "debug", skip_all)]
-    fn lower_qpath_const(
+    fn lower_resolved_assoc_const_path(
         &self,
         span: Span,
         opt_self_ty: Option<Ty<'tcx>>,
@@ -1798,7 +1726,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_segment: Option<&hir::PathSegment<'tcx>>,
         item_segment: &hir::PathSegment<'tcx>,
     ) -> Const<'tcx> {
-        match self.lower_qpath_shared(
+        match self.lower_resolved_assoc_item_path(
             span,
             opt_self_ty,
             item_def_id,
@@ -1814,8 +1742,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
+    /// Lower a [resolved][hir::QPath::Resolved] (type-level) associated item path.
     #[instrument(level = "debug", skip_all)]
-    fn lower_qpath_shared(
+    fn lower_resolved_assoc_item_path(
         &self,
         span: Span,
         opt_self_ty: Option<Ty<'tcx>>,
@@ -1830,7 +1759,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         debug!(?trait_def_id);
 
         let Some(self_ty) = opt_self_ty else {
-            return Err(self.error_missing_qpath_self_ty(
+            return Err(self.report_missing_self_ty_for_resolved_path(
                 trait_def_id,
                 span,
                 item_segment,
@@ -1849,57 +1778,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         Ok((item_def_id, item_args))
     }
 
-    fn error_missing_qpath_self_ty(
-        &self,
-        trait_def_id: DefId,
-        span: Span,
-        item_segment: &hir::PathSegment<'tcx>,
-        assoc_tag: ty::AssocTag,
-    ) -> ErrorGuaranteed {
-        let tcx = self.tcx();
-        let path_str = tcx.def_path_str(trait_def_id);
-
-        let def_id = self.item_def_id();
-        debug!(item_def_id = ?def_id);
-
-        // FIXME: document why/how this is different from `tcx.local_parent(def_id)`
-        let parent_def_id = tcx.hir_get_parent_item(tcx.local_def_id_to_hir_id(def_id)).to_def_id();
-        debug!(?parent_def_id);
-
-        // If the trait in segment is the same as the trait defining the item,
-        // use the `<Self as ..>` syntax in the error.
-        let is_part_of_self_trait_constraints = def_id.to_def_id() == trait_def_id;
-        let is_part_of_fn_in_self_trait = parent_def_id == trait_def_id;
-
-        let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
-            vec!["Self".to_string()]
-        } else {
-            // Find all the types that have an `impl` for the trait.
-            tcx.all_impls(trait_def_id)
-                .filter_map(|impl_def_id| tcx.impl_trait_header(impl_def_id))
-                .filter(|header| {
-                    // Consider only accessible traits
-                    tcx.visibility(trait_def_id).is_accessible_from(self.item_def_id(), tcx)
-                        && header.polarity != ty::ImplPolarity::Negative
-                })
-                .map(|header| header.trait_ref.instantiate_identity().self_ty())
-                // We don't care about blanket impls.
-                .filter(|self_ty| !self_ty.has_non_region_param())
-                .map(|self_ty| tcx.erase_regions(self_ty).to_string())
-                .collect()
-        };
-        // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
-        // references the trait. Relevant for the first case in
-        // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
-        self.report_ambiguous_assoc(
-            span,
-            &type_names,
-            &[path_str],
-            item_segment.ident.name,
-            assoc_tag,
-        )
-    }
-
     pub fn prohibit_generic_args<'a>(
         &self,
         segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
@@ -1908,7 +1786,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let args_visitors = segments.clone().flat_map(|segment| segment.args().args);
         let mut result = Ok(());
         if let Some(_) = args_visitors.clone().next() {
-            result = Err(self.report_prohibit_generics_error(
+            result = Err(self.report_prohibited_generic_args(
                 segments.clone(),
                 args_visitors,
                 err_extend,
@@ -2069,14 +1947,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         generic_segments
     }
 
-    /// Lower a type `Path` to a type.
+    /// Lower a [resolved][hir::QPath::Resolved] path to a type.
     #[instrument(level = "debug", skip_all)]
-    pub fn lower_path(
+    pub fn lower_resolved_ty_path(
         &self,
         opt_self_ty: Option<Ty<'tcx>>,
         path: &hir::Path<'tcx>,
         hir_id: HirId,
-        permit_variants: bool,
+        permit_variants: PermitVariants,
     ) -> Ty<'tcx> {
         debug!(?path.res, ?opt_self_ty, ?path.segments);
         let tcx = self.tcx();
@@ -2107,7 +1985,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 );
                 self.lower_path_segment(span, did, path.segments.last().unwrap())
             }
-            Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => {
+            Res::Def(kind @ DefKind::Variant, def_id)
+                if let PermitVariants::Yes = permit_variants =>
+            {
                 // Lower "variant type" as if it were a real type.
                 // The resulting `Ty` is type of the variant's enum for now.
                 assert_eq!(opt_self_ty, None);
@@ -2202,7 +2082,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 } else {
                     None
                 };
-                self.lower_qpath_ty(
+                self.lower_resolved_assoc_ty_path(
                     span,
                     opt_self_ty,
                     def_id,
@@ -2298,7 +2178,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
-    /// Convert a [`hir::ConstArg`] to a [`ty::Const`](Const).
+    /// Lower a [`hir::ConstArg`] to a (type-level) [`ty::Const`](Const).
     #[instrument(skip(self), level = "debug")]
     pub fn lower_const_arg(
         &self,
@@ -2323,7 +2203,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             if tcx.features().generic_const_parameter_types()
                 && (anon_const_type.has_free_regions() || anon_const_type.has_erased_regions())
             {
-                let e = tcx.dcx().span_err(
+                let e = self.dcx().span_err(
                     const_arg.span(),
                     "anonymous constants with lifetimes in their type are not yet supported",
                 );
@@ -2334,7 +2214,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // use this type to feed the `type_of` and query results must not contain inference
             // variables otherwise we will ICE.
             if anon_const_type.has_non_region_infer() {
-                let e = tcx.dcx().span_err(
+                let e = self.dcx().span_err(
                     const_arg.span(),
                     "anonymous constants with inferred types are not yet supported",
                 );
@@ -2344,7 +2224,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // We error when the type contains unsubstituted generics since we do not currently
             // give the anon const any of the generics from the parent.
             if anon_const_type.has_non_region_param() {
-                let e = tcx.dcx().span_err(
+                let e = self.dcx().span_err(
                     const_arg.span(),
                     "anonymous constants referencing generics are not yet supported",
                 );
@@ -2363,13 +2243,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             hir::ConstArgKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
                 debug!(?maybe_qself, ?path);
                 let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
-                self.lower_const_path_resolved(opt_self_ty, path, hir_id)
+                self.lower_resolved_const_path(opt_self_ty, path, hir_id)
             }
-            hir::ConstArgKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
-                debug!(?qself, ?segment);
-                let ty = self.lower_ty(qself);
-                self.lower_assoc_path_const(hir_id, const_arg.span(), ty, qself, segment)
-                    .unwrap_or_else(|guar| Const::new_error(tcx, guar))
+            hir::ConstArgKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => {
+                debug!(?hir_self_ty, ?segment);
+                let self_ty = self.lower_ty(hir_self_ty);
+                self.lower_type_relative_const_path(
+                    self_ty,
+                    hir_self_ty,
+                    segment,
+                    hir_id,
+                    const_arg.span(),
+                )
+                .unwrap_or_else(|guar| Const::new_error(tcx, guar))
             }
             hir::ConstArgKind::Path(qpath @ hir::QPath::LangItem(..)) => {
                 ty::Const::new_error_with_message(
@@ -2383,7 +2269,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
     }
 
-    fn lower_const_path_resolved(
+    /// Lower a [resolved][hir::QPath::Resolved] path to a (type-level) constant.
+    fn lower_resolved_const_path(
         &self,
         opt_self_ty: Option<Ty<'tcx>>,
         path: &hir::Path<'tcx>,
@@ -2420,7 +2307,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 } else {
                     None
                 };
-                self.lower_qpath_const(
+                self.lower_resolved_assoc_const_path(
                     span,
                     opt_self_ty,
                     did,
@@ -2630,7 +2517,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
                 debug!(?maybe_qself, ?path);
                 let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
-                self.lower_path(opt_self_ty, path, hir_ty.hir_id, false)
+                self.lower_resolved_ty_path(opt_self_ty, path, hir_ty.hir_id, PermitVariants::No)
             }
             &hir::TyKind::OpaqueDef(opaque_ty) => {
                 // If this is an RPITIT and we are using the new RPITIT lowering scheme, we
@@ -2684,12 +2571,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let guar = self.dcx().emit_err(BadReturnTypeNotation { span: hir_ty.span });
                 Ty::new_error(tcx, guar)
             }
-            hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
-                debug!(?qself, ?segment);
-                let ty = self.lower_ty(qself);
-                self.lower_assoc_path_ty(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false)
-                    .map(|(ty, _, _)| ty)
-                    .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
+            hir::TyKind::Path(hir::QPath::TypeRelative(hir_self_ty, segment)) => {
+                debug!(?hir_self_ty, ?segment);
+                let self_ty = self.lower_ty(hir_self_ty);
+                self.lower_type_relative_ty_path(
+                    self_ty,
+                    hir_self_ty,
+                    segment,
+                    hir_ty.hir_id,
+                    hir_ty.span,
+                    PermitVariants::No,
+                )
+                .map(|(ty, _, _)| ty)
+                .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
             }
             &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span)) => {
                 let def_id = tcx.require_lang_item(lang_item, Some(span));