diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 18fc1158b04..1181925dd96 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -67,10 +67,8 @@ use rustc_hir::{Item, ItemKind, Node};
 use rustc_middle::dep_graph::DepContext;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{
-    self,
-    error::TypeError,
-    subst::{GenericArgKind, Subst, SubstsRef},
-    Binder, EarlyBinder, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
+    self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable,
+    TypeSuperFoldable,
 };
 use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
 use rustc_target::spec::abi;
@@ -926,10 +924,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         mut t1_out: &mut DiagnosticStyledString,
         mut t2_out: &mut DiagnosticStyledString,
         path: String,
-        sub: ty::subst::SubstsRef<'tcx>,
+        sub: &'tcx [ty::GenericArg<'tcx>],
         other_path: String,
         other_ty: Ty<'tcx>,
     ) -> Option<()> {
+        // FIXME/HACK: Go back to `SubstsRef` to use its inherent methods,
+        // ideally that shouldn't be necessary.
+        let sub = self.tcx.intern_substs(sub);
         for (i, ta) in sub.types().enumerate() {
             if ta == other_ty {
                 self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty);
@@ -960,45 +961,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
     }
 
-    /// For generic types with parameters with defaults, remove the parameters corresponding to
-    /// the defaults. This repeats a lot of the logic found in `ty::print::pretty`.
-    fn strip_generic_default_params(
-        &self,
-        def_id: DefId,
-        substs: ty::subst::SubstsRef<'tcx>,
-    ) -> SubstsRef<'tcx> {
-        let generics = self.tcx.generics_of(def_id);
-        let mut num_supplied_defaults = 0;
-
-        let default_params = generics.params.iter().rev().filter_map(|param| match param.kind {
-            ty::GenericParamDefKind::Type { has_default: true, .. } => Some(param.def_id),
-            ty::GenericParamDefKind::Const { has_default: true } => Some(param.def_id),
-            _ => None,
-        });
-        for (def_id, actual) in iter::zip(default_params, substs.iter().rev()) {
-            match actual.unpack() {
-                GenericArgKind::Const(c) => {
-                    if EarlyBinder(self.tcx.const_param_default(def_id)).subst(self.tcx, substs)
-                        != c
-                    {
-                        break;
-                    }
-                }
-                GenericArgKind::Type(ty) => {
-                    if self.tcx.bound_type_of(def_id).subst(self.tcx, substs) != ty {
-                        break;
-                    }
-                }
-                _ => break,
-            }
-            num_supplied_defaults += 1;
-        }
-        let len = generics.params.len();
-        let mut generics = generics.clone();
-        generics.params.truncate(len - num_supplied_defaults);
-        substs.truncate_to(self.tcx, &generics)
-    }
-
     /// Given two `fn` signatures highlight only sub-parts that are different.
     fn cmp_fn_sig(
         &self,
@@ -1156,8 +1118,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => {
                 let did1 = def1.did();
                 let did2 = def2.did();
-                let sub_no_defaults_1 = self.strip_generic_default_params(did1, sub1);
-                let sub_no_defaults_2 = self.strip_generic_default_params(did2, sub2);
+                let sub_no_defaults_1 =
+                    self.tcx.generics_of(did1).own_substs_no_defaults(self.tcx, sub1);
+                let sub_no_defaults_2 =
+                    self.tcx.generics_of(did2).own_substs_no_defaults(self.tcx, sub2);
                 let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
                 let path1 = self.tcx.def_path_str(did1);
                 let path2 = self.tcx.def_path_str(did2);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index 7b5c377f7b4..207d2870c5c 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -2,6 +2,7 @@ use crate::infer::type_variable::TypeVariableOriginKind;
 use crate::infer::InferCtxt;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
+use rustc_hir::def::Res;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -11,7 +12,7 @@ use rustc_middle::infer::unify_key::ConstVariableOriginKind;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
-use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, InferConst};
+use rustc_middle::ty::{self, DefIdTree, InferConst};
 use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{BytePos, Span};
@@ -853,12 +854,23 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
                             hir::TyKind::Path(hir::QPath::Resolved(_self_ty, path)),
                         ) => {
                             if tcx.res_generics_def_id(path.res) != Some(def.did()) {
-                                bug!(
-                                    "unexpected path: def={:?} substs={:?} path={:?}",
-                                    def,
-                                    substs,
-                                    path,
-                                );
+                                match path.res {
+                                    Res::Def(DefKind::TyAlias, _) => {
+                                        // FIXME: Ideally we should support this. For that
+                                        // we have to map back from the self type to the
+                                        // type alias though. That's difficult.
+                                        //
+                                        // See the `need_type_info/type-alias.rs` test for
+                                        // some examples.
+                                    }
+                                    // There cannot be inference variables in the self type,
+                                    // so there's nothing for us to do here.
+                                    Res::SelfTy { .. } => {}
+                                    _ => warn!(
+                                        "unexpected path: def={:?} substs={:?} path={:?}",
+                                        def, substs, path,
+                                    ),
+                                }
                             } else {
                                 return Box::new(
                                     self.resolved_path_inferred_subst_iter(path, substs)
@@ -958,26 +970,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
                 generics.own_substs(substs).iter().position(|&arg| self.generic_arg_is_target(arg))
             {
                 let substs = self.infcx.resolve_vars_if_possible(substs);
-                let num_args = generics
-                    .params
-                    .iter()
-                    .rev()
-                    .filter(|&p| !matches!(p.kind, GenericParamDefKind::Lifetime))
-                    .skip_while(|&param| {
-                        if let Some(default) = param.default_value(tcx) {
-                            // FIXME: Using structural comparisions has a bunch of false negatives.
-                            //
-                            // We should instead try to replace inference variables with placeholders and
-                            // then use `infcx.can_eq`. That probably should be a separate method
-                            // generally used during error reporting.
-                            default.subst(tcx, substs) == substs[param.index as usize]
-                        } else {
-                            false
-                        }
-                    })
-                    .count();
-                let generic_args =
-                    &generics.own_substs(substs)[generics.own_counts().lifetimes..][..num_args];
+                let generic_args = &generics.own_substs_no_defaults(tcx, substs)
+                    [generics.own_counts().lifetimes..];
                 let span = match expr.kind {
                     ExprKind::MethodCall(path, _, _) => path.ident.span,
                     _ => expr.span,
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 1feabb2d6b1..5fff840c39e 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -228,8 +228,47 @@ impl<'tcx> Generics {
         })
     }
 
+    /// Returns the substs corresponding to the generic parameters
+    /// of this item, excluding `Self`.
+    ///
+    /// **This should only be used for diagnostics purposes.**
+    pub fn own_substs_no_defaults(
+        &'tcx self,
+        tcx: TyCtxt<'tcx>,
+        substs: &'tcx [ty::GenericArg<'tcx>],
+    ) -> &'tcx [ty::GenericArg<'tcx>] {
+        let mut own_params = self.parent_count..self.count();
+        if self.has_self && self.parent.is_none() {
+            own_params.start = 1;
+        }
+
+        // Filter the default arguments.
+        //
+        // This currently uses structural equality instead
+        // of semantic equivalance. While not ideal, that's
+        // good enough for now as this should only be used
+        // for diagnostics anyways.
+        own_params.end -= self
+            .params
+            .iter()
+            .rev()
+            .take_while(|param| {
+                param.default_value(tcx).map_or(false, |default| {
+                    default.subst(tcx, substs) == substs[param.index as usize]
+                })
+            })
+            .count();
+
+        &substs[own_params]
+    }
+
     /// Returns the substs corresponding to the generic parameters of this item, excluding `Self`.
-    pub fn own_substs(&'tcx self, substs: SubstsRef<'tcx>) -> &'tcx [ty::GenericArg<'tcx>] {
+    ///
+    /// **This should only be used for diagnostics purposes.**
+    pub fn own_substs(
+        &'tcx self,
+        substs: &'tcx [ty::GenericArg<'tcx>],
+    ) -> &'tcx [ty::GenericArg<'tcx>] {
         let own = &substs[self.parent_count..][..self.params.len()];
         if self.has_self && self.parent.is_none() { &own[1..] } else { &own }
     }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 48ac7ecdf05..7f0f3755c4b 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -22,7 +22,6 @@ use crate::mir::{Body, GeneratorLayout};
 use crate::traits::{self, Reveal};
 use crate::ty;
 use crate::ty::fast_reject::SimplifiedType;
-use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef};
 use crate::ty::util::Discr;
 pub use adt::*;
 pub use assoc::*;
@@ -44,6 +43,7 @@ use rustc_session::cstore::CrateStoreDyn;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::abi::Align;
+pub use subst::*;
 pub use vtable::*;
 
 use std::fmt::Debug;
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index 9d8124eb25d..5ad93d77820 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -149,7 +149,7 @@ pub trait Printer<'tcx>: Sized {
                         // on top of the same path, but without its own generics.
                         _ => {
                             if !generics.params.is_empty() && substs.len() >= generics.count() {
-                                let args = self.generic_args_to_print(generics, substs);
+                                let args = generics.own_substs_no_defaults(self.tcx(), substs);
                                 return self.path_generic_args(
                                     |cx| cx.print_def_path(def_id, parent_substs),
                                     args,
@@ -184,43 +184,6 @@ pub trait Printer<'tcx>: Sized {
         }
     }
 
-    fn generic_args_to_print(
-        &self,
-        generics: &'tcx ty::Generics,
-        substs: &'tcx [GenericArg<'tcx>],
-    ) -> &'tcx [GenericArg<'tcx>] {
-        let mut own_params = generics.parent_count..generics.count();
-
-        // Don't print args for `Self` parameters (of traits).
-        if generics.has_self && own_params.start == 0 {
-            own_params.start = 1;
-        }
-
-        // Don't print args that are the defaults of their respective parameters.
-        own_params.end -= generics
-            .params
-            .iter()
-            .rev()
-            .take_while(|param| match param.kind {
-                ty::GenericParamDefKind::Lifetime => false,
-                ty::GenericParamDefKind::Type { has_default, .. } => {
-                    has_default
-                        && substs[param.index as usize]
-                            == GenericArg::from(
-                                self.tcx().bound_type_of(param.def_id).subst(self.tcx(), substs),
-                            )
-                }
-                ty::GenericParamDefKind::Const { has_default } => {
-                    has_default
-                        && substs[param.index as usize]
-                            == GenericArg::from(self.tcx().const_param_default(param.def_id))
-                }
-            })
-            .count();
-
-        &substs[own_params]
-    }
-
     fn default_print_impl_path(
         self,
         impl_def_id: DefId,
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 7328b18a328..53a97a46b2d 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -825,12 +825,11 @@ pub trait PrettyPrinter<'tcx>:
 
         for (fn_once_trait_ref, entry) in fn_traits {
             // Get the (single) generic ty (the args) of this FnOnce trait ref.
-            let generics = self.generic_args_to_print(
-                self.tcx().generics_of(fn_once_trait_ref.def_id()),
-                fn_once_trait_ref.skip_binder().substs,
-            );
+            let generics = self.tcx().generics_of(fn_once_trait_ref.def_id());
+            let args =
+                generics.own_substs_no_defaults(self.tcx(), fn_once_trait_ref.skip_binder().substs);
 
-            match (entry.return_ty, generics[0].expect_ty()) {
+            match (entry.return_ty, args[0].expect_ty()) {
                 // We can only print `impl Fn() -> ()` if we have a tuple of args and we recorded
                 // a return type.
                 (Some(return_ty), arg_tys) if matches!(arg_tys.kind(), ty::Tuple(_)) => {
@@ -892,15 +891,13 @@ pub trait PrettyPrinter<'tcx>:
                 print(trait_ref.skip_binder().print_only_trait_name())
             );
 
-            let generics = self.generic_args_to_print(
-                self.tcx().generics_of(trait_ref.def_id()),
-                trait_ref.skip_binder().substs,
-            );
+            let generics = self.tcx().generics_of(trait_ref.def_id());
+            let args = generics.own_substs_no_defaults(self.tcx(), trait_ref.skip_binder().substs);
 
-            if !generics.is_empty() || !assoc_items.is_empty() {
+            if !args.is_empty() || !assoc_items.is_empty() {
                 let mut first = true;
 
-                for ty in generics {
+                for ty in args {
                     if first {
                         p!("<");
                         first = false;
@@ -1071,10 +1068,10 @@ pub trait PrettyPrinter<'tcx>:
                     let dummy_cx = cx.tcx().mk_ty_infer(ty::FreshTy(0));
                     let principal = principal.with_self_ty(cx.tcx(), dummy_cx);
 
-                    let args = cx.generic_args_to_print(
-                        cx.tcx().generics_of(principal.def_id),
-                        principal.substs,
-                    );
+                    let args = cx
+                        .tcx()
+                        .generics_of(principal.def_id)
+                        .own_substs_no_defaults(cx.tcx(), principal.substs);
 
                     // Don't print `'_` if there's no unerased regions.
                     let print_regions = args.iter().any(|arg| match arg.unpack() {
diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs
index 308c121a941..f633e56b0ec 100644
--- a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs
+++ b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.rs
@@ -15,12 +15,12 @@ impl Traitor<1, 2> for u64 {}
 
 
 fn uwu<const N: u8>() -> impl Traitor<N> {
-    //~^ error: the trait bound `u32: Traitor<N, N>` is not satisfied
+    //~^ error: the trait bound `u32: Traitor<N>` is not satisfied
     1_u32
 }
 
 fn owo() -> impl Traitor {
-    //~^ error: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
+    //~^ error: the trait bound `u64: Traitor` is not satisfied
     1_u64
 }
 
diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
index 8031da28ca1..cbe4a4ac0d6 100644
--- a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
+++ b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
@@ -6,21 +6,21 @@ LL | fn rawr() -> impl Trait {
    |
    = help: the trait `Trait` is implemented for `Uwu<N>`
 
-error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied
+error[E0277]: the trait bound `u32: Traitor<N>` is not satisfied
   --> $DIR/rp_impl_trait_fail.rs:17:26
    |
 LL | fn uwu<const N: u8>() -> impl Traitor<N> {
-   |                          ^^^^^^^^^^^^^^^ the trait `Traitor<N, N>` is not implemented for `u32`
+   |                          ^^^^^^^^^^^^^^^ the trait `Traitor<N>` is not implemented for `u32`
    |
    = help: the following other types implement trait `Traitor<N, M>`:
              <u32 as Traitor<N, 2_u8>>
              <u64 as Traitor<1_u8, 2_u8>>
 
-error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
+error[E0277]: the trait bound `u64: Traitor` is not satisfied
   --> $DIR/rp_impl_trait_fail.rs:22:13
    |
 LL | fn owo() -> impl Traitor {
-   |             ^^^^^^^^^^^^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64`
+   |             ^^^^^^^^^^^^ the trait `Traitor` is not implemented for `u64`
    |
    = help: the following other types implement trait `Traitor<N, M>`:
              <u32 as Traitor<N, 2_u8>>
diff --git a/src/test/ui/const-generics/defaults/trait_objects_fail.rs b/src/test/ui/const-generics/defaults/trait_objects_fail.rs
index 7ba12d02b6e..5e779d2e8de 100644
--- a/src/test/ui/const-generics/defaults/trait_objects_fail.rs
+++ b/src/test/ui/const-generics/defaults/trait_objects_fail.rs
@@ -26,5 +26,5 @@ fn main() {
     foo(&10_u32);
     //~^ error: the trait bound `u32: Trait` is not satisfied
     bar(&true);
-    //~^ error: the trait bound `bool: Traitor<{_: u8}, {_: u8}>` is not satisfied
+    //~^ error: the trait bound `bool: Traitor<{_: u8}>` is not satisfied
 }
diff --git a/src/test/ui/const-generics/defaults/trait_objects_fail.stderr b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr
index 7f8a1f742d8..60dc96f675a 100644
--- a/src/test/ui/const-generics/defaults/trait_objects_fail.stderr
+++ b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr
@@ -9,16 +9,16 @@ LL |     foo(&10_u32);
    = help: the trait `Trait<2_u8>` is implemented for `u32`
    = note: required for the cast to the object type `dyn Trait`
 
-error[E0277]: the trait bound `bool: Traitor<{_: u8}, {_: u8}>` is not satisfied
+error[E0277]: the trait bound `bool: Traitor<{_: u8}>` is not satisfied
   --> $DIR/trait_objects_fail.rs:28:9
    |
 LL |     bar(&true);
-   |     --- ^^^^^ the trait `Traitor<{_: u8}, {_: u8}>` is not implemented for `bool`
+   |     --- ^^^^^ the trait `Traitor<{_: u8}>` is not implemented for `bool`
    |     |
    |     required by a bound introduced by this call
    |
    = help: the trait `Traitor<2_u8, 3_u8>` is implemented for `bool`
-   = note: required for the cast to the object type `dyn Traitor<{_: u8}, {_: u8}>`
+   = note: required for the cast to the object type `dyn Traitor<{_: u8}>`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/inference/need_type_info/concrete-impl.rs b/src/test/ui/inference/need_type_info/concrete-impl.rs
new file mode 100644
index 00000000000..72e0e74f323
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/concrete-impl.rs
@@ -0,0 +1,16 @@
+trait Ambiguous<A> {
+    fn method() {}
+}
+
+struct One;
+struct Two;
+struct Struct;
+
+impl Ambiguous<One> for Struct {}
+impl Ambiguous<Two> for Struct {}
+
+fn main() {
+    <Struct as Ambiguous<_>>::method();
+    //~^ ERROR type annotations needed
+    //~| ERROR type annotations needed
+}
diff --git a/src/test/ui/inference/need_type_info/concrete-impl.stderr b/src/test/ui/inference/need_type_info/concrete-impl.stderr
new file mode 100644
index 00000000000..b79d34affa2
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/concrete-impl.stderr
@@ -0,0 +1,33 @@
+error[E0282]: type annotations needed
+  --> $DIR/concrete-impl.rs:13:5
+   |
+LL |     <Struct as Ambiguous<_>>::method();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `Self` declared on the trait `Ambiguous`
+   |
+help: consider specifying the generic argument
+   |
+LL |     <Struct as Ambiguous::<_>>::method();
+   |                         ~~~~~
+
+error[E0283]: type annotations needed
+  --> $DIR/concrete-impl.rs:13:5
+   |
+LL |     <Struct as Ambiguous<_>>::method();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `Self` declared on the trait `Ambiguous`
+   |
+note: multiple `impl`s satisfying `Struct: Ambiguous<_>` found
+  --> $DIR/concrete-impl.rs:9:1
+   |
+LL | impl Ambiguous<One> for Struct {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl Ambiguous<Two> for Struct {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider specifying the generic argument
+   |
+LL |     <Struct as Ambiguous::<_>>::method();
+   |                         ~~~~~
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0282, E0283.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/src/test/ui/inference/need_type_info/self-ty-in-path.rs b/src/test/ui/inference/need_type_info/self-ty-in-path.rs
new file mode 100644
index 00000000000..768a8cc3778
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/self-ty-in-path.rs
@@ -0,0 +1,13 @@
+// Test that we don't ICE when encountering a `Self` in a path.
+struct TestErr<T>(T);
+
+impl<T> TestErr<T> {
+    fn func_a<U>() {}
+
+    fn func_b() {
+        Self::func_a();
+        //~^ ERROR type annotations needed
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/inference/need_type_info/self-ty-in-path.stderr b/src/test/ui/inference/need_type_info/self-ty-in-path.stderr
new file mode 100644
index 00000000000..04b521dbdb3
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/self-ty-in-path.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed
+  --> $DIR/self-ty-in-path.rs:8:9
+   |
+LL |         Self::func_a();
+   |         ^^^^^^^^^^^^ cannot infer type of the type parameter `U` declared on the associated function `func_a`
+   |
+help: consider specifying the generic argument
+   |
+LL |         Self::func_a::<U>();
+   |                     +++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/inference/need_type_info/type-alias-indirect.rs b/src/test/ui/inference/need_type_info/type-alias-indirect.rs
new file mode 100644
index 00000000000..0ed02ddc5f3
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/type-alias-indirect.rs
@@ -0,0 +1,18 @@
+// An addition to the `type-alias.rs` test,
+// see the FIXME in that file for why this test
+// exists.
+//
+// If there is none, feel free to remove this test
+// again.
+struct Ty<T>(T);
+impl<T> Ty<T> {
+    fn new() {}
+}
+
+type IndirectAlias<T> = Ty<Box<T>>;
+fn indirect_alias() {
+    IndirectAlias::new();
+    //~^ ERROR type annotations needed
+}
+
+fn main() {}
diff --git a/src/test/ui/inference/need_type_info/type-alias-indirect.stderr b/src/test/ui/inference/need_type_info/type-alias-indirect.stderr
new file mode 100644
index 00000000000..6161690df50
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/type-alias-indirect.stderr
@@ -0,0 +1,9 @@
+error[E0282]: type annotations needed
+  --> $DIR/type-alias-indirect.rs:14:5
+   |
+LL |     IndirectAlias::new();
+   |     ^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/inference/need_type_info/type-alias.rs b/src/test/ui/inference/need_type_info/type-alias.rs
new file mode 100644
index 00000000000..f921b046b6c
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/type-alias.rs
@@ -0,0 +1,36 @@
+// Test the inference errors in case the relevant path
+// uses a type alias.
+//
+// Regression test for #97698.
+struct Ty<T>(T);
+impl<T> Ty<T> {
+    fn new() {}
+}
+
+type DirectAlias<T> = Ty<T>;
+fn direct_alias() {
+    DirectAlias::new()
+    //~^ ERROR type annotations needed
+}
+
+type IndirectAlias<T> = Ty<Box<T>>;
+fn indirect_alias() {
+    IndirectAlias::new();
+    // FIXME: This should also emit an error.
+    //
+    // Added it separately as `type-alias-indirect.rs`
+    // where it does error.
+}
+
+struct TyDefault<T, U = u32>(T, U);
+impl<T> TyDefault<T> {
+    fn new() {}
+}
+
+type DirectButWithDefaultAlias<T> = TyDefault<T>;
+fn direct_but_with_default_alias() {
+    DirectButWithDefaultAlias::new();
+    //~^ ERROR type annotations needed
+}
+
+fn main() {}
diff --git a/src/test/ui/inference/need_type_info/type-alias.stderr b/src/test/ui/inference/need_type_info/type-alias.stderr
new file mode 100644
index 00000000000..a33f49baf54
--- /dev/null
+++ b/src/test/ui/inference/need_type_info/type-alias.stderr
@@ -0,0 +1,15 @@
+error[E0282]: type annotations needed
+  --> $DIR/type-alias.rs:12:5
+   |
+LL |     DirectAlias::new()
+   |     ^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T`
+
+error[E0282]: type annotations needed
+  --> $DIR/type-alias.rs:32:5
+   |
+LL |     DirectButWithDefaultAlias::new();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0282`.