about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-01-24 11:12:01 +0000
committerbors <bors@rust-lang.org>2025-01-24 11:12:01 +0000
commit8231e8599e238ff4e717639bd68c6abb8579fe8d (patch)
treebd5801d2bc7eb09acfa728ae6b5d83fafc249a1f
parent061ee95ce197dc9b276fc5363eddbfc0ecc08584 (diff)
parentc58fe21cb919aedce5408f478c86b19e8e17bab2 (diff)
downloadrust-8231e8599e238ff4e717639bd68c6abb8579fe8d.tar.gz
rust-8231e8599e238ff4e717639bd68c6abb8579fe8d.zip
Auto merge of #135272 - BoxyUwU:generic_arg_infer_reliability_2, r=compiler-errors
Forbid usage of `hir` `Infer` const/ty variants in ambiguous contexts

The feature `generic_arg_infer` allows providing `_` as an argument to const generics in order to infer them. This introduces a syntactic ambiguity as to whether generic arguments are type or const arguments. In order to get around this we introduced a fourth `GenericArg` variant, `Infer` used to represent `_` as an argument to generic parameters when we don't know if its a type or a const argument.

This made hir visitors that care about `TyKind::Infer` or `ConstArgKind::Infer` very error prone as checking for `TyKind::Infer`s in  `visit_ty` would find *some* type infer arguments but not *all* of them as they would sometimes be lowered to `GenericArg::Infer` instead.

Additionally the `visit_infer` method would previously only visit `GenericArg::Infer` not *all* infers (e.g. `TyKind::Infer`), this made it very easy to override `visit_infer` and expect it to visit all infers when in reality it would only visit *some* infers.

---

This PR aims to fix those issues by making the `TyKind` and `ConstArgKind` types generic over whether the infer types/consts are represented by `Ty/ConstArgKind::Infer` or out of line (e.g. by a `GenericArg::Infer` or accessible by overiding `visit_infer`). We then make HIR Visitors convert all const args and types to the versions where infer vars are stored out of line and call `visit_infer` in cases where a `Ty`/`Const` would previously have had a `Ty/ConstArgKind::Infer` variant:

API Summary
```rust
enum AmbigArg {}

enum Ty/ConstArgKind<Unambig = ()> {
   ...
   Infer(Unambig),
}

impl Ty/ConstArg {
  fn try_as_ambig_ty/ct(self) -> Option<Ty/ConstArg<AmbigArg>>;
}
impl Ty/ConstArg<AmbigArg> {
  fn as_unambig_ty/ct(self) -> Ty/ConstArg;
}

enum InferKind {
  Ty(Ty),
  Const(ConstArg),
  Ambig(InferArg),
}

trait Visitor {
  ...
  fn visit_ty/const_arg(&mut self, Ty/ConstArg<AmbigArg>) -> Self::Result;
  fn visit_infer(&mut self, id: HirId, sp: Span, kind: InferKind) -> Self::Result;
}

// blanket impl'd, not meant to be overriden
trait VisitorExt {
  fn visit_ty/const_arg_unambig(&mut self, Ty/ConstArg) -> Self::Result;
}

fn walk_unambig_ty/const_arg(&mut V, Ty/ConstArg) -> Self::Result;
fn walk_ty/const_arg(&mut V, Ty/ConstArg<AmbigArg>) -> Self::Result;
```

The end result is that `visit_infer` visits *all* infer args and is also the *only* way to visit an infer arg, `visit_ty` and `visit_const_arg` can now no longer encounter a `Ty/ConstArgKind::Infer`. Representing this in the type system means that it is now very difficult to mess things up, either accessing `TyKind::Infer` "just works" and you won't miss *some* type infers- or it doesn't work and you have to look at `visit_infer` or some `GenericArg::Infer` which forces you to think about the full complexity involved.

Unfortunately there is no lint right now about explicitly matching on uninhabited variants, I can't find the context for why this is the case :woman_shrugging:

I'm not convinced the framing of un/ambig ty/consts is necessarily the right one but I'm not sure what would be better. I somewhat like calling them full/partial types based on the fact that `Ty<Partial>`/`Ty<Full>` directly specifies how many of the type kinds are actually represented compared to `Ty<Ambig>` which which leaves that to the reader to figure out based on the logical consequences of it the type being in an ambiguous position.

---

tool changes have been modified in their own commits for easier reviewing by anyone getting cc'd from subtree changes. I also attempted to split out "bug fixes arising from the refactoring" into their own commit so they arent lumped in with a big general refactor commit

Fixes #112110
-rw-r--r--compiler/rustc_ast/src/ast.rs38
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs40
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs40
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs207
-rw-r--r--compiler/rustc_hir/src/hir/tests.rs83
-rw-r--r--compiler/rustc_hir/src/intravisit.rs226
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs37
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs60
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs29
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs22
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs19
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs17
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs21
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs27
-rw-r--r--compiler/rustc_lint/src/internal.rs6
-rw-r--r--compiler/rustc_lint/src/late.rs9
-rw-r--r--compiler/rustc_lint/src/lints.rs3
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs4
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs4
-rw-r--r--compiler/rustc_lint/src/pass_by_value.rs5
-rw-r--r--compiler/rustc_lint/src/passes.rs2
-rw-r--r--compiler/rustc_lint/src/traits.rs8
-rw-r--r--compiler/rustc_lint/src/types.rs7
-rw-r--r--compiler/rustc_middle/src/ty/context.rs6
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs18
-rw-r--r--compiler/rustc_middle/src/values.rs8
-rw-r--r--compiler/rustc_passes/src/check_attr.rs2
-rw-r--r--compiler/rustc_passes/src/dead.rs2
-rw-r--r--compiler/rustc_passes/src/input_stats.rs7
-rw-r--r--compiler/rustc_passes/src/stability.rs13
-rw-r--r--compiler/rustc_privacy/src/lib.rs22
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs16
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs22
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs10
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs19
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs13
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs4
-rw-r--r--src/librustdoc/clean/mod.rs23
-rw-r--r--src/librustdoc/visit_ast.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/box_default.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/as_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_macros.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_types.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mut.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/op_ref.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/ref_option_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/types/type_complexity.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/types/vec_box.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs15
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs6
-rw-r--r--tests/ui/closures/binder/forbid_ambig_const_infers.rs9
-rw-r--r--tests/ui/closures/binder/forbid_ambig_const_infers.stderr10
-rw-r--r--tests/ui/closures/binder/forbid_ambig_type_infers.rs9
-rw-r--r--tests/ui/closures/binder/forbid_ambig_type_infers.stderr10
-rw-r--r--tests/ui/closures/binder/forbid_const_infer.rs7
-rw-r--r--tests/ui/closures/binder/forbid_const_infer.stderr10
-rw-r--r--tests/ui/const-generics/generic_arg_infer/parend_infer.rs12
-rw-r--r--tests/ui/const-generics/issues/issue-62878.min.stderr14
-rw-r--r--tests/ui/const-generics/issues/issue-62878.rs2
-rw-r--r--tests/ui/did_you_mean/bad-assoc-ty.stderr5
-rw-r--r--tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr15
-rw-r--r--tests/ui/feature-gates/feature-gate-generic_arg_infer.rs6
-rw-r--r--tests/ui/generics/issue-79605.stderr5
-rw-r--r--tests/ui/macros/macro-span-issue-116502.stderr7
-rw-r--r--tests/ui/parser/issues/issue-14303-fncall.full.stderr8
-rw-r--r--tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr8
-rw-r--r--tests/ui/parser/issues/issue-14303-fncall.rs9
-rw-r--r--tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr4
-rw-r--r--tests/ui/type-alias-impl-trait/issue-77179.stderr5
-rw-r--r--tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs2
-rw-r--r--tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr4
-rw-r--r--tests/ui/typeck/typeck_type_placeholder_item.stderr10
119 files changed, 1054 insertions, 667 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 8e73df63ef5..727fd59c6b3 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -28,6 +28,7 @@ use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::tagged_ptr::Tag;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 pub use rustc_span::AttrId;
 use rustc_span::source_map::{Spanned, respan};
@@ -287,6 +288,7 @@ impl ParenthesizedArgs {
     }
 }
 
+use crate::AstDeref;
 pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId};
 
 /// Modifiers on a trait bound like `~const`, `?` and `!`.
@@ -2165,6 +2167,14 @@ impl Ty {
         }
         final_ty
     }
+
+    pub fn is_maybe_parenthesised_infer(&self) -> bool {
+        match &self.kind {
+            TyKind::Infer => true,
+            TyKind::Paren(inner) => inner.ast_deref().is_maybe_parenthesised_infer(),
+            _ => false,
+        }
+    }
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
@@ -2269,10 +2279,32 @@ impl TyKind {
 
 /// Syntax used to declare a trait object.
 #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
+#[repr(u8)]
 pub enum TraitObjectSyntax {
-    Dyn,
-    DynStar,
-    None,
+    // SAFETY: When adding new variants make sure to update the `Tag` impl.
+    Dyn = 0,
+    DynStar = 1,
+    None = 2,
+}
+
+/// SAFETY: `TraitObjectSyntax` only has 3 data-less variants which means
+/// it can be represented with a `u2`. We use `repr(u8)` to guarantee the
+/// discriminants of the variants are no greater than `3`.
+unsafe impl Tag for TraitObjectSyntax {
+    const BITS: u32 = 2;
+
+    fn into_usize(self) -> usize {
+        self as u8 as usize
+    }
+
+    unsafe fn from_usize(tag: usize) -> Self {
+        match tag {
+            0 => TraitObjectSyntax::Dyn,
+            1 => TraitObjectSyntax::DynStar,
+            2 => TraitObjectSyntax::None,
+            _ => unreachable!(),
+        }
+    }
 }
 
 #[derive(Clone, Encodable, Decodable, Debug)]
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 9cfdbc47495..f8fb21d5cd0 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -1,3 +1,4 @@
+use intravisit::InferKind;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_hir as hir;
 use rustc_hir::def_id::{LocalDefId, LocalDefIdMap};
@@ -265,14 +266,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
-    fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) {
-        self.insert(const_arg.span(), const_arg.hir_id, Node::ConstArg(const_arg));
-
-        self.with_parent(const_arg.hir_id, |this| {
-            intravisit::walk_const_arg(this, const_arg);
-        });
-    }
-
     fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
         self.insert(expr.span, expr.hir_id, Node::Expr(expr));
 
@@ -302,22 +295,41 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         intravisit::walk_path_segment(self, path_segment);
     }
 
-    fn visit_ty(&mut self, ty: &'hir Ty<'hir>) {
-        self.insert(ty.span, ty.hir_id, Node::Ty(ty));
+    fn visit_ty(&mut self, ty: &'hir Ty<'hir, AmbigArg>) {
+        self.insert(ty.span, ty.hir_id, Node::Ty(ty.as_unambig_ty()));
 
         self.with_parent(ty.hir_id, |this| {
             intravisit::walk_ty(this, ty);
         });
     }
 
-    fn visit_infer(&mut self, inf: &'hir InferArg) {
-        self.insert(inf.span, inf.hir_id, Node::Infer(inf));
+    fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir, AmbigArg>) {
+        self.insert(
+            const_arg.as_unambig_ct().span(),
+            const_arg.hir_id,
+            Node::ConstArg(const_arg.as_unambig_ct()),
+        );
 
-        self.with_parent(inf.hir_id, |this| {
-            intravisit::walk_inf(this, inf);
+        self.with_parent(const_arg.hir_id, |this| {
+            intravisit::walk_ambig_const_arg(this, const_arg);
         });
     }
 
+    fn visit_infer(
+        &mut self,
+        inf_id: HirId,
+        inf_span: Span,
+        kind: InferKind<'hir>,
+    ) -> Self::Result {
+        match kind {
+            InferKind::Ty(ty) => self.insert(inf_span, inf_id, Node::Ty(ty)),
+            InferKind::Const(ct) => self.insert(inf_span, inf_id, Node::ConstArg(ct)),
+            InferKind::Ambig(inf) => self.insert(inf_span, inf_id, Node::Infer(inf)),
+        }
+
+        self.visit_id(inf_id);
+    }
+
     fn visit_trait_ref(&mut self, tr: &'hir TraitRef<'hir>) {
         self.insert(tr.path.span, tr.hir_ref_id, Node::TraitRef(tr));
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0e28590bd66..b9f1a4220b8 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -48,6 +48,7 @@ use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::tagged_ptr::TaggedRef;
 use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
@@ -1083,17 +1084,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         match arg {
             ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)),
             ast::GenericArg::Type(ty) => {
+                // We cannot just match on `TyKind::Infer` as `(_)` is represented as
+                // `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer`
+                if ty.is_maybe_parenthesised_infer() {
+                    return GenericArg::Infer(hir::InferArg {
+                        hir_id: self.lower_node_id(ty.id),
+                        span: self.lower_span(ty.span),
+                    });
+                }
+
                 match &ty.kind {
-                    TyKind::Infer if self.tcx.features().generic_arg_infer() => {
-                        return GenericArg::Infer(hir::InferArg {
-                            hir_id: self.lower_node_id(ty.id),
-                            span: self.lower_span(ty.span),
-                        });
-                    }
                     // We parse const arguments as path types as we cannot distinguish them during
                     // parsing. We try to resolve that ambiguity by attempting resolution in both the
                     // type and value namespaces. If we resolved the path in the value namespace, we
                     // transform it into a generic const argument.
+                    //
+                    // FIXME: Should we be handling `(PATH_TO_CONST)`?
                     TyKind::Path(None, path) => {
                         if let Some(res) = self
                             .resolver
@@ -1110,15 +1116,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                                 let ct =
                                     self.lower_const_path_to_const_arg(path, res, ty.id, ty.span);
-                                return GenericArg::Const(ct);
+                                return GenericArg::Const(ct.try_as_ambig_ct().unwrap());
                             }
                         }
                     }
                     _ => {}
                 }
-                GenericArg::Type(self.lower_ty(ty, itctx))
+                GenericArg::Type(self.lower_ty(ty, itctx).try_as_ambig_ty().unwrap())
+            }
+            ast::GenericArg::Const(ct) => {
+                GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap())
             }
-            ast::GenericArg::Const(ct) => GenericArg::Const(self.lower_anon_const_to_const_arg(ct)),
         }
     }
 
@@ -1158,7 +1166,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let lifetime_bound = this.elided_dyn_bound(t.span);
                 (bounds, lifetime_bound)
             });
-            let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None);
+            let kind = hir::TyKind::TraitObject(
+                bounds,
+                TaggedRef::new(lifetime_bound, TraitObjectSyntax::None),
+            );
             return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() };
         }
 
@@ -1185,7 +1196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
         let kind = match &t.kind {
-            TyKind::Infer => hir::TyKind::Infer,
+            TyKind::Infer => hir::TyKind::Infer(()),
             TyKind::Err(guar) => hir::TyKind::Err(*guar),
             TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
             TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
@@ -1309,7 +1320,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span));
                     (bounds, lifetime_bound)
                 });
-                hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
+                hir::TyKind::TraitObject(bounds, TaggedRef::new(lifetime_bound, *kind))
             }
             TyKind::ImplTrait(def_node_id, bounds) => {
                 let span = t.span;
@@ -2041,7 +2052,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     )
                     .stash(c.value.span, StashKey::UnderscoreForArrayLengths);
                 }
-                let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span));
+                let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ());
                 self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind })
             }
             _ => self.lower_anon_const_to_const_arg(c),
@@ -2365,8 +2376,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         hir_id = self.next_id();
                         hir::TyKind::TraitObject(
                             arena_vec![self; principal],
-                            self.elided_dyn_bound(span),
-                            TraitObjectSyntax::None,
+                            TaggedRef::new(self.elided_dyn_bound(span), TraitObjectSyntax::None),
                         )
                     }
                     _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)),
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 043144a5464..75abff7461b 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -525,7 +525,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
             FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
         };
-        let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
+        let args = smallvec![GenericArg::Type(
+            self.arena.alloc(self.ty_tup(*inputs_span, inputs)).try_as_ambig_ty().unwrap()
+        )];
 
         // If we have a bound like `async Fn() -> T`, make sure that we mark the
         // `Output = T` associated type bound with the right feature gates.
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index f0baa20648c..3c4e4c29197 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -8,7 +8,7 @@ use rustc_hir::QPath::Resolved;
 use rustc_hir::WherePredicateKind::BoundPredicate;
 use rustc_hir::def::Res::Def;
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::VisitorExt;
 use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate};
 use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound};
 use rustc_middle::bug;
@@ -887,7 +887,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 if alias_ty.span.desugaring_kind().is_some() {
                     // Skip `async` desugaring `impl Future`.
                 }
-                if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
+                if let TyKind::TraitObject(_, lt) = alias_ty.kind {
                     if lt.ident.name == kw::Empty {
                         spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
                     } else {
@@ -987,7 +987,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         for found_did in found_dids {
             let mut traits = vec![];
             let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
-            hir_v.visit_ty(self_ty);
+            hir_v.visit_ty_unambig(self_ty);
             debug!("trait spans found: {:?}", traits);
             for span in &traits {
                 let mut multi_span: MultiSpan = vec![*span].into();
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 9349b46ec5b..ccd13badad7 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -432,7 +432,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
             // must highlight the variable.
             // NOTE(eddyb) this is handled in/by the sole caller
             // (`give_name_if_anonymous_region_appears_in_arguments`).
-            hir::TyKind::Infer => None,
+            hir::TyKind::Infer(()) => None,
 
             _ => Some(argument_hir_ty),
         }
@@ -615,7 +615,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
                 }
 
                 (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => {
-                    search_stack.push((ty, hir_ty));
+                    search_stack.push((ty, hir_ty.as_unambig_ty()));
                 }
 
                 (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => {
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 5339feb5d27..5075ed86a6a 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -15,6 +15,7 @@ pub use rustc_ast::{
 };
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::sorted_map::SortedMap;
+use rustc_data_structures::tagged_ptr::TaggedRef;
 use rustc_index::IndexVec;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::def_id::LocalDefId;
@@ -30,7 +31,7 @@ use crate::LangItem;
 use crate::def::{CtorKind, DefKind, Res};
 use crate::def_id::{DefId, LocalDefIdMap};
 pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
-use crate::intravisit::FnKind;
+use crate::intravisit::{FnKind, VisitorExt};
 
 #[derive(Debug, Copy, Clone, HashStable_Generic)]
 pub struct Lifetime {
@@ -263,14 +264,58 @@ impl<'hir> PathSegment<'hir> {
 /// So, `ConstArg` (specifically, [`ConstArgKind`]) distinguishes between const args
 /// that are [just paths](ConstArgKind::Path) (currently just bare const params)
 /// versus const args that are literals or have arbitrary computations (e.g., `{ 1 + 3 }`).
+///
+/// The `Unambig` generic parameter represents whether the position this const is from is
+/// unambiguously a const or ambiguous as to whether it is a type or a const. When in an
+/// ambiguous context the parameter is instantiated with an uninhabited type making the
+/// [`ConstArgKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead.
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
-pub struct ConstArg<'hir> {
+#[repr(C)]
+pub struct ConstArg<'hir, Unambig = ()> {
     #[stable_hasher(ignore)]
     pub hir_id: HirId,
-    pub kind: ConstArgKind<'hir>,
+    pub kind: ConstArgKind<'hir, Unambig>,
+}
+
+impl<'hir> ConstArg<'hir, AmbigArg> {
+    /// Converts a `ConstArg` in an ambiguous position to one in an unambiguous position.
+    ///
+    /// Functions accepting an unambiguous consts may expect the [`ConstArgKind::Infer`] variant
+    /// to be used. Care should be taken to separately handle infer consts when calling this
+    /// function as it cannot be handled by downstream code making use of the returned const.
+    ///
+    /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or
+    /// specifically matching on [`GenericArg::Infer`] when handling generic arguments.
+    ///
+    /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer]
+    pub fn as_unambig_ct(&self) -> &ConstArg<'hir> {
+        // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the
+        // layout is the same across different ZST type arguments.
+        let ptr = self as *const ConstArg<'hir, AmbigArg> as *const ConstArg<'hir, ()>;
+        unsafe { &*ptr }
+    }
 }
 
 impl<'hir> ConstArg<'hir> {
+    /// Converts a `ConstArg` in an unambigous position to one in an ambiguous position. This is
+    /// fallible as the [`ConstArgKind::Infer`] variant is not present in ambiguous positions.
+    ///
+    /// Functions accepting ambiguous consts will not handle the [`ConstArgKind::Infer`] variant, if
+    /// infer consts are relevant to you then care should be taken to handle them separately.
+    pub fn try_as_ambig_ct(&self) -> Option<&ConstArg<'hir, AmbigArg>> {
+        if let ConstArgKind::Infer(_, ()) = self.kind {
+            return None;
+        }
+
+        // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the layout is
+        // the same across different ZST type arguments. We also asserted that the `self` is
+        // not a `ConstArgKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`.
+        let ptr = self as *const ConstArg<'hir> as *const ConstArg<'hir, AmbigArg>;
+        Some(unsafe { &*ptr })
+    }
+}
+
+impl<'hir, Unambig> ConstArg<'hir, Unambig> {
     pub fn anon_const_hir_id(&self) -> Option<HirId> {
         match self.kind {
             ConstArgKind::Anon(ac) => Some(ac.hir_id),
@@ -282,14 +327,15 @@ impl<'hir> ConstArg<'hir> {
         match self.kind {
             ConstArgKind::Path(path) => path.span(),
             ConstArgKind::Anon(anon) => anon.span,
-            ConstArgKind::Infer(span) => span,
+            ConstArgKind::Infer(span, _) => span,
         }
     }
 }
 
 /// See [`ConstArg`].
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
-pub enum ConstArgKind<'hir> {
+#[repr(u8, C)]
+pub enum ConstArgKind<'hir, Unambig = ()> {
     /// **Note:** Currently this is only used for bare const params
     /// (`N` where `fn foo<const N: usize>(...)`),
     /// not paths to any const (`N` where `const N: usize = ...`).
@@ -297,11 +343,9 @@ pub enum ConstArgKind<'hir> {
     /// However, in the future, we'll be using it for all of those.
     Path(QPath<'hir>),
     Anon(&'hir AnonConst),
-    /// **Note:** Not all inferred consts are represented as
-    /// `ConstArgKind::Infer`. In cases where it is ambiguous whether
-    /// a generic arg is a type or a const, inference variables are
-    /// represented as `GenericArg::Infer` instead.
-    Infer(Span),
+    /// This variant is not always used to represent inference consts, sometimes
+    /// [`GenericArg::Infer`] is used instead.
+    Infer(Span, Unambig),
 }
 
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
@@ -313,19 +357,24 @@ pub struct InferArg {
 
 impl InferArg {
     pub fn to_ty(&self) -> Ty<'static> {
-        Ty { kind: TyKind::Infer, span: self.span, hir_id: self.hir_id }
+        Ty { kind: TyKind::Infer(()), span: self.span, hir_id: self.hir_id }
     }
 }
 
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub enum GenericArg<'hir> {
     Lifetime(&'hir Lifetime),
-    Type(&'hir Ty<'hir>),
-    Const(&'hir ConstArg<'hir>),
-    /// **Note:** Inference variables are only represented as
-    /// `GenericArg::Infer` in cases where it is ambiguous whether
-    /// a generic arg is a type or a const. Otherwise, inference variables
-    /// are represented as `TyKind::Infer` or `ConstArgKind::Infer`.
+    Type(&'hir Ty<'hir, AmbigArg>),
+    Const(&'hir ConstArg<'hir, AmbigArg>),
+    /// Inference variables in [`GenericArg`] are always represnted by
+    /// `GenericArg::Infer` instead of the `Infer` variants on [`TyKind`] and
+    /// [`ConstArgKind`] as it is not clear until hir ty lowering whether a
+    /// `_` argument is a type or const argument.
+    ///
+    /// However, some builtin types' generic arguments are represented by [`TyKind`]
+    /// without a [`GenericArg`], instead directly storing a [`Ty`] or [`ConstArg`]. In
+    /// such cases they *are* represented by the `Infer` variants on [`TyKind`] and
+    /// [`ConstArgKind`] as it is not ambiguous whether the argument is a type or const.
     Infer(InferArg),
 }
 
@@ -353,7 +402,7 @@ impl GenericArg<'_> {
             GenericArg::Lifetime(_) => "lifetime",
             GenericArg::Type(_) => "type",
             GenericArg::Const(_) => "constant",
-            GenericArg::Infer(_) => "inferred",
+            GenericArg::Infer(_) => "placeholder",
         }
     }
 
@@ -764,11 +813,8 @@ impl<'hir> Generics<'hir> {
                     && let [.., segment] = trait_ref.path.segments
                     && let Some(ret_ty) = segment.args().paren_sugar_output()
                     && let ret_ty = ret_ty.peel_refs()
-                    && let TyKind::TraitObject(
-                        _,
-                        _,
-                        TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar,
-                    ) = ret_ty.kind
+                    && let TyKind::TraitObject(_, tagged_ptr) = ret_ty.kind
+                    && let TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar = tagged_ptr.tag()
                     && ret_ty.span.can_be_used_for_suggestions()
                 {
                     Some(ret_ty.span)
@@ -2917,15 +2963,84 @@ impl<'hir> AssocItemConstraintKind<'hir> {
     }
 }
 
+/// An uninhabited enum used to make `Infer` variants on [`Ty`] and [`ConstArg`] be
+/// unreachable. Zero-Variant enums are guaranteed to have the same layout as the never
+/// type.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
-pub struct Ty<'hir> {
+pub enum AmbigArg {}
+
+#[derive(Debug, Clone, Copy, HashStable_Generic)]
+#[repr(C)]
+/// Represents a type in the `HIR`.
+///
+/// The `Unambig` generic parameter represents whether the position this type is from is
+/// unambiguously a type or ambiguous as to whether it is a type or a const. When in an
+/// ambiguous context the parameter is instantiated with an uninhabited type making the
+/// [`TyKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead.
+pub struct Ty<'hir, Unambig = ()> {
     #[stable_hasher(ignore)]
     pub hir_id: HirId,
-    pub kind: TyKind<'hir>,
     pub span: Span,
+    pub kind: TyKind<'hir, Unambig>,
+}
+
+impl<'hir> Ty<'hir, AmbigArg> {
+    /// Converts a `Ty` in an ambiguous position to one in an unambiguous position.
+    ///
+    /// Functions accepting an unambiguous types may expect the [`TyKind::Infer`] variant
+    /// to be used. Care should be taken to separately handle infer types when calling this
+    /// function as it cannot be handled by downstream code making use of the returned ty.
+    ///
+    /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or
+    /// specifically matching on [`GenericArg::Infer`] when handling generic arguments.
+    ///
+    /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer]
+    pub fn as_unambig_ty(&self) -> &Ty<'hir> {
+        // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is
+        // the same across different ZST type arguments.
+        let ptr = self as *const Ty<'hir, AmbigArg> as *const Ty<'hir, ()>;
+        unsafe { &*ptr }
+    }
+}
+
+impl<'hir> Ty<'hir> {
+    /// Converts a `Ty` in an unambigous position to one in an ambiguous position. This is
+    /// fallible as the [`TyKind::Infer`] variant is not present in ambiguous positions.
+    ///
+    /// Functions accepting ambiguous types will not handle the [`TyKind::Infer`] variant, if
+    /// infer types are relevant to you then care should be taken to handle them separately.
+    pub fn try_as_ambig_ty(&self) -> Option<&Ty<'hir, AmbigArg>> {
+        if let TyKind::Infer(()) = self.kind {
+            return None;
+        }
+
+        // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is
+        // the same across different ZST type arguments. We also asserted that the `self` is
+        // not a `TyKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`.
+        let ptr = self as *const Ty<'hir> as *const Ty<'hir, AmbigArg>;
+        Some(unsafe { &*ptr })
+    }
+}
+
+impl<'hir> Ty<'hir, AmbigArg> {
+    pub fn peel_refs(&self) -> &Ty<'hir> {
+        let mut final_ty = self.as_unambig_ty();
+        while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind {
+            final_ty = ty;
+        }
+        final_ty
+    }
 }
 
 impl<'hir> Ty<'hir> {
+    pub fn peel_refs(&self) -> &Self {
+        let mut final_ty = self;
+        while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind {
+            final_ty = ty;
+        }
+        final_ty
+    }
+
     /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
     pub fn as_generic_param(&self) -> Option<(DefId, Ident)> {
         let TyKind::Path(QPath::Resolved(None, path)) = self.kind else {
@@ -2942,19 +3057,11 @@ impl<'hir> Ty<'hir> {
         }
     }
 
-    pub fn peel_refs(&self) -> &Self {
-        let mut final_ty = self;
-        while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind {
-            final_ty = ty;
-        }
-        final_ty
-    }
-
     pub fn find_self_aliases(&self) -> Vec<Span> {
         use crate::intravisit::Visitor;
         struct MyVisitor(Vec<Span>);
         impl<'v> Visitor<'v> for MyVisitor {
-            fn visit_ty(&mut self, t: &'v Ty<'v>) {
+            fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) {
                 if matches!(
                     &t.kind,
                     TyKind::Path(QPath::Resolved(_, Path {
@@ -2970,7 +3077,7 @@ impl<'hir> Ty<'hir> {
         }
 
         let mut my_visitor = MyVisitor(vec![]);
-        my_visitor.visit_ty(self);
+        my_visitor.visit_ty_unambig(self);
         my_visitor.0
     }
 
@@ -2979,14 +3086,14 @@ impl<'hir> Ty<'hir> {
     pub fn is_suggestable_infer_ty(&self) -> bool {
         fn are_suggestable_generic_args(generic_args: &[GenericArg<'_>]) -> bool {
             generic_args.iter().any(|arg| match arg {
-                GenericArg::Type(ty) => ty.is_suggestable_infer_ty(),
+                GenericArg::Type(ty) => ty.as_unambig_ty().is_suggestable_infer_ty(),
                 GenericArg::Infer(_) => true,
                 _ => false,
             })
         }
         debug!(?self);
         match &self.kind {
-            TyKind::Infer => true,
+            TyKind::Infer(()) => true,
             TyKind::Slice(ty) => ty.is_suggestable_infer_ty(),
             TyKind::Array(ty, length) => {
                 ty.is_suggestable_infer_ty() || matches!(length.kind, ConstArgKind::Infer(..))
@@ -3200,7 +3307,9 @@ pub enum InferDelegationKind {
 
 /// The various kinds of types recognized by the compiler.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
-pub enum TyKind<'hir> {
+// SAFETY: `repr(u8)` is required so that `TyKind<()>` and `TyKind<!>` are layout compatible
+#[repr(u8, C)]
+pub enum TyKind<'hir, Unambig = ()> {
     /// Actual type should be inherited from `DefId` signature
     InferDelegation(DefId, InferDelegationKind),
     /// A variable length slice (i.e., `[T]`).
@@ -3230,21 +3339,22 @@ pub enum TyKind<'hir> {
     TraitAscription(GenericBounds<'hir>),
     /// A trait object type `Bound1 + Bound2 + Bound3`
     /// where `Bound` is a trait or a lifetime.
-    TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax),
+    ///
+    /// We use pointer tagging to represent a `&'hir Lifetime` and `TraitObjectSyntax` pair
+    /// as otherwise this type being `repr(C)` would result in `TyKind` increasing in size.
+    TraitObject(&'hir [PolyTraitRef<'hir>], TaggedRef<'hir, Lifetime, TraitObjectSyntax>),
     /// Unused for now.
     Typeof(&'hir AnonConst),
-    /// `TyKind::Infer` means the type should be inferred instead of it having been
-    /// specified. This can appear anywhere in a type.
-    ///
-    /// **Note:** Not all inferred types are represented as
-    /// `TyKind::Infer`. In cases where it is ambiguous whether
-    /// a generic arg is a type or a const, inference variables are
-    /// represented as `GenericArg::Infer` instead.
-    Infer,
     /// Placeholder for a type that has failed to be defined.
     Err(rustc_span::ErrorGuaranteed),
     /// Pattern types (`pattern_type!(u32 is 1..)`)
     Pat(&'hir Ty<'hir>, &'hir Pat<'hir>),
+    /// `TyKind::Infer` means the type should be inferred instead of it having been
+    /// specified. This can appear anywhere in a type.
+    ///
+    /// This variant is not always used to represent inference types, sometimes
+    /// [`GenericArg::Infer`] is used instead.
+    Infer(Unambig),
 }
 
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
@@ -4537,3 +4647,6 @@ fn debug_fn(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Deb
     }
     DebugFn(f)
 }
+
+#[cfg(test)]
+mod tests;
diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs
new file mode 100644
index 00000000000..300d4435530
--- /dev/null
+++ b/compiler/rustc_hir/src/hir/tests.rs
@@ -0,0 +1,83 @@
+use rustc_span::def_id::DefIndex;
+
+use super::*;
+
+macro_rules! define_tests {
+    ($($name:ident $kind:ident $variant:ident {$($init:tt)*})*) => {$(
+        #[test]
+        fn $name() {
+            let unambig = $kind::$variant::<'_, ()> { $($init)* };
+            let unambig_to_ambig = unsafe { std::mem::transmute::<_, $kind<'_, AmbigArg>>(unambig) };
+
+            assert!(matches!(&unambig_to_ambig, $kind::$variant { $($init)* }));
+
+            let ambig_to_unambig = unsafe { std::mem::transmute::<_, $kind<'_, ()>>(unambig_to_ambig) };
+
+            assert!(matches!(&ambig_to_unambig, $kind::$variant { $($init)* }));
+        }
+    )*};
+}
+
+define_tests! {
+    cast_never TyKind Never {}
+    cast_tup TyKind Tup { 0: &[Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }] }
+    cast_ptr TyKind Ptr { 0: MutTy { ty: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, mutbl: Mutability::Not }}
+    cast_array TyKind Array {
+        0: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never },
+        1: &ConstArg { hir_id: HirId::INVALID, kind: ConstArgKind::Anon(&AnonConst {
+            hir_id: HirId::INVALID,
+            def_id: LocalDefId { local_def_index: DefIndex::ZERO },
+            body: BodyId { hir_id: HirId::INVALID },
+            span: DUMMY_SP,
+        })}
+    }
+
+    cast_anon ConstArgKind Anon {
+        0: &AnonConst {
+            hir_id: HirId::INVALID,
+            def_id: LocalDefId { local_def_index: DefIndex::ZERO },
+            body: BodyId { hir_id: HirId::INVALID },
+            span: DUMMY_SP,
+        }
+    }
+}
+
+#[test]
+fn trait_object_roundtrips() {
+    trait_object_roundtrips_impl(TraitObjectSyntax::Dyn);
+    trait_object_roundtrips_impl(TraitObjectSyntax::DynStar);
+    trait_object_roundtrips_impl(TraitObjectSyntax::None);
+}
+
+fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) {
+    let unambig = TyKind::TraitObject::<'_, ()>(
+        &[],
+        TaggedRef::new(
+            &const {
+                Lifetime {
+                    hir_id: HirId::INVALID,
+                    ident: Ident::new(sym::name, DUMMY_SP),
+                    res: LifetimeName::Static,
+                }
+            },
+            syntax,
+        ),
+    );
+    let unambig_to_ambig = unsafe { std::mem::transmute::<_, TyKind<'_, AmbigArg>>(unambig) };
+
+    match unambig_to_ambig {
+        TyKind::TraitObject(_, tagged_ref) => {
+            assert!(tagged_ref.tag() == syntax)
+        }
+        _ => panic!("`TyKind::TraitObject` did not roundtrip"),
+    };
+
+    let ambig_to_unambig = unsafe { std::mem::transmute::<_, TyKind<'_, ()>>(unambig_to_ambig) };
+
+    match ambig_to_unambig {
+        TyKind::TraitObject(_, tagged_ref) => {
+            assert!(tagged_ref.tag() == syntax)
+        }
+        _ => panic!("`TyKind::TraitObject` did not roundtrip"),
+    };
+}
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index ef863aca090..b0d80d0f809 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -351,18 +351,48 @@ pub trait Visitor<'v>: Sized {
     fn visit_inline_const(&mut self, c: &'v ConstBlock) -> Self::Result {
         walk_inline_const(self, c)
     }
-    fn visit_const_arg(&mut self, c: &'v ConstArg<'v>) -> Self::Result {
-        walk_const_arg(self, c)
+
+    fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result {
+        walk_generic_arg(self, generic_arg)
+    }
+
+    /// All types are treated as ambiguous types for the purposes of hir visiting in
+    /// order to ensure that visitors can handle infer vars without it being too error-prone.
+    ///
+    /// See the doc comments on [`Ty`] for an explanation of what it means for a type to be
+    /// ambiguous.
+    ///
+    /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars.
+    fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) -> Self::Result {
+        walk_ty(self, t)
     }
+
+    /// All consts are treated as ambiguous consts for the purposes of hir visiting in
+    /// order to ensure that visitors can handle infer vars without it being too error-prone.
+    ///
+    /// See the doc comments on [`ConstArg`] for an explanation of what it means for a const to be
+    /// ambiguous.
+    ///
+    /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars.
+    fn visit_const_arg(&mut self, c: &'v ConstArg<'v, AmbigArg>) -> Self::Result {
+        walk_ambig_const_arg(self, c)
+    }
+
+    #[allow(unused_variables)]
+    fn visit_infer(&mut self, inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result {
+        self.visit_id(inf_id)
+    }
+
+    fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result {
+        walk_lifetime(self, lifetime)
+    }
+
     fn visit_expr(&mut self, ex: &'v Expr<'v>) -> Self::Result {
         walk_expr(self, ex)
     }
     fn visit_expr_field(&mut self, field: &'v ExprField<'v>) -> Self::Result {
         walk_expr_field(self, field)
     }
-    fn visit_ty(&mut self, t: &'v Ty<'v>) -> Self::Result {
-        walk_ty(self, t)
-    }
     fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) {
         // Do nothing. Only a few visitors need to know the details of the pattern type,
         // and they opt into it. All other visitors will just choke on our fake patterns
@@ -444,15 +474,6 @@ pub trait Visitor<'v>: Sized {
     fn visit_label(&mut self, label: &'v Label) -> Self::Result {
         walk_label(self, label)
     }
-    fn visit_infer(&mut self, inf: &'v InferArg) -> Self::Result {
-        walk_inf(self, inf)
-    }
-    fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result {
-        walk_generic_arg(self, generic_arg)
-    }
-    fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result {
-        walk_lifetime(self, lifetime)
-    }
     // The span is that of the surrounding type/pattern/expr/whatever.
     fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, _span: Span) -> Self::Result {
         walk_qpath(self, qpath, id)
@@ -486,6 +507,26 @@ pub trait Visitor<'v>: Sized {
     }
 }
 
+pub trait VisitorExt<'v>: Visitor<'v> {
+    /// Extension trait method to visit types in unambiguous positions, this is not
+    /// directly on the [`Visitor`] trait as this method should never be overridden.
+    ///
+    /// Named `visit_ty_unambig` instead of `visit_unambig_ty` to aid in discovery
+    /// by IDes when `v.visit_ty` is written.
+    fn visit_ty_unambig(&mut self, t: &'v Ty<'v>) -> Self::Result {
+        walk_unambig_ty(self, t)
+    }
+    /// Extension trait method to visit consts in unambiguous positions, this is not
+    /// directly on the [`Visitor`] trait as this method should never be overridden.
+    ///
+    /// Named `visit_const_arg_unambig` instead of `visit_unambig_const_arg` to aid in
+    /// discovery by IDes when `v.visit_const_arg` is written.
+    fn visit_const_arg_unambig(&mut self, c: &'v ConstArg<'v>) -> Self::Result {
+        walk_const_arg(self, c)
+    }
+}
+impl<'v, V: Visitor<'v>> VisitorExt<'v> for V {}
+
 pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) -> V::Result {
     try_visit!(visitor.visit_id(param.hir_id));
     visitor.visit_pat(param.pat)
@@ -503,12 +544,12 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
         }
         ItemKind::Static(ref typ, _, body) => {
             try_visit!(visitor.visit_id(item.hir_id()));
-            try_visit!(visitor.visit_ty(typ));
+            try_visit!(visitor.visit_ty_unambig(typ));
             try_visit!(visitor.visit_nested_body(body));
         }
         ItemKind::Const(ref typ, ref generics, body) => {
             try_visit!(visitor.visit_id(item.hir_id()));
-            try_visit!(visitor.visit_ty(typ));
+            try_visit!(visitor.visit_ty_unambig(typ));
             try_visit!(visitor.visit_generics(generics));
             try_visit!(visitor.visit_nested_body(body));
         }
@@ -539,7 +580,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
         }
         ItemKind::TyAlias(ref ty, ref generics) => {
             try_visit!(visitor.visit_id(item.hir_id()));
-            try_visit!(visitor.visit_ty(ty));
+            try_visit!(visitor.visit_ty_unambig(ty));
             try_visit!(visitor.visit_generics(generics));
         }
         ItemKind::Enum(ref enum_definition, ref generics) => {
@@ -561,7 +602,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
             try_visit!(visitor.visit_id(item.hir_id()));
             try_visit!(visitor.visit_generics(generics));
             visit_opt!(visitor, visit_trait_ref, of_trait);
-            try_visit!(visitor.visit_ty(self_ty));
+            try_visit!(visitor.visit_ty_unambig(self_ty));
             walk_list!(visitor, visit_impl_item_ref, *items);
         }
         ItemKind::Struct(ref struct_definition, ref generics)
@@ -618,7 +659,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(
             walk_list!(visitor, visit_ident, param_names.iter().copied());
         }
         ForeignItemKind::Static(ref typ, _, _) => {
-            try_visit!(visitor.visit_ty(typ));
+            try_visit!(visitor.visit_ty_unambig(typ));
         }
         ForeignItemKind::Type => (),
     }
@@ -632,7 +673,7 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v LetStmt<'v>) -
     try_visit!(visitor.visit_id(local.hir_id));
     try_visit!(visitor.visit_pat(local.pat));
     visit_opt!(visitor, visit_block, local.els);
-    visit_opt!(visitor, visit_ty, local.ty);
+    visit_opt!(visitor, visit_ty_unambig, local.ty);
     V::Result::output()
 }
 
@@ -735,18 +776,6 @@ pub fn walk_inline_const<'v, V: Visitor<'v>>(
     visitor.visit_nested_body(constant.body)
 }
 
-pub fn walk_const_arg<'v, V: Visitor<'v>>(
-    visitor: &mut V,
-    const_arg: &'v ConstArg<'v>,
-) -> V::Result {
-    try_visit!(visitor.visit_id(const_arg.hir_id));
-    match &const_arg.kind {
-        ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()),
-        ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon),
-        ConstArgKind::Infer(..) => V::Result::output(),
-    }
-}
-
 pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) -> V::Result {
     try_visit!(visitor.visit_id(expression.hir_id));
     match expression.kind {
@@ -758,7 +787,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
         }
         ExprKind::Repeat(ref element, ref count) => {
             try_visit!(visitor.visit_expr(element));
-            try_visit!(visitor.visit_const_arg(count));
+            try_visit!(visitor.visit_const_arg_unambig(count));
         }
         ExprKind::Struct(ref qpath, fields, ref optional_base) => {
             try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span));
@@ -789,7 +818,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
         }
         ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => {
             try_visit!(visitor.visit_expr(subexpression));
-            try_visit!(visitor.visit_ty(typ));
+            try_visit!(visitor.visit_ty_unambig(typ));
         }
         ExprKind::DropTemps(ref subexpression) => {
             try_visit!(visitor.visit_expr(subexpression));
@@ -798,7 +827,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
             // match the visit order in walk_local
             try_visit!(visitor.visit_expr(init));
             try_visit!(visitor.visit_pat(pat));
-            visit_opt!(visitor, visit_ty, ty);
+            visit_opt!(visitor, visit_ty_unambig, ty);
         }
         ExprKind::If(ref cond, ref then, ref else_opt) => {
             try_visit!(visitor.visit_expr(cond));
@@ -866,7 +895,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
             try_visit!(visitor.visit_inline_asm(asm, expression.hir_id));
         }
         ExprKind::OffsetOf(ref container, ref fields) => {
-            try_visit!(visitor.visit_ty(container));
+            try_visit!(visitor.visit_ty_unambig(container));
             walk_list!(visitor, visit_ident, fields.iter().copied());
         }
         ExprKind::Yield(ref subexpression, _) => {
@@ -874,7 +903,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
         }
         ExprKind::UnsafeBinderCast(_kind, expr, ty) => {
             try_visit!(visitor.visit_expr(expr));
-            visit_opt!(visitor, visit_ty, ty);
+            visit_opt!(visitor, visit_ty_unambig, ty);
         }
         ExprKind::Lit(_) | ExprKind::Err(_) => {}
     }
@@ -886,20 +915,49 @@ pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField
     try_visit!(visitor.visit_ident(field.ident));
     visitor.visit_expr(field.expr)
 }
+/// We track whether an infer var is from a [`Ty`], [`ConstArg`], or [`GenericArg`] so that
+/// HIR visitors overriding [`Visitor::visit_infer`] can determine what kind of infer is being visited
+pub enum InferKind<'hir> {
+    Ty(&'hir Ty<'hir>),
+    Const(&'hir ConstArg<'hir>),
+    Ambig(&'hir InferArg),
+}
 
-pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result {
+pub fn walk_generic_arg<'v, V: Visitor<'v>>(
+    visitor: &mut V,
+    generic_arg: &'v GenericArg<'v>,
+) -> V::Result {
+    match generic_arg {
+        GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt),
+        GenericArg::Type(ty) => visitor.visit_ty(ty),
+        GenericArg::Const(ct) => visitor.visit_const_arg(ct),
+        GenericArg::Infer(inf) => visitor.visit_infer(inf.hir_id, inf.span, InferKind::Ambig(inf)),
+    }
+}
+
+pub fn walk_unambig_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result {
+    match typ.try_as_ambig_ty() {
+        Some(ambig_ty) => visitor.visit_ty(ambig_ty),
+        None => {
+            try_visit!(visitor.visit_id(typ.hir_id));
+            visitor.visit_infer(typ.hir_id, typ.span, InferKind::Ty(typ))
+        }
+    }
+}
+
+pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -> V::Result {
     try_visit!(visitor.visit_id(typ.hir_id));
 
     match typ.kind {
-        TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty(ty)),
-        TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty(mutable_type.ty)),
+        TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)),
+        TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty_unambig(mutable_type.ty)),
         TyKind::Ref(ref lifetime, ref mutable_type) => {
             try_visit!(visitor.visit_lifetime(lifetime));
-            try_visit!(visitor.visit_ty(mutable_type.ty));
+            try_visit!(visitor.visit_ty_unambig(mutable_type.ty));
         }
         TyKind::Never => {}
         TyKind::Tup(tuple_element_types) => {
-            walk_list!(visitor, visit_ty, tuple_element_types);
+            walk_list!(visitor, visit_ty_unambig, tuple_element_types);
         }
         TyKind::BareFn(ref function_declaration) => {
             walk_list!(visitor, visit_generic_param, function_declaration.generic_params);
@@ -907,7 +965,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
         }
         TyKind::UnsafeBinder(ref unsafe_binder) => {
             walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params);
-            try_visit!(visitor.visit_ty(unsafe_binder.inner_ty));
+            try_visit!(visitor.visit_ty_unambig(unsafe_binder.inner_ty));
         }
         TyKind::Path(ref qpath) => {
             try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span));
@@ -919,25 +977,49 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul
             walk_list!(visitor, visit_param_bound, bounds);
         }
         TyKind::Array(ref ty, ref length) => {
-            try_visit!(visitor.visit_ty(ty));
-            try_visit!(visitor.visit_const_arg(length));
+            try_visit!(visitor.visit_ty_unambig(ty));
+            try_visit!(visitor.visit_const_arg_unambig(length));
         }
-        TyKind::TraitObject(bounds, ref lifetime, _syntax) => {
+        TyKind::TraitObject(bounds, ref lifetime) => {
             for bound in bounds {
                 try_visit!(visitor.visit_poly_trait_ref(bound));
             }
             try_visit!(visitor.visit_lifetime(lifetime));
         }
         TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)),
-        TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {}
+        TyKind::InferDelegation(..) | TyKind::Err(_) => {}
         TyKind::Pat(ty, pat) => {
-            try_visit!(visitor.visit_ty(ty));
+            try_visit!(visitor.visit_ty_unambig(ty));
             try_visit!(visitor.visit_pattern_type_pattern(pat));
         }
     }
     V::Result::output()
 }
 
+pub fn walk_const_arg<'v, V: Visitor<'v>>(
+    visitor: &mut V,
+    const_arg: &'v ConstArg<'v>,
+) -> V::Result {
+    match const_arg.try_as_ambig_ct() {
+        Some(ambig_ct) => visitor.visit_const_arg(ambig_ct),
+        None => {
+            try_visit!(visitor.visit_id(const_arg.hir_id));
+            visitor.visit_infer(const_arg.hir_id, const_arg.span(), InferKind::Const(const_arg))
+        }
+    }
+}
+
+pub fn walk_ambig_const_arg<'v, V: Visitor<'v>>(
+    visitor: &mut V,
+    const_arg: &'v ConstArg<'v, AmbigArg>,
+) -> V::Result {
+    try_visit!(visitor.visit_id(const_arg.hir_id));
+    match &const_arg.kind {
+        ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()),
+        ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon),
+    }
+}
+
 pub fn walk_generic_param<'v, V: Visitor<'v>>(
     visitor: &mut V,
     param: &'v GenericParam<'v>,
@@ -949,9 +1031,11 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(
     }
     match param.kind {
         GenericParamKind::Lifetime { .. } => {}
-        GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default),
+        GenericParamKind::Type { ref default, .. } => {
+            visit_opt!(visitor, visit_ty_unambig, default)
+        }
         GenericParamKind::Const { ref ty, ref default, synthetic: _ } => {
-            try_visit!(visitor.visit_ty(ty));
+            try_visit!(visitor.visit_ty_unambig(ty));
             if let Some(ref default) = default {
                 try_visit!(visitor.visit_const_param_default(param.hir_id, default));
             }
@@ -964,7 +1048,7 @@ pub fn walk_const_param_default<'v, V: Visitor<'v>>(
     visitor: &mut V,
     ct: &'v ConstArg<'v>,
 ) -> V::Result {
-    visitor.visit_const_arg(ct)
+    visitor.visit_const_arg_unambig(ct)
 }
 
 pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) -> V::Result {
@@ -986,7 +1070,7 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>(
             bound_generic_params,
             origin: _,
         }) => {
-            try_visit!(visitor.visit_ty(bounded_ty));
+            try_visit!(visitor.visit_ty_unambig(bounded_ty));
             walk_list!(visitor, visit_param_bound, bounds);
             walk_list!(visitor, visit_generic_param, bound_generic_params);
         }
@@ -999,8 +1083,8 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>(
             walk_list!(visitor, visit_param_bound, bounds);
         }
         WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => {
-            try_visit!(visitor.visit_ty(lhs_ty));
-            try_visit!(visitor.visit_ty(rhs_ty));
+            try_visit!(visitor.visit_ty_unambig(lhs_ty));
+            try_visit!(visitor.visit_ty_unambig(rhs_ty));
         }
     }
     V::Result::output()
@@ -1010,13 +1094,13 @@ pub fn walk_fn_decl<'v, V: Visitor<'v>>(
     visitor: &mut V,
     function_declaration: &'v FnDecl<'v>,
 ) -> V::Result {
-    walk_list!(visitor, visit_ty, function_declaration.inputs);
+    walk_list!(visitor, visit_ty_unambig, function_declaration.inputs);
     visitor.visit_fn_ret_ty(&function_declaration.output)
 }
 
 pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FnRetTy<'v>) -> V::Result {
     if let FnRetTy::Return(output_ty) = *ret_ty {
-        try_visit!(visitor.visit_ty(output_ty));
+        try_visit!(visitor.visit_ty_unambig(output_ty));
     }
     V::Result::output()
 }
@@ -1069,7 +1153,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(
     try_visit!(visitor.visit_id(hir_id));
     match *kind {
         TraitItemKind::Const(ref ty, default) => {
-            try_visit!(visitor.visit_ty(ty));
+            try_visit!(visitor.visit_ty_unambig(ty));
             visit_opt!(visitor, visit_nested_body, default);
         }
         TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => {
@@ -1087,7 +1171,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(
         }
         TraitItemKind::Type(bounds, ref default) => {
             walk_list!(visitor, visit_param_bound, bounds);
-            visit_opt!(visitor, visit_ty, default);
+            visit_opt!(visitor, visit_ty_unambig, default);
         }
     }
     V::Result::output()
@@ -1125,7 +1209,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(
     try_visit!(visitor.visit_id(impl_item.hir_id()));
     match *kind {
         ImplItemKind::Const(ref ty, body) => {
-            try_visit!(visitor.visit_ty(ty));
+            try_visit!(visitor.visit_ty_unambig(ty));
             visitor.visit_nested_body(body)
         }
         ImplItemKind::Fn(ref sig, body_id) => visitor.visit_fn(
@@ -1135,7 +1219,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>(
             impl_item.span,
             impl_item.owner_id.def_id,
         ),
-        ImplItemKind::Type(ref ty) => visitor.visit_ty(ty),
+        ImplItemKind::Type(ref ty) => visitor.visit_ty_unambig(ty),
     }
 }
 
@@ -1223,7 +1307,7 @@ pub fn walk_field_def<'v, V: Visitor<'v>>(
     try_visit!(visitor.visit_id(*hir_id));
     try_visit!(visitor.visit_ident(*ident));
     visit_opt!(visitor, visit_anon_const, default);
-    visitor.visit_ty(*ty)
+    visitor.visit_ty_unambig(*ty)
 }
 
 pub fn walk_enum_def<'v, V: Visitor<'v>>(
@@ -1252,18 +1336,6 @@ pub fn walk_inf<'v, V: Visitor<'v>>(visitor: &mut V, inf: &'v InferArg) -> V::Re
     visitor.visit_id(inf.hir_id)
 }
 
-pub fn walk_generic_arg<'v, V: Visitor<'v>>(
-    visitor: &mut V,
-    generic_arg: &'v GenericArg<'v>,
-) -> V::Result {
-    match generic_arg {
-        GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt),
-        GenericArg::Type(ty) => visitor.visit_ty(ty),
-        GenericArg::Const(ct) => visitor.visit_const_arg(ct),
-        GenericArg::Infer(inf) => visitor.visit_infer(inf),
-    }
-}
-
 pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) -> V::Result {
     try_visit!(visitor.visit_id(lifetime.hir_id));
     visitor.visit_ident(lifetime.ident)
@@ -1276,11 +1348,11 @@ pub fn walk_qpath<'v, V: Visitor<'v>>(
 ) -> V::Result {
     match *qpath {
         QPath::Resolved(ref maybe_qself, ref path) => {
-            visit_opt!(visitor, visit_ty, maybe_qself);
+            visit_opt!(visitor, visit_ty_unambig, maybe_qself);
             visitor.visit_path(path, id)
         }
         QPath::TypeRelative(ref qself, ref segment) => {
-            try_visit!(visitor.visit_ty(qself));
+            try_visit!(visitor.visit_ty_unambig(qself));
             visitor.visit_path_segment(segment)
         }
         QPath::LangItem(..) => V::Result::output(),
@@ -1320,8 +1392,8 @@ pub fn walk_assoc_item_constraint<'v, V: Visitor<'v>>(
     try_visit!(visitor.visit_generic_args(constraint.gen_args));
     match constraint.kind {
         AssocItemConstraintKind::Equality { ref term } => match term {
-            Term::Ty(ref ty) => try_visit!(visitor.visit_ty(ty)),
-            Term::Const(ref c) => try_visit!(visitor.visit_const_arg(c)),
+            Term::Ty(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)),
+            Term::Const(ref c) => try_visit!(visitor.visit_const_arg_unambig(c)),
         },
         AssocItemConstraintKind::Bound { bounds } => {
             walk_list!(visitor, visit_param_bound, bounds)
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index 381062b1429..8ec2054bf53 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -6,6 +6,7 @@
 #![allow(internal_features)]
 #![feature(associated_type_defaults)]
 #![feature(closure_track_caller)]
+#![feature(exhaustive_patterns)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 4a957d5da24..dbc5c634c45 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -6,9 +6,9 @@ use hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
-use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{GenericParamKind, ImplItemKind, intravisit};
+use rustc_hir::intravisit::VisitorExt;
+use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit};
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::util;
@@ -1610,7 +1610,7 @@ fn compare_synthetic_generics<'tcx>(
                     struct Visitor(hir::def_id::LocalDefId);
                     impl<'v> intravisit::Visitor<'v> for Visitor {
                         type Result = ControlFlow<Span>;
-                        fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) -> Self::Result {
+                        fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) -> Self::Result {
                             if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
                                 && let Res::Def(DefKind::TyParam, def_id) = path.res
                                 && def_id == self.0.to_def_id()
@@ -1622,9 +1622,9 @@ fn compare_synthetic_generics<'tcx>(
                         }
                     }
 
-                    let span = input_tys.iter().find_map(|ty| {
-                        intravisit::Visitor::visit_ty(&mut Visitor(impl_def_id), ty).break_value()
-                    })?;
+                    let span = input_tys
+                        .iter()
+                        .find_map(|ty| Visitor(impl_def_id).visit_ty_unambig(ty).break_value())?;
 
                     let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
                     let bounds = bounds.first()?.span().to(bounds.last()?.span());
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index dd6adb17c5e..cd19993f937 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -6,10 +6,10 @@ use rustc_abi::ExternAbi;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
-use rustc_hir::ItemKind;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
 use rustc_hir::lang_items::LangItem;
+use rustc_hir::{AmbigArg, ItemKind};
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
 use rustc_macros::LintDiagnostic;
@@ -2196,7 +2196,7 @@ impl<'tcx> Visitor<'tcx> for CollectUsageSpans<'_> {
         // Skip the generics. We only care about fields, not where clause/param bounds.
     }
 
-    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result {
+    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
         if let hir::TyKind::Path(hir::QPath::Resolved(None, qpath)) = t.kind {
             if let Res::Def(DefKind::TyParam, def_id) = qpath.res
                 && def_id == self.param_def_id
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 86c6532c97d..c517d25fcbf 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -28,7 +28,7 @@ use rustc_errors::{
 };
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::{self, Visitor, walk_generics};
+use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt, walk_generics};
 use rustc_hir::{self as hir, GenericParamKind, HirId, Node};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::ObligationCause;
@@ -139,29 +139,12 @@ pub(crate) struct HirPlaceholderCollector {
 }
 
 impl<'v> Visitor<'v> for HirPlaceholderCollector {
-    fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
-        if let hir::TyKind::Infer = t.kind {
-            self.spans.push(t.span);
-        }
-        intravisit::walk_ty(self, t)
-    }
-    fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) {
-        match generic_arg {
-            hir::GenericArg::Infer(inf) => {
-                self.spans.push(inf.span);
-                self.may_contain_const_infer = true;
-                intravisit::walk_inf(self, inf);
-            }
-            hir::GenericArg::Type(t) => self.visit_ty(t),
-            _ => {}
-        }
-    }
-    fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) {
-        if let hir::ConstArgKind::Infer(span) = const_arg.kind {
+    fn visit_infer(&mut self, _inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result {
+        self.spans.push(inf_span);
+
+        if let InferKind::Const(_) | InferKind::Ambig(_) = kind {
             self.may_contain_const_infer = true;
-            self.spans.push(span);
         }
-        intravisit::walk_const_arg(self, const_arg)
     }
 }
 
@@ -583,7 +566,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
             .iter()
             .enumerate()
             .map(|(i, a)| {
-                if let hir::TyKind::Infer = a.kind {
+                if let hir::TyKind::Infer(()) = a.kind {
                     if let Some(suggested_ty) =
                         self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
                     {
@@ -593,21 +576,21 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
                 }
 
                 // Only visit the type looking for `_` if we didn't fix the type above
-                visitor.visit_ty(a);
+                visitor.visit_ty_unambig(a);
                 self.lowerer().lower_arg_ty(a, None)
             })
             .collect();
 
         let output_ty = match decl.output {
             hir::FnRetTy::Return(output) => {
-                if let hir::TyKind::Infer = output.kind
+                if let hir::TyKind::Infer(()) = output.kind
                     && let Some(suggested_ty) =
                         self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
                 {
                     infer_replacements.push((output.span, suggested_ty.to_string()));
                     Ty::new_error_with_message(tcx, output.span, suggested_ty.to_string())
                 } else {
-                    visitor.visit_ty(output);
+                    visitor.visit_ty_unambig(output);
                     self.lower_ty(output)
                 }
             }
@@ -1453,7 +1436,7 @@ fn recover_infer_ret_ty<'tcx>(
     });
 
     let mut visitor = HirPlaceholderCollector::default();
-    visitor.visit_ty(infer_ret_ty);
+    visitor.visit_ty_unambig(infer_ret_ty);
 
     let mut diag = bad_placeholder(icx.lowerer(), visitor.spans, "return type");
     let ret_ty = fn_sig.output();
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 075615de522..2ac8acbd3a4 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -3,9 +3,10 @@ use std::ops::ControlFlow;
 
 use hir::intravisit::{self, Visitor};
 use hir::{GenericParamKind, HirId, Node};
-use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::VisitorExt;
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint;
 use rustc_span::{Span, Symbol, kw};
@@ -461,7 +462,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
 
     impl<'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'tcx> {
         type Result = ControlFlow<Span>;
-        fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) -> ControlFlow<Span> {
+        fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) -> ControlFlow<Span> {
             match ty.kind {
                 hir::TyKind::BareFn(..) => {
                     self.outer_index.shift_in(1);
@@ -539,7 +540,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
         if let GenericParamKind::Const { ty, default: _, synthetic: _ } = p.kind {
             let prev = self.in_param_ty;
             self.in_param_ty = true;
-            let res = self.visit_ty(ty);
+            let res = self.visit_ty_unambig(ty);
             self.in_param_ty = prev;
             res
         } else {
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index e8706d1adfb..582a2c7a0fc 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -14,11 +14,11 @@ use rustc_ast::visit::walk_list;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt};
 use rustc_hir::{
-    GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, LifetimeName, Node,
+    self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap,
+    LifetimeName, Node,
 };
 use rustc_macros::extension;
 use rustc_middle::hir::nested_filter;
@@ -489,15 +489,17 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     struct FindInferInClosureWithBinder;
                     impl<'v> Visitor<'v> for FindInferInClosureWithBinder {
                         type Result = ControlFlow<Span>;
-                        fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result {
-                            if matches!(t.kind, hir::TyKind::Infer) {
-                                ControlFlow::Break(t.span)
-                            } else {
-                                intravisit::walk_ty(self, t)
-                            }
+
+                        fn visit_infer(
+                            &mut self,
+                            _inf_id: HirId,
+                            inf_span: Span,
+                            _kind: InferKind<'v>,
+                        ) -> Self::Result {
+                            ControlFlow::Break(inf_span)
                         }
                     }
-                    FindInferInClosureWithBinder.visit_ty(ty).break_value()
+                    FindInferInClosureWithBinder.visit_ty_unambig(ty).break_value()
                 }
 
                 let infer_in_rt_sp = match fn_decl.output {
@@ -749,7 +751,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
     }
 
     #[instrument(level = "debug", skip(self))]
-    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
         match ty.kind {
             hir::TyKind::BareFn(c) => {
                 let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = c
@@ -810,7 +812,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     intravisit::walk_ty(this, ty);
                 });
             }
-            hir::TyKind::TraitObject(bounds, lifetime, _) => {
+            hir::TyKind::TraitObject(bounds, lifetime) => {
+                let lifetime = lifetime.pointer();
+
                 debug!(?bounds, ?lifetime, "TraitObject");
                 let scope = Scope::TraitRefBoundary { s: self.scope };
                 self.with(scope, |this| {
@@ -827,7 +831,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                         // use the object lifetime defaulting
                         // rules. So e.g., `Box<dyn Debug>` becomes
                         // `Box<dyn Debug + 'static>`.
-                        self.resolve_object_lifetime_default(lifetime)
+                        self.resolve_object_lifetime_default(&*lifetime)
                     }
                     LifetimeName::Infer => {
                         // If the user writes `'_`, we use the *ordinary* elision
@@ -838,7 +842,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     }
                     LifetimeName::Param(..) | LifetimeName::Static => {
                         // If the user wrote an explicit name, use that.
-                        self.visit_lifetime(lifetime);
+                        self.visit_lifetime(&*lifetime);
                     }
                     LifetimeName::Error => {}
                 }
@@ -849,7 +853,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     lifetime: self.map.defs.get(&lifetime_ref.hir_id.local_id).cloned(),
                     s: self.scope,
                 };
-                self.with(scope, |this| this.visit_ty(mt.ty));
+                self.with(scope, |this| this.visit_ty_unambig(mt.ty));
             }
             hir::TyKind::TraitAscription(bounds) => {
                 let scope = Scope::TraitRefBoundary { s: self.scope };
@@ -891,7 +895,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                         this.visit_param_bound(bound);
                     }
                     if let Some(ty) = ty {
-                        this.visit_ty(ty);
+                        this.visit_ty_unambig(ty);
                     }
                 })
             }
@@ -910,7 +914,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
             }),
             Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| {
                 this.visit_generics(impl_item.generics);
-                this.visit_ty(ty);
+                this.visit_ty_unambig(ty);
             }),
             Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| {
                 intravisit::walk_impl_item(this, impl_item)
@@ -1019,7 +1023,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                 };
                 self.with(scope, |this| {
                     walk_list!(this, visit_generic_param, bound_generic_params);
-                    this.visit_ty(bounded_ty);
+                    this.visit_ty_unambig(bounded_ty);
                     walk_list!(this, visit_param_bound, bounds);
                 })
             }
@@ -1034,8 +1038,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
             &hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate {
                 lhs_ty, rhs_ty, ..
             }) => {
-                self.visit_ty(lhs_ty);
-                self.visit_ty(rhs_ty);
+                self.visit_ty_unambig(lhs_ty);
+                self.visit_ty_unambig(rhs_ty);
             }
         }
     }
@@ -1068,13 +1072,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
             GenericParamKind::Lifetime { .. } => {}
             GenericParamKind::Type { default, .. } => {
                 if let Some(ty) = default {
-                    self.visit_ty(ty);
+                    self.visit_ty_unambig(ty);
                 }
             }
             GenericParamKind::Const { ty, default, .. } => {
-                self.visit_ty(ty);
+                self.visit_ty_unambig(ty);
                 if let Some(default) = default {
-                    self.visit_const_arg(default);
+                    self.visit_const_arg_unambig(default);
                 }
             }
         }
@@ -1983,15 +1987,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
             },
             |this| {
                 for input in inputs {
-                    this.visit_ty(input);
+                    this.visit_ty_unambig(input);
                 }
                 if !in_closure && let Some(output) = output {
-                    this.visit_ty(output);
+                    this.visit_ty_unambig(output);
                 }
             },
         );
         if in_closure && let Some(output) = output {
-            self.visit_ty(output);
+            self.visit_ty_unambig(output);
         }
     }
 
@@ -2309,7 +2313,7 @@ fn is_late_bound_map(
 
     let mut constrained_by_input = ConstrainedCollector { regions: Default::default(), tcx };
     for arg_ty in sig.decl.inputs {
-        constrained_by_input.visit_ty(arg_ty);
+        constrained_by_input.visit_ty_unambig(arg_ty);
     }
 
     let mut appears_in_output =
@@ -2417,7 +2421,7 @@ fn is_late_bound_map(
     }
 
     impl<'v> Visitor<'v> for ConstrainedCollector<'_> {
-        fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+        fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
             match ty.kind {
                 hir::TyKind::Path(
                     hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..),
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 51e15767b8c..348d4d708b5 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -1,10 +1,9 @@
 use core::ops::ControlFlow;
 
 use rustc_errors::{Applicability, StashKey, Suggestions};
-use rustc_hir as hir;
-use rustc_hir::HirId;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::VisitorExt;
+use rustc_hir::{self as hir, AmbigArg, HirId};
 use rustc_middle::query::plumbing::CyclePlaceholder;
 use rustc_middle::ty::fold::fold_regions;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
@@ -451,7 +450,7 @@ fn infer_placeholder_type<'tcx>(
             let mut visitor = HirPlaceholderCollector::default();
             let node = tcx.hir_node_by_def_id(def_id);
             if let Some(ty) = node.ty() {
-                visitor.visit_ty(ty);
+                visitor.visit_ty_unambig(ty);
             }
             // If we have just one span, let's try to steal a const `_` feature error.
             let try_steal_span = if !tcx.features().generic_arg_infer() && visitor.spans.len() == 1
@@ -525,7 +524,7 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) ->
     struct HasTait;
     impl<'tcx> Visitor<'tcx> for HasTait {
         type Result = ControlFlow<()>;
-        fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result {
+        fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
             if let hir::TyKind::OpaqueDef(..) = t.kind {
                 ControlFlow::Break(())
             } else {
@@ -533,5 +532,5 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) ->
             }
         }
     }
-    HasTait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break()
+    HasTait.visit_ty_unambig(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break()
 }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index df00948dd21..fe3dcb35639 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -1,10 +1,9 @@
 use rustc_ast::ast::ParamKindOrd;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, struct_span_code_err};
-use rustc_hir as hir;
-use rustc_hir::GenericArg;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, GenericArg};
 use rustc_middle::ty::{
     self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty,
 };
@@ -41,17 +40,6 @@ fn generic_arg_mismatch_err(
         param.kind.descr(),
     );
 
-    if let GenericParamDefKind::Const { .. } = param.kind {
-        if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) {
-            err.help("const arguments cannot yet be inferred with `_`");
-            tcx.disabled_nightly_features(
-                &mut err,
-                param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)),
-                [(String::new(), sym::generic_arg_infer)],
-            );
-        }
-    }
-
     let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diag<'_>| {
         let suggestions = vec![
             (arg.span().shrink_to_lo(), String::from("{ ")),
@@ -270,6 +258,21 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
                             GenericParamDefKind::Const { .. },
                             _,
                         ) => {
+                            if let GenericParamDefKind::Const { .. } = param.kind
+                                && let GenericArg::Infer(inf) = arg
+                                && !tcx.features().generic_arg_infer()
+                            {
+                                rustc_session::parse::feature_err(
+                                    tcx.sess,
+                                    sym::generic_arg_infer,
+                                    inf.span,
+                                    "const arguments cannot yet be inferred with `_`",
+                                )
+                                .emit();
+                            }
+
+                            // We lower to an infer even when the feature gate is not enabled
+                            // as it is useful for diagnostics to be able to see a `ConstKind::Infer`
                             args.push(ctx.provided_kind(&args, param, arg));
                             args_iter.next();
                             params.next();
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 a1f2b8c7594..539c5f6a20a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -23,9 +23,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     ) -> Option<ErrorGuaranteed> {
         let tcx = self.tcx();
 
-        let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
+        let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) =
             self_ty.kind
-        else {
+            && let TraitObjectSyntax::None = tagged_ptr.tag()
+        {
+            poly_trait_ref
+        } else {
             return None;
         };
 
@@ -294,7 +297,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let (dyn_str, paren_dyn_str) =
                     if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") };
 
-                let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind {
+                let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind {
                     // There are more than one trait bound, we need surrounding parentheses.
                     vec![
                         (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()),
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 cb90fff782f..61d5869c19f 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -517,14 +517,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         self.lowerer.lower_lifetime(lt, RegionInferReason::Param(param)).into()
                     }
                     (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => {
-                        handle_ty_args(has_default, ty)
+                        // We handle the other parts of `Ty` in the match arm below
+                        handle_ty_args(has_default, ty.as_unambig_ty())
                     }
                     (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => {
                         handle_ty_args(has_default, &inf.to_ty())
                     }
-                    (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
-                        self.lowerer.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into()
-                    }
+                    (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self
+                        .lowerer
+                        // Ambig portions of `ConstArg` are handled in the match arm below
+                        .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id))
+                        .into(),
                     (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
                         self.lowerer.ct_infer(Some(param), inf.span).into()
                     }
@@ -2115,7 +2118,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
             ),
             hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
-            hir::ConstArgKind::Infer(span) => self.ct_infer(None, span),
+            hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
         }
     }
 
@@ -2311,7 +2314,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     tcx.late_bound_vars(hir_ty.hir_id),
                 ),
             ),
-            hir::TyKind::TraitObject(bounds, lifetime, repr) => {
+            hir::TyKind::TraitObject(bounds, tagged_ptr) => {
+                let lifetime = tagged_ptr.pointer();
+                let repr = tagged_ptr.tag();
+
                 if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) {
                     // Don't continue with type analysis if the `dyn` keyword is missing
                     // It generates confusing errors, especially if the user meant to use another
@@ -2420,7 +2426,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length)
             }
             hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(),
-            hir::TyKind::Infer => {
+            hir::TyKind::Infer(()) => {
                 // Infer also appears as the type of arguments or return
                 // values in an ExprKind::Closure, or as
                 // the type of local variables. Both of these cases are
@@ -2553,7 +2559,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
     pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> {
         match ty.kind {
-            hir::TyKind::Infer if let Some(expected_ty) = expected_ty => {
+            hir::TyKind::Infer(()) if let Some(expected_ty) = expected_ty => {
                 self.record_ty(ty.hir_id, expected_ty, ty.span);
                 expected_ty
             }
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index c0fb94d2cb2..f5abcd23440 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -1,6 +1,5 @@
-use rustc_hir as hir;
-use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{ForeignItem, ForeignItemKind};
+use rustc_hir::intravisit::{self, Visitor, VisitorExt};
+use rustc_hir::{self as hir, AmbigArg, ForeignItem, ForeignItemKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::{ObligationCause, WellFormedLoc};
 use rustc_middle::bug;
@@ -68,11 +67,13 @@ fn diagnostic_hir_wf_check<'tcx>(
     }
 
     impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
-        fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+        fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
             let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis());
             let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
 
-            let tcx_ty = self.icx.lower_ty(ty);
+            // We don't handle infer vars but we wouldn't handle them anyway as we're creating a
+            // fresh `InferCtxt` in this function.
+            let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty());
             // This visitor can walk into binders, resulting in the `tcx_ty` to
             // potentially reference escaping bound variables. We simply erase
             // those here.
@@ -149,7 +150,11 @@ fn diagnostic_hir_wf_check<'tcx>(
                         .iter()
                         .flat_map(|seg| seg.args().args)
                         .filter_map(|arg| {
-                            if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None }
+                            if let hir::GenericArg::Type(ty) = arg {
+                                Some(ty.as_unambig_ty())
+                            } else {
+                                None
+                            }
                         })
                         .chain([impl_.self_ty])
                         .collect(),
@@ -196,7 +201,7 @@ fn diagnostic_hir_wf_check<'tcx>(
         }
     };
     for ty in tys {
-        visitor.visit_ty(ty);
+        visitor.visit_ty_unambig(ty);
     }
     visitor.cause
 }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 3ff6acd79fc..afc0f627f69 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -402,7 +402,8 @@ impl<'a> State<'a> {
                 self.print_bounds("impl", bounds);
             }
             hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false),
-            hir::TyKind::TraitObject(bounds, lifetime, syntax) => {
+            hir::TyKind::TraitObject(bounds, lifetime) => {
+                let syntax = lifetime.tag();
                 match syntax {
                     ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"),
                     ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"),
@@ -421,7 +422,7 @@ impl<'a> State<'a> {
                 if !lifetime.is_elided() {
                     self.nbsp();
                     self.word_space("+");
-                    self.print_lifetime(lifetime);
+                    self.print_lifetime(lifetime.pointer());
                 }
             }
             hir::TyKind::Array(ty, ref length) => {
@@ -441,7 +442,7 @@ impl<'a> State<'a> {
                 self.word("/*ERROR*/");
                 self.pclose();
             }
-            hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => {
+            hir::TyKind::Infer(()) | hir::TyKind::InferDelegation(..) => {
                 self.word("_");
             }
             hir::TyKind::Pat(ty, pat) => {
@@ -1799,8 +1800,8 @@ impl<'a> State<'a> {
                         match generic_arg {
                             GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt),
                             GenericArg::Lifetime(_) => {}
-                            GenericArg::Type(ty) => s.print_type(ty),
-                            GenericArg::Const(ct) => s.print_const_arg(ct),
+                            GenericArg::Type(ty) => s.print_type(ty.as_unambig_ty()),
+                            GenericArg::Const(ct) => s.print_const_arg(ct.as_unambig_ct()),
                             GenericArg::Infer(_inf) => s.word("_"),
                         }
                     });
@@ -2150,7 +2151,7 @@ impl<'a> State<'a> {
             s.ann.nested(s, Nested::BodyParamPat(body_id, i));
             i += 1;
 
-            if let hir::TyKind::Infer = ty.kind {
+            if let hir::TyKind::Infer(()) = ty.kind {
                 // Print nothing.
             } else {
                 s.word(":");
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index c128485d93e..1f3f03b9929 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -10,7 +10,7 @@ use rustc_hir as hir;
 use rustc_hir::HirId;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::{InferKind, Visitor};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
@@ -641,16 +641,21 @@ impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> {
 impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> {
     type Result = ControlFlow<errors::SuggestAnnotation>;
 
-    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result {
+    fn visit_infer(
+        &mut self,
+        inf_id: HirId,
+        inf_span: Span,
+        _kind: InferKind<'tcx>,
+    ) -> Self::Result {
         // Try to replace `_` with `()`.
-        if let hir::TyKind::Infer = hir_ty.kind
-            && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(hir_ty.hir_id)
+        if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id)
             && let Some(vid) = self.fcx.root_vid(ty)
             && self.reachable_vids.contains(&vid)
         {
-            return ControlFlow::Break(errors::SuggestAnnotation::Unit(hir_ty.span));
+            return ControlFlow::Break(errors::SuggestAnnotation::Unit(inf_span));
         }
-        hir::intravisit::walk_ty(self, hir_ty)
+
+        ControlFlow::Continue(())
     }
 
     fn visit_qpath(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index e26c09e3601..8e647ad3c6a 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::VisitorExt;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{self as hir, ExprKind, GenericArg, HirId, Node, QPath, intravisit};
+use rustc_hir::{self as hir, AmbigArg, ExprKind, GenericArg, HirId, Node, QPath, intravisit};
 use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend;
 use rustc_hir_analysis::hir_ty_lowering::generics::{
     check_generic_arg_count_for_call, lower_generic_args,
@@ -470,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> {
-            fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+            fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
                 if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id)
                 {
                     self.clauses.extend(clauses.iter().cloned());
@@ -480,7 +480,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let mut clauses = CollectClauses { clauses: vec![], fcx: self };
-        clauses.visit_ty(hir_ty);
+        clauses.visit_ty_unambig(hir_ty);
         self.tcx.mk_clauses(&clauses.clauses)
     }
 
@@ -1272,14 +1272,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .lower_lifetime(lt, RegionInferReason::Param(param))
                         .into(),
                     (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
-                        self.fcx.lower_ty(ty).raw.into()
-                    }
-                    (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
-                        self.fcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into()
+                        // We handle the ambig portions of `Ty` in match arm below
+                        self.fcx.lower_ty(ty.as_unambig_ty()).raw.into()
                     }
                     (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
-                        self.fcx.ty_infer(Some(param), inf.span).into()
+                        self.fcx.lower_ty(&inf.to_ty()).raw.into()
                     }
+                    (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self
+                        .fcx
+                        // Ambiguous parts of `ConstArg` are handled in the match arms below
+                        .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id))
+                        .into(),
                     (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
                         self.fcx.ct_infer(Some(param), inf.span).into()
                     }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 9cd9ca040ce..c9e55695e5d 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -251,7 +251,8 @@ fn typeck_with_inspect<'tcx>(
 fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Option<Ty<'tcx>> {
     let tcx = fcx.tcx;
     let def_id = fcx.body_id;
-    let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = node.ty() {
+    let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer(()), span, .. }) = node.ty()
+    {
         if let Some(item) = tcx.opt_associated_item(def_id.into())
             && let ty::AssocKind::Const = item.kind
             && let ty::AssocItemContainer::Impl = item.container
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index f549ced9dc3..880ee83c80a 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -425,14 +425,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                         .lower_lifetime(lt, RegionInferReason::Param(param))
                         .into(),
                     (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
-                        self.cfcx.lower_ty(ty).raw.into()
-                    }
-                    (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
-                        self.cfcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into()
+                        // We handle the ambig portions of `Ty` in the match arms below
+                        self.cfcx.lower_ty(ty.as_unambig_ty()).raw.into()
                     }
                     (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
-                        self.cfcx.ty_infer(Some(param), inf.span).into()
+                        self.cfcx.lower_ty(&inf.to_ty()).raw.into()
                     }
+                    (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self
+                        .cfcx
+                        // We handle the ambig portions of `ConstArg` in the match arms below
+                        .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id))
+                        .into(),
                     (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
                         self.cfcx.ct_infer(Some(param), inf.span).into()
                     }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 683cacdff7d..1bf5b19d68d 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -6,9 +6,8 @@ use std::mem;
 
 use rustc_data_structures::unord::ExtendUnord;
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
-use rustc_hir::HirId;
-use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::intravisit::{self, InferKind, Visitor};
+use rustc_hir::{self as hir, AmbigArg, HirId};
 use rustc_middle::span_bug;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
@@ -354,7 +353,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
         self.write_ty_to_typeck_results(l.hir_id, var_ty);
     }
 
-    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
+    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
         intravisit::walk_ty(self, hir_ty);
         // If there are type checking errors, Type privacy pass will stop,
         // so we may not get the type from hid_id, see #104513
@@ -364,12 +363,20 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
         }
     }
 
-    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
-        intravisit::walk_inf(self, inf);
-        // Ignore cases where the inference is a const.
-        if let Some(ty) = self.fcx.node_ty_opt(inf.hir_id) {
-            let ty = self.resolve(ty, &inf.span);
-            self.write_ty_to_typeck_results(inf.hir_id, ty);
+    fn visit_infer(
+        &mut self,
+        inf_id: HirId,
+        inf_span: Span,
+        _kind: InferKind<'cx>,
+    ) -> Self::Result {
+        self.visit_id(inf_id);
+
+        // We don't currently write inference results of const infer vars to
+        // the typeck results as there is not yet any part of the compiler that
+        // needs this information.
+        if let Some(ty) = self.fcx.node_ty_opt(inf_id) {
+            let ty = self.resolve(ty, &inf_span);
+            self.write_ty_to_typeck_results(inf_id, ty);
         }
     }
 }
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index b31a4c74787..166ff60f7e1 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -5,8 +5,8 @@ use rustc_ast as ast;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
-    BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind,
-    Path, PathSegment, QPath, Ty, TyKind,
+    AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat,
+    PatKind, Path, PathSegment, QPath, Ty, TyKind,
 };
 use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -159,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
         }
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx, AmbigArg>) {
         match &ty.kind {
             TyKind::Path(QPath::Resolved(_, path)) => {
                 if lint_ty_kind_usage(cx, &path.res) {
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index a4d50c73104..c6d7b839e19 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -8,9 +8,8 @@ use std::cell::Cell;
 
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::join;
-use rustc_hir as hir;
 use rustc_hir::def_id::{LocalDefId, LocalModDefId};
-use rustc_hir::{HirId, intravisit as hir_visit};
+use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::Session;
@@ -214,15 +213,11 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
         })
     }
 
-    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
+    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) {
         lint_callback!(self, check_ty, t);
         hir_visit::walk_ty(self, t);
     }
 
-    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
-        hir_visit::walk_inf(self, inf);
-    }
-
     fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: HirId) {
         if !self.context.only_module {
             self.process_mod(m, n);
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 2f610802ff5..84a6272198f 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -9,6 +9,7 @@ use rustc_errors::{
 };
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::VisitorExt;
 use rustc_hir::{self as hir, MissingLifetimeKind};
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::inhabitedness::InhabitedPredicate;
@@ -293,7 +294,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> {
         // avoid doing throwaway work in case the lint ends up getting suppressed.
         let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() };
         if let Some(ty) = self.ty {
-            hir::intravisit::Visitor::visit_ty(&mut collector, ty);
+            collector.visit_ty_unambig(ty);
         }
 
         let affect_object_lifetime_defaults = self
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index a1cc3a85109..1bf19047ade 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -1,6 +1,6 @@
 use rustc_errors::MultiSpan;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::intravisit::{self, Visitor, VisitorExt};
 use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::{declare_lint, impl_lint_pass};
@@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 // 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref
                 // of the `impl` definition
                 let mut collector = PathCollector { paths: Vec::new() };
-                collector.visit_ty(&impl_.self_ty);
+                collector.visit_ty_unambig(&impl_.self_ty);
                 if let Some(of_trait) = &impl_.of_trait {
                     collector.visit_trait_ref(of_trait);
                 }
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 53a411e2b87..d347a8c1bc7 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -1,4 +1,4 @@
-use rustc_hir as hir;
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::fold::BottomUpFolder;
@@ -67,7 +67,7 @@ declare_lint! {
 declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
 
 impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
         let hir::TyKind::OpaqueDef(opaque) = &ty.kind else {
             return;
         };
diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs
index 3f264859d48..244cd358e9c 100644
--- a/compiler/rustc_lint/src/pass_by_value.rs
+++ b/compiler/rustc_lint/src/pass_by_value.rs
@@ -1,6 +1,5 @@
-use rustc_hir as hir;
 use rustc_hir::def::Res;
-use rustc_hir::{GenericArg, PathSegment, QPath, TyKind};
+use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -22,7 +21,7 @@ declare_tool_lint! {
 declare_lint_pass!(PassByValue => [PASS_BY_VALUE]);
 
 impl<'tcx> LateLintPass<'tcx> for PassByValue {
-    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
         match &ty.kind {
             TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
                 if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index 3a323298bee..8cc8f911d3a 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -25,7 +25,7 @@ macro_rules! late_lint_methods {
             fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>);
             fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>);
             fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>);
-            fn check_ty(a: &'tcx rustc_hir::Ty<'tcx>);
+            fn check_ty(a: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>);
             fn check_generic_param(a: &'tcx rustc_hir::GenericParam<'tcx>);
             fn check_generics(a: &'tcx rustc_hir::Generics<'tcx>);
             fn check_poly_trait_ref(a: &'tcx rustc_hir::PolyTraitRef<'tcx>);
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index a9797c3b32a..e0937e43c9a 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -1,4 +1,4 @@
-use rustc_hir::{self as hir, LangItem};
+use rustc_hir::{self as hir, AmbigArg, LangItem};
 use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::sym;
 
@@ -110,8 +110,10 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
         }
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
-        let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
+    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
+        let hir::TyKind::TraitObject(bounds, _lifetime_and_syntax_pointer) = &ty.kind else {
+            return;
+        };
         for bound in &bounds[..] {
             let def_id = bound.trait_ref.trait_def_id();
             if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) {
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 0757e6840c6..0060f33888e 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -4,7 +4,8 @@ use std::ops::ControlFlow;
 use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, WrappingRange};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::DiagMessage;
-use rustc_hir::{Expr, ExprKind, LangItem};
+use rustc_hir::intravisit::VisitorExt;
+use rustc_hir::{AmbigArg, Expr, ExprKind, LangItem};
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
 use rustc_middle::ty::{
@@ -1526,7 +1527,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         }
 
         impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> {
-            fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
+            fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
                 debug!(?ty);
                 if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
                     && !self.visitor.is_internal_abi(*abi)
@@ -1554,7 +1555,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() };
         ty.visit_with(&mut visitor);
-        hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);
+        visitor.visit_ty_unambig(hir_ty);
 
         iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
     }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 2d76f6ec7d6..aeb734ba3f6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -33,7 +33,7 @@ use rustc_errors::{
 use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::definitions::Definitions;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::VisitorExt;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate};
 use rustc_index::IndexVec;
@@ -2028,7 +2028,7 @@ impl<'tcx> TyCtxt<'tcx> {
         };
 
         let mut v = TraitObjectVisitor(vec![], self.hir());
-        v.visit_ty(hir_output);
+        v.visit_ty_unambig(hir_output);
         v.0
     }
 
@@ -2050,7 +2050,7 @@ impl<'tcx> TyCtxt<'tcx> {
             && let Some(alias_ty) = self.hir_node_by_def_id(local_id).alias_ty() // it is type alias
             && let Some(alias_generics) = self.hir_node_by_def_id(local_id).generics()
         {
-            v.visit_ty(alias_ty);
+            v.visit_ty_unambig(alias_ty);
             if !v.0.is_empty() {
                 return Some((
                     v.0,
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index e4187d2760c..b4b66a8133a 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -9,7 +9,7 @@ use rustc_errors::{
 };
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind};
+use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind};
 use rustc_span::{BytePos, Span};
 use rustc_type_ir::TyKind::*;
 
@@ -570,18 +570,18 @@ pub fn suggest_constraining_type_params<'a>(
 pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>);
 
 impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> {
-    fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+    fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
         match ty.kind {
-            hir::TyKind::TraitObject(
-                _,
-                hir::Lifetime {
+            hir::TyKind::TraitObject(_, tagged_ptr)
+                if let hir::Lifetime {
                     res:
                         hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static,
                     ..
-                },
-                _,
-            )
-            | hir::TyKind::OpaqueDef(..) => self.0.push(ty),
+                } = tagged_ptr.pointer() =>
+            {
+                self.0.push(ty.as_unambig_ty())
+            }
+            hir::TyKind::OpaqueDef(..) => self.0.push(ty.as_unambig_ty()),
             _ => {}
         }
         hir::intravisit::walk_ty(self, ty);
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index 9120a248d95..867f8f63969 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -374,7 +374,13 @@ fn find_item_ty_spans(
                         if let hir::GenericArg::Type(ty) = arg
                             && params_in_repr.contains(i as u32)
                         {
-                            find_item_ty_spans(tcx, ty, needle, spans, seen_representable);
+                            find_item_ty_spans(
+                                tcx,
+                                ty.as_unambig_ty(),
+                                needle,
+                                spans,
+                                seen_representable,
+                            );
                         }
                     }
                 }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index dbb87443eed..576ca24bf99 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -2808,7 +2808,7 @@ fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool {
             && let Some(&[hir::GenericArg::Type(ty)]) =
                 path.segments.last().map(|last| last.args().args)
         {
-            doc_fake_variadic_is_allowed_self_ty(ty)
+            doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty())
         } else {
             false
         })
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 34deb854e0f..e5b63b9b4a6 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -460,7 +460,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                             }
 
                             // mark self_ty live
-                            intravisit::walk_ty(self, impl_ref.self_ty);
+                            intravisit::walk_unambig_ty(self, impl_ref.self_ty);
                             if let Some(&impl_item_id) =
                                 self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id)
                             {
diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs
index 6617cf2f723..8b10543f6fd 100644
--- a/compiler/rustc_passes/src/input_stats.rs
+++ b/compiler/rustc_passes/src/input_stats.rs
@@ -5,8 +5,7 @@
 use rustc_ast::visit::BoundKind;
 use rustc_ast::{self as ast, NodeId, visit as ast_visit};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir as hir;
-use rustc_hir::{HirId, intravisit as hir_visit};
+use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::util::common::to_readable_str;
@@ -363,7 +362,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
         hir_visit::walk_expr_field(self, f)
     }
 
-    fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
+    fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) {
         record_variants!((self, t, t.kind, Some(t.hir_id), hir, Ty, TyKind), [
             InferDelegation,
             Slice,
@@ -476,7 +475,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
             hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
             hir::GenericArg::Type(ty) => self.visit_ty(ty),
             hir::GenericArg::Const(ct) => self.visit_const_arg(ct),
-            hir::GenericArg::Infer(inf) => self.visit_infer(inf),
+            hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id),
         }
     }
 
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 60734122e63..a52f080038d 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -11,12 +11,11 @@ use rustc_attr_parsing::{
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
 use rustc_feature::{ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature};
-use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
 use rustc_hir::hir_id::CRATE_HIR_ID;
-use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
+use rustc_hir::intravisit::{self, Visitor, VisitorExt};
+use rustc_hir::{self as hir, AmbigArg, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures};
 use rustc_middle::middle::privacy::EffectiveVisibilities;
@@ -802,7 +801,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     )) = stab
                     {
                         let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
-                        c.visit_ty(self_ty);
+                        c.visit_ty_unambig(self_ty);
                         c.visit_trait_ref(t);
 
                         // do not lint when the trait isn't resolved, since resolution error should
@@ -1028,7 +1027,7 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
         intravisit::walk_trait_ref(self, t)
     }
 
-    fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
+    fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) {
         if let TyKind::Never = t.kind {
             self.fully_stable = false;
         }
@@ -1042,12 +1041,12 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
 
     fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
         for ty in fd.inputs {
-            self.visit_ty(ty)
+            self.visit_ty_unambig(ty)
         }
         if let hir::FnRetTy::Return(output_ty) = fd.output {
             match output_ty.kind {
                 TyKind::Never => {} // `-> !` is stable
-                _ => self.visit_ty(output_ty),
+                _ => self.visit_ty_unambig(output_ty),
             }
         }
     }
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index cb7b0815a49..d19df08519d 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -27,8 +27,8 @@ use rustc_data_structures::intern::Interned;
 use rustc_errors::MultiSpan;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId};
-use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind};
+use rustc_hir::intravisit::{self, InferKind, Visitor};
+use rustc_hir::{AmbigArg, AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind};
 use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
@@ -1179,7 +1179,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
         self.maybe_typeck_results = old_maybe_typeck_results;
     }
 
-    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
+    fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
         self.span = hir_ty.span;
         if self
             .visit(
@@ -1195,12 +1195,17 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
         intravisit::walk_ty(self, hir_ty);
     }
 
-    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
-        self.span = inf.span;
+    fn visit_infer(
+        &mut self,
+        inf_id: rustc_hir::HirId,
+        inf_span: Span,
+        _kind: InferKind<'tcx>,
+    ) -> Self::Result {
+        self.span = inf_span;
         if let Some(ty) = self
             .maybe_typeck_results
-            .unwrap_or_else(|| span_bug!(inf.span, "`hir::InferArg` outside of a body"))
-            .node_type_opt(inf.hir_id)
+            .unwrap_or_else(|| span_bug!(inf_span, "Inference variable outside of a body"))
+            .node_type_opt(inf_id)
         {
             if self.visit(ty).is_break() {
                 return;
@@ -1208,7 +1213,8 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
         } else {
             // FIXME: check types of const infers here.
         }
-        intravisit::walk_inf(self, inf);
+
+        self.visit_id(inf_id)
     }
 
     // Check types of expressions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
index 2a487a48a8e..b9f3abc2534 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
@@ -1,8 +1,8 @@
 use core::ops::ControlFlow;
 
-use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::intravisit::{self, Visitor, VisitorExt};
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_bound_vars as rbv;
@@ -48,7 +48,7 @@ fn find_component_for_bound_region<'tcx>(
     region_def_id: DefId,
 ) -> Option<&'tcx hir::Ty<'tcx>> {
     FindNestedTypeVisitor { tcx, region_def_id, current_index: ty::INNERMOST }
-        .visit_ty(arg)
+        .visit_ty_unambig(arg)
         .break_value()
 }
 
@@ -74,7 +74,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
         self.tcx.hir()
     }
 
-    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
         match arg.kind {
             hir::TyKind::BareFn(_) => {
                 self.current_index.shift_in(1);
@@ -101,7 +101,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                     Some(rbv::ResolvedArg::EarlyBound(id)) => {
                         debug!("EarlyBound id={:?}", id);
                         if id.to_def_id() == self.region_def_id {
-                            return ControlFlow::Break(arg);
+                            return ControlFlow::Break(arg.as_unambig_ty());
                         }
                     }
 
@@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                         if debruijn_index == self.current_index
                             && id.to_def_id() == self.region_def_id
                         {
-                            return ControlFlow::Break(arg);
+                            return ControlFlow::Break(arg.as_unambig_ty());
                         }
                     }
 
@@ -147,7 +147,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                 )
                 .is_break()
                 {
-                    ControlFlow::Break(arg)
+                    ControlFlow::Break(arg.as_unambig_ty())
                 } else {
                     ControlFlow::Continue(())
                 };
@@ -210,7 +210,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
         ControlFlow::Continue(())
     }
 
-    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
         // ignore nested types
         //
         // If you have a type like `Foo<'a, &Ty>` we
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs
index 54d8a9e25ca..886581bc35f 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs
@@ -4,7 +4,7 @@
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::VisitorExt;
 use rustc_middle::bug;
 use rustc_middle::ty::TypeVisitor;
 use tracing::debug;
@@ -87,7 +87,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             for matching_def_id in v.0 {
                 let mut hir_v =
                     super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id);
-                hir_v.visit_ty(impl_self_ty);
+                hir_v.visit_ty_unambig(impl_self_ty);
             }
 
             if traits.is_empty() {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
index 503090b5797..dfbef39e9e1 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -3,9 +3,9 @@
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{Visitor, walk_ty};
+use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
 use rustc_hir::{
-    self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
+    self as hir, AmbigArg, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
     LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind,
 };
 use rustc_middle::ty::{
@@ -153,7 +153,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                     let mut add_label = true;
                     if let hir::FnRetTy::Return(ty) = fn_decl.output {
                         let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
-                        v.visit_ty(ty);
+                        v.visit_ty_unambig(ty);
                         if !v.0.is_empty() {
                             span = v.0.clone().into();
                             spans = v.0;
@@ -374,7 +374,7 @@ pub fn suggest_new_region_bound(
                     }
                 }
             }
-            TyKind::TraitObject(_, lt, _) => {
+            TyKind::TraitObject(_, lt) => {
                 if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
                     err.span_suggestion_verbose(
                         fn_return.span.shrink_to_hi(),
@@ -500,7 +500,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                             // In that case, only the first one will get suggestions.
                             let mut traits = vec![];
                             let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
-                            hir_v.visit_ty(self_ty);
+                            hir_v.visit_ty_unambig(self_ty);
                             !traits.is_empty()
                         })
                     {
@@ -560,7 +560,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         for found_did in found_dids {
             let mut traits = vec![];
             let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
-            hir_v.visit_ty(self_ty);
+            hir_v.visit_ty_unambig(self_ty);
             for &span in &traits {
                 let subdiag = DynTraitConstraintSuggestion { span, ident };
                 subdiag.add_to_diag(err);
@@ -591,12 +591,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor {
 pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
 
 impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
-    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
-        if let TyKind::TraitObject(
-            poly_trait_refs,
-            Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
-            _,
-        ) = t.kind
+    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) {
+        if let TyKind::TraitObject(poly_trait_refs, lifetime_ptr) = t.kind
+            && let Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. } =
+                lifetime_ptr.pointer()
         {
             for ptr in poly_trait_refs {
                 if Some(self.1) == ptr.trait_ref.trait_def_id() {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
index 95dd1b28a39..bbcd28c0835 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
@@ -1,10 +1,10 @@
 //! Error Reporting for `impl` items that do not match the obligations from their `trait`.
 
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
 use rustc_hir::def::{Namespace, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::{Visitor, walk_ty};
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::ExpectedFound;
@@ -137,11 +137,13 @@ impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
         self.tcx.hir()
     }
 
-    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) {
         match arg.kind {
             hir::TyKind::Ref(_, ref mut_ty) => {
                 // We don't want to suggest looking into borrowing `&T` or `&Self`.
-                hir::intravisit::walk_ty(self, mut_ty.ty);
+                if let Some(ambig_ty) = mut_ty.ty.try_as_ambig_ty() {
+                    walk_ty(self, ambig_ty);
+                }
                 return;
             }
             hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
index 21124cf20e5..af7e56961b7 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
@@ -655,7 +655,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     && let ty::Ref(found_region, _, _) = found.kind()
                     && expected_region.is_bound()
                     && !found_region.is_bound()
-                    && let hir::TyKind::Infer = arg_hir.kind
+                    && let hir::TyKind::Infer(()) = arg_hir.kind
                 {
                     // If the expected region is late bound, the found region is not, and users are asking compiler
                     // to infer the type, we can suggest adding `: &_`.
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index bcd3b0109b7..961719f263c 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -580,8 +580,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             self.tcx.hir_node_by_def_id(obligation.cause.body_id)
                             && let hir::ItemKind::Impl(impl_) = item.kind
                             && let None = impl_.of_trait
-                            && let hir::TyKind::TraitObject(_, _, syntax) = impl_.self_ty.kind
-                            && let TraitObjectSyntax::None = syntax
+                            && let hir::TyKind::TraitObject(_, tagged_ptr) = impl_.self_ty.kind
+                            && let TraitObjectSyntax::None = tagged_ptr.tag()
                             && impl_.self_ty.span.edition().at_least_rust_2021()
                         {
                             // Silence the dyn-compatibility error in favor of the missing dyn on
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index f2bcc51e687..8111983c539 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::{self as hir, LangItem};
+use rustc_hir::{self as hir, AmbigArg, LangItem};
 use rustc_infer::traits::{
     DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode,
     PredicateObligation, SelectionError,
@@ -87,9 +87,9 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> {
         }
     }
 
-    fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+    fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
         if self.span == ty.span {
-            self.ty_result = Some(ty);
+            self.ty_result = Some(ty.as_unambig_ty());
         } else {
             hir::intravisit::walk_ty(self, ty);
         }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 4669d286665..471105773e2 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -14,14 +14,13 @@ use rustc_errors::{
     Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, 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::intravisit::Visitor;
+use rustc_hir::intravisit::{Visitor, VisitorExt};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{
-    CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, expr_needs_parens,
-    is_range_literal,
+    self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node,
+    expr_needs_parens, is_range_literal,
 };
 use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk};
 use rustc_middle::hir::map;
@@ -179,7 +178,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
         let mut ty_spans = vec![];
         for input in fn_sig.decl.inputs {
             ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id }
-                .visit_ty(input);
+                .visit_ty_unambig(input);
         }
         // The type param `T: Trait` we will suggest to introduce.
         let type_param = format!("{type_param_name}: {bound_str}");
@@ -3074,7 +3073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 }
                 if let Some(ty) = ty {
                     match ty.kind {
-                        hir::TyKind::TraitObject(traits, _, _) => {
+                        hir::TyKind::TraitObject(traits, _) => {
                             let (span, kw) = match traits {
                                 [first, ..] if first.span.lo() == ty.span.lo() => {
                                     // Missing `dyn` in front of trait object.
@@ -5065,7 +5064,7 @@ pub struct SelfVisitor<'v> {
 }
 
 impl<'v> Visitor<'v> for SelfVisitor<'v> {
-    fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+    fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
         if let hir::TyKind::Path(path) = ty.kind
             && let hir::QPath::TypeRelative(inner_ty, segment) = path
             && (Some(segment.ident.name) == self.name || self.name.is_none())
@@ -5073,7 +5072,7 @@ impl<'v> Visitor<'v> for SelfVisitor<'v> {
             && let hir::QPath::Resolved(None, inner_path) = inner_path
             && let Res::SelfTyAlias { .. } = inner_path.res
         {
-            self.paths.push(ty);
+            self.paths.push(ty.as_unambig_ty());
         }
         hir::intravisit::walk_ty(self, ty);
     }
@@ -5187,7 +5186,7 @@ struct ReplaceImplTraitVisitor<'a> {
 }
 
 impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> {
-    fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) {
+    fn visit_ty(&mut self, t: &'hir hir::Ty<'hir, AmbigArg>) {
         if let hir::TyKind::Path(hir::QPath::Resolved(
             None,
             hir::Path { res: Res::Def(_, segment_did), .. },
@@ -5480,7 +5479,7 @@ impl<'v> Visitor<'v> for FindTypeParam {
         // Skip where-clauses, to avoid suggesting indirection for type parameters found there.
     }
 
-    fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
+    fn visit_ty(&mut self, ty: &hir::Ty<'_, AmbigArg>) {
         // We collect the spans of all uses of the "bare" type param, like in `field: T` or
         // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
         // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 0bf91ad35c1..2dfa72972ba 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -6,11 +6,10 @@ use rustc_errors::{
     Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic,
     EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic,
 };
-use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit::{Visitor, walk_ty};
-use rustc_hir::{FnRetTy, GenericParamKind, Node};
+use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
+use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
 use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt};
@@ -579,7 +578,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
             }
 
             impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
-                fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+                fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
                     let make_suggestion = |ident: Ident| {
                         if ident.name == kw::Empty && ident.span.is_empty() {
                             format!("{}, ", self.suggestion_param_name)
@@ -642,16 +641,16 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
             if let Some(fn_decl) = node.fn_decl()
                 && let hir::FnRetTy::Return(ty) = fn_decl.output
             {
-                visitor.visit_ty(ty);
+                visitor.visit_ty_unambig(ty);
             }
             if visitor.suggestions.is_empty() {
                 // Do not suggest constraining the `&self` param, but rather the return type.
                 // If that is wrong (because it is not sufficient), a follow up error will tell the
                 // user to fix it. This way we lower the chances of *over* constraining, but still
                 // get the cake of "correctly" contrained in two steps.
-                visitor.visit_ty(self.ty_sup);
+                visitor.visit_ty_unambig(self.ty_sup);
             }
-            visitor.visit_ty(self.ty_sub);
+            visitor.visit_ty_unambig(self.ty_sub);
             if visitor.suggestions.is_empty() {
                 return false;
             }
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index 8f9f66db1bd..f71b924b177 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -1,8 +1,8 @@
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
 use rustc_middle::{bug, span_bug};
@@ -187,7 +187,7 @@ fn associated_types_for_impl_traits_in_associated_fn(
             }
 
             impl<'tcx> Visitor<'tcx> for RPITVisitor {
-                fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+                fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
                     if let hir::TyKind::OpaqueDef(opaq) = ty.kind
                         && self.rpits.insert(opaq.def_id)
                     {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index fbd934bf718..7853e311a04 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1747,9 +1747,9 @@ fn maybe_expand_private_type_alias<'tcx>(
     };
     let hir::ItemKind::TyAlias(ty, generics) = alias else { return None };
 
-    let provided_params = &path.segments.last().expect("segments were empty");
+    let final_seg = &path.segments.last().expect("segments were empty");
     let mut args = DefIdMap::default();
-    let generic_args = provided_params.args();
+    let generic_args = final_seg.args();
 
     let mut indices: hir::GenericParamCount = Default::default();
     for param in generics.params.iter() {
@@ -1781,7 +1781,7 @@ fn maybe_expand_private_type_alias<'tcx>(
                 let type_ = generic_args.args.iter().find_map(|arg| match arg {
                     hir::GenericArg::Type(ty) => {
                         if indices.types == j {
-                            return Some(*ty);
+                            return Some(ty.as_unambig_ty());
                         }
                         j += 1;
                         None
@@ -1843,10 +1843,13 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
             ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect())
         }
         TyKind::Path(_) => clean_qpath(ty, cx),
-        TyKind::TraitObject(bounds, lifetime, _) => {
+        TyKind::TraitObject(bounds, lifetime) => {
             let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect();
-            let lifetime =
-                if !lifetime.is_elided() { Some(clean_lifetime(lifetime, cx)) } else { None };
+            let lifetime = if !lifetime.is_elided() {
+                Some(clean_lifetime(lifetime.pointer(), cx))
+            } else {
+                None
+            };
             DynTrait(bounds, lifetime)
         }
         TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))),
@@ -1854,7 +1857,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
             UnsafeBinder(Box::new(clean_unsafe_binder_ty(unsafe_binder_ty, cx)))
         }
         // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
-        TyKind::Infer
+        TyKind::Infer(())
         | TyKind::Err(_)
         | TyKind::Typeof(..)
         | TyKind::InferDelegation(..)
@@ -2533,8 +2536,10 @@ fn clean_generic_args<'tcx>(
                     GenericArg::Lifetime(clean_lifetime(lt, cx))
                 }
                 hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
-                hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty, cx)),
-                hir::GenericArg::Const(ct) => GenericArg::Const(Box::new(clean_const(ct, cx))),
+                hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty.as_unambig_ty(), cx)),
+                hir::GenericArg::Const(ct) => {
+                    GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct(), cx)))
+                }
                 hir::GenericArg::Infer(_inf) => GenericArg::Infer,
             })
             .collect::<Vec<_>>()
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index d46b0dee36c..e4628e4f837 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -598,8 +598,13 @@ impl<'tcx> Visitor<'tcx> for RustdocVisitor<'_, 'tcx> {
         // Unneeded.
     }
 
-    fn visit_infer(&mut self, _: &hir::InferArg) {
-        // Unneeded.
+    fn visit_infer(
+        &mut self,
+        _inf_id: hir::HirId,
+        _inf_span: Span,
+        _kind: hir::intravisit::InferKind<'tcx>,
+    ) -> Self::Result {
+        // Unneeded
     }
 
     fn visit_lifetime(&mut self, _: &hir::Lifetime) {
diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs
index a1ca23e65ff..79fd6ffe46c 100644
--- a/src/tools/clippy/clippy_lints/src/box_default.rs
+++ b/src/tools/clippy/clippy_lints/src/box_default.rs
@@ -4,12 +4,12 @@ use clippy_utils::ty::expr_sig;
 use clippy_utils::{is_default_equivalent, path_def_id};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::intravisit::{Visitor, walk_ty};
-use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind};
+use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
+use rustc_hir::{AmbigArg, Block, Expr, ExprKind, HirId, LetStmt, Node, QPath, Ty, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -92,8 +92,13 @@ fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>)
 struct InferVisitor(bool);
 
 impl Visitor<'_> for InferVisitor {
-    fn visit_ty(&mut self, t: &Ty<'_>) {
-        self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..));
+    fn visit_infer(&mut self, inf_id: HirId, _inf_span: Span, _kind: InferKind<'_>) -> Self::Result {
+        self.0 = true;
+        self.visit_id(inf_id);
+    }
+
+    fn visit_ty(&mut self, t: &Ty<'_, AmbigArg>) {
+        self.0 |= matches!(t.kind, TyKind::OpaqueDef(..) | TyKind::TraitObject(..));
         if !self.0 {
             walk_ty(self, t);
         }
@@ -104,7 +109,7 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match cx.tcx.parent_hir_node(expr.hir_id) {
         Node::LetStmt(LetStmt { ty: Some(ty), .. }) => {
             let mut v = InferVisitor::default();
-            v.visit_ty(ty);
+            v.visit_ty_unambig(ty);
             !v.0
         },
         Node::Expr(Expr {
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs b/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs
index 536126fd02b..3ab6693756f 100644
--- a/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/as_pointer_underscore.rs
@@ -4,7 +4,7 @@ use rustc_middle::ty::Ty;
 
 pub fn check<'tcx>(cx: &LateContext<'tcx>, ty_into: Ty<'_>, cast_to_hir: &'tcx rustc_hir::Ty<'tcx>) {
     if let rustc_hir::TyKind::Ptr(rustc_hir::MutTy { ty, .. }) = cast_to_hir.kind
-        && matches!(ty.kind, rustc_hir::TyKind::Infer)
+        && matches!(ty.kind, rustc_hir::TyKind::Infer(()))
     {
         clippy_utils::diagnostics::span_lint_and_sugg(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs b/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
index 56e894c6261..3ac486dd63f 100644
--- a/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
@@ -7,7 +7,7 @@ use rustc_middle::ty;
 use super::AS_UNDERSCORE;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
-    if matches!(ty.kind, TyKind::Infer) {
+    if matches!(ty.kind, TyKind::Infer(())) {
         span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
             let ty_resolved = cx.typeck_results().expr_ty(expr);
             if let ty::Error(_) = ty_resolved.kind() {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index 4ad39d9160d..c326a0d935c 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -38,7 +38,7 @@ pub(super) fn check(
                 return;
             };
             match cast_to_hir.kind {
-                TyKind::Infer => {
+                TyKind::Infer(()) => {
                     diag.span_suggestion_verbose(
                         expr.span,
                         "use `Into::into` instead",
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index 205357afd84..e4c0db5d9ef 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -24,7 +24,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
             && let Some(generic_args) = method_path.args
             && let [GenericArg::Type(cast_to)] = generic_args.args
             // There probably is no obvious reason to do this, just to be consistent with `as` cases.
-            && !is_hir_ty_cfg_dependant(cx, cast_to)
+            && !is_hir_ty_cfg_dependant(cx, cast_to.as_unambig_ty())
         {
             let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
             lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
index a138ade54aa..bdc389d39dd 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -43,9 +43,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
     {
         let mut app = Applicability::MachineApplicable;
         let turbofish = match &cast_to_hir_ty.kind {
-            TyKind::Infer => String::new(),
+            TyKind::Infer(()) => String::new(),
             TyKind::Ptr(mut_ty) => {
-                if matches!(mut_ty.ty.kind, TyKind::Infer) {
+                if matches!(mut_ty.ty.kind, TyKind::Infer(())) {
                     String::new()
                 } else {
                     format!(
diff --git a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
index f699bba20ed..592c820a25e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
@@ -34,9 +34,9 @@ pub(super) fn check<'tcx>(
 
         let mut app = Applicability::MachineApplicable;
         let turbofish = match &cast_to_hir_ty.kind {
-            TyKind::Infer => String::new(),
+            TyKind::Infer(()) => String::new(),
             TyKind::Ptr(mut_ty) => {
-                if matches!(mut_ty.ty.kind, TyKind::Infer) {
+                if matches!(mut_ty.ty.kind, TyKind::Infer(())) {
                     String::new()
                 } else {
                     format!(
diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
index 332e897def7..9e1876e40f9 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(
                 }
             },
             // Ignore `p as *const _`
-            TyKind::Infer => return false,
+            TyKind::Infer(()) => return false,
             _ => {},
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
index c5c4a28646d..a34af6bc226 100644
--- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
@@ -18,7 +18,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>
             Mutability::Not => ("`0 as *const _` detected", "ptr::null"),
         };
 
-        let sugg = if let TyKind::Infer = mut_ty.ty.kind {
+        let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind {
             format!("{std_or_core}::{sugg_fn}()")
         } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
             format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 653726872c6..f5589d8f8e2 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -11,10 +11,10 @@ use rustc_ast::util::parser::ExprPrecedence;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{Visitor, walk_ty};
+use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
 use rustc_hir::{
-    self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
-    PatKind, Path, QPath, TyKind, UnOp,
+    self as hir, AmbigArg, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
+    Pat, PatKind, Path, QPath, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
@@ -796,7 +796,7 @@ impl TyCoercionStability {
                     if let Some(args) = path.args
                         && args.args.iter().any(|arg| match arg {
                             hir::GenericArg::Infer(_) => true,
-                            hir::GenericArg::Type(ty) => ty_contains_infer(ty),
+                            hir::GenericArg::Type(ty) => ty_contains_infer(ty.as_unambig_ty()),
                             _ => false,
                         })
                     {
@@ -815,7 +815,7 @@ impl TyCoercionStability {
                 | TyKind::Path(_) => Self::Deref,
                 TyKind::OpaqueDef(..)
                 | TyKind::TraitAscription(..)
-                | TyKind::Infer
+                | TyKind::Infer(())
                 | TyKind::Typeof(..)
                 | TyKind::TraitObject(..)
                 | TyKind::InferDelegation(..)
@@ -889,29 +889,23 @@ impl TyCoercionStability {
 fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
     struct V(bool);
     impl Visitor<'_> for V {
-        fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
-            if self.0
-                || matches!(
-                    ty.kind,
-                    TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err(_)
-                )
-            {
+        fn visit_infer(&mut self, inf_id: HirId, _inf_span: Span, kind: InferKind<'_>) -> Self::Result {
+            if let InferKind::Ty(_) | InferKind::Ambig(_) = kind {
                 self.0 = true;
-            } else {
-                walk_ty(self, ty);
             }
+            self.visit_id(inf_id);
         }
 
-        fn visit_generic_arg(&mut self, arg: &hir::GenericArg<'_>) {
-            if self.0 || matches!(arg, hir::GenericArg::Infer(_)) {
+        fn visit_ty(&mut self, ty: &hir::Ty<'_, AmbigArg>) {
+            if self.0 || matches!(ty.kind, TyKind::OpaqueDef(..) | TyKind::Typeof(_) | TyKind::Err(_)) {
                 self.0 = true;
-            } else if let hir::GenericArg::Type(ty) = arg {
-                self.visit_ty(ty);
+            } else {
+                walk_ty(self, ty);
             }
         }
     }
     let mut v = V(false);
-    v.visit_ty(ty);
+    v.visit_ty_unambig(ty);
     v.0
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
index a78c392e208..4e8853821c3 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
@@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Diag;
 use rustc_hir::def_id::DefIdMap;
 use rustc_hir::{
-    Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
+    AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
@@ -140,7 +140,7 @@ impl LateLintPass<'_> for DisallowedMacros {
         self.check(cx, stmt.span, None);
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) {
+    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_, AmbigArg>) {
         self.check(cx, ty.span, None);
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
index 3265404f2b2..947677e14bd 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefIdMap;
-use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
+use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::impl_lint_pass;
@@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
         }
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) {
         if let TyKind::Path(path) = &ty.kind {
             self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
         }
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index c0b4743fd71..57a30de7ad1 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -76,22 +76,22 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind {
             for arg in args {
-                check_clousure(cx, Some(receiver), arg);
+                check_closure(cx, Some(receiver), arg);
             }
         }
         if let ExprKind::Call(func, args) = expr.kind {
-            check_clousure(cx, None, func);
+            check_closure(cx, None, func);
             for arg in args {
-                check_clousure(cx, None, arg);
+                check_closure(cx, None, arg);
             }
         }
     }
 }
 
 #[allow(clippy::too_many_lines)]
-fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) {
+fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) {
     let body = if let ExprKind::Closure(c) = expr.kind
-        && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer))
+        && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(())))
         && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_))
         && !expr.span.from_expansion()
     {
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index d0159ab89e1..688979311c8 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -3,10 +3,10 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use clippy_utils::{is_from_proc_macro, trait_ref_of_method};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
-use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty};
+use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty, walk_unambig_ty};
 use rustc_hir::{
-    BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
-    PredicateOrigin, Ty, WherePredicate, WherePredicateKind,
+    AmbigArg, BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item,
+    ItemKind, PredicateOrigin, Ty, WherePredicate, WherePredicateKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
@@ -196,7 +196,7 @@ fn bound_to_trait_def_id(bound: &GenericBound<'_>) -> Option<LocalDefId> {
 impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> {
     type NestedFilter = nested_filter::OnlyBodies;
 
-    fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
+    fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) {
         if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
             self.ty_params.remove(&def_id);
         } else {
@@ -234,7 +234,7 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> {
                 // type, any params we find nested inside of it are being used as concrete types,
                 // and can therefore can be considered used. So, we're fine to walk the left-hand
                 // side of the where bound.
-                walk_ty(self, predicate.bounded_ty);
+                walk_unambig_ty(self, predicate.bounded_ty);
             }
             for bound in predicate.bounds {
                 walk_param_bound(self, bound);
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index e43c311eb85..9a73d0c0993 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -103,7 +103,9 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
                         "replace the `Into` implementation with `From<{}>`",
                         middle_trait_ref.self_ty()
                     );
-                    if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
+                    if let Some(suggestions) =
+                        convert_to_from(cx, into_trait_seg, target_ty.as_unambig_ty(), self_ty, impl_item_ref)
+                    {
                         diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
                     } else {
                         diag.help(message);
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index ad2da3c7fcd..47a5c19215b 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -2,9 +2,8 @@ use std::borrow::Cow;
 use std::collections::BTreeMap;
 
 use rustc_errors::{Applicability, Diag};
-use rustc_hir as hir;
-use rustc_hir::intravisit::{Visitor, walk_body, walk_expr, walk_inf, walk_ty};
-use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
+use rustc_hir::intravisit::{Visitor, VisitorExt, walk_body, walk_expr, walk_ty};
+use rustc_hir::{self as hir, AmbigArg, Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
@@ -112,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
         match item.kind {
             ItemKind::Impl(impl_) => {
                 let mut vis = ImplicitHasherTypeVisitor::new(cx);
-                vis.visit_ty(impl_.self_ty);
+                vis.visit_ty_unambig(impl_.self_ty);
 
                 for target in &vis.found {
                     if !item.span.eq_ctxt(target.span()) {
@@ -159,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
 
                 for ty in sig.decl.inputs {
                     let mut vis = ImplicitHasherTypeVisitor::new(cx);
-                    vis.visit_ty(ty);
+                    vis.visit_ty_unambig(ty);
 
                     for target in &vis.found {
                         if generics.span.from_expansion() {
@@ -287,21 +286,13 @@ impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
 }
 
 impl<'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'_, 'tcx> {
-    fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
-        if let Some(target) = ImplicitHasherType::new(self.cx, t) {
+    fn visit_ty(&mut self, t: &'tcx hir::Ty<'_, AmbigArg>) {
+        if let Some(target) = ImplicitHasherType::new(self.cx, t.as_unambig_ty()) {
             self.found.push(target);
         }
 
         walk_ty(self, t);
     }
-
-    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
-        if let Some(target) = ImplicitHasherType::new(self.cx, &inf.to_ty()) {
-            self.found.push(target);
-        }
-
-        walk_inf(self, inf);
-    }
 }
 
 /// Looks for default-hasher-dependent constructors like `HashMap::new`.
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index ef272c305d3..d02d9b2102b 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -3,8 +3,8 @@ use clippy_utils::source::snippet;
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
-    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, TyKind,
-    WherePredicateKind,
+    AmbigArg, AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers,
+    TyKind, WherePredicateKind,
 };
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -146,7 +146,9 @@ fn try_resolve_type<'tcx>(
     index: usize,
 ) -> Option<Ty<'tcx>> {
     match args.get(index - 1) {
-        Some(GenericArg::Type(ty)) => Some(lower_ty(tcx, ty)),
+        // I don't think we care about `GenericArg::Infer` since this is all for stuff in type signatures
+        // which do not permit inference variables.
+        Some(GenericArg::Type(ty)) => Some(lower_ty(tcx, ty.as_unambig_ty())),
         Some(_) => None,
         None => Some(tcx.type_of(generics.own_params[index].def_id).skip_binder()),
     }
@@ -335,7 +337,7 @@ impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls {
         }
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &rustc_hir::Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &rustc_hir::Ty<'tcx, AmbigArg>) {
         if let TyKind::OpaqueDef(opaque_ty, ..) = ty.kind {
             check(cx, opaque_ty.bounds);
         }
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
index 5a11702d7ce..2d2438514cc 100644
--- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -28,7 +28,7 @@ declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
 impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
         if let Some(ty) = local.ty // Ensure that it has a type defined
-            && let TyKind::Infer = &ty.kind // that type is '_'
+            && let TyKind::Infer(()) = &ty.kind // that type is '_'
             && local.span.eq_ctxt(ty.span)
             && !in_external_macro(cx.tcx.sess, local.span)
             && !is_from_proc_macro(cx, ty)
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 239822f4085..e6761ea5c67 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -7,13 +7,13 @@ use rustc_errors::Applicability;
 use rustc_hir::FnRetTy::Return;
 use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound, walk_poly_trait_ref,
-    walk_trait_ref, walk_ty, walk_where_predicate,
+    Visitor, VisitorExt, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound,
+    walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_unambig_ty, walk_where_predicate,
 };
 use rustc_hir::{
-    BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics,
-    HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
-    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate,
+    AmbigArg, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind,
+    Generics, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node,
+    PolyTraitRef, PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate,
     WherePredicateKind, lang_items,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -232,11 +232,11 @@ fn could_use_elision<'tcx>(
 
     // extract lifetimes in input argument types
     for arg in func.inputs {
-        input_visitor.visit_ty(arg);
+        input_visitor.visit_ty_unambig(arg);
     }
     // extract lifetimes in output type
     if let Return(ty) = func.output {
-        output_visitor.visit_ty(ty);
+        output_visitor.visit_ty_unambig(ty);
     }
     for lt in named_generics {
         input_visitor.visit_generic_param(lt);
@@ -340,7 +340,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident:
         && let Some(self_ty) = func.inputs.first()
     {
         let mut visitor = RefVisitor::new(cx);
-        visitor.visit_ty(self_ty);
+        visitor.visit_ty_unambig(self_ty);
 
         !visitor.all_lts().is_empty()
     } else {
@@ -426,14 +426,14 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> {
         }
     }
 
-    fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
+    fn visit_ty(&mut self, ty: &'tcx Ty<'_, AmbigArg>) {
         match ty.kind {
             TyKind::BareFn(&BareFnTy { decl, .. }) => {
                 let mut sub_visitor = RefVisitor::new(self.cx);
                 sub_visitor.visit_fn_decl(decl);
                 self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
             },
-            TyKind::TraitObject(bounds, lt, _) => {
+            TyKind::TraitObject(bounds, lt) => {
                 if !lt.is_elided() {
                     self.unelided_trait_object_lifetime = true;
                 }
@@ -456,7 +456,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
                 // a predicate like F: Trait or F: for<'a> Trait<'a>
                 let mut visitor = RefVisitor::new(cx);
                 // walk the type F, it may not contain LT refs
-                walk_ty(&mut visitor, pred.bounded_ty);
+                walk_unambig_ty(&mut visitor, pred.bounded_ty);
                 if !visitor.all_lts().is_empty() {
                     return true;
                 }
@@ -477,8 +477,8 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
             },
             WherePredicateKind::EqPredicate(ref pred) => {
                 let mut visitor = RefVisitor::new(cx);
-                walk_ty(&mut visitor, pred.lhs_ty);
-                walk_ty(&mut visitor, pred.rhs_ty);
+                walk_unambig_ty(&mut visitor, pred.lhs_ty);
+                walk_unambig_ty(&mut visitor, pred.rhs_ty);
                 if !visitor.lts.is_empty() {
                     return true;
                 }
@@ -541,7 +541,7 @@ where
         try_visit!(self.visit_id(hir_id));
 
         self.bounded_ty_depth += 1;
-        try_visit!(self.visit_ty(bounded_ty));
+        try_visit!(self.visit_ty_unambig(bounded_ty));
         self.bounded_ty_depth -= 1;
 
         walk_list!(self, visit_param_bound, bounds);
@@ -625,7 +625,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
     if let Some(ref trait_ref) = impl_.of_trait {
         walk_trait_ref(&mut checker, trait_ref);
     }
-    walk_ty(&mut checker, impl_.self_ty);
+    walk_unambig_ty(&mut checker, impl_.self_ty);
     for item in impl_.items {
         walk_impl_item_ref(&mut checker, item);
     }
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 3c669d94d69..37412866539 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
 use rustc_span::edition::Edition;
@@ -123,7 +123,7 @@ impl LateLintPass<'_> for MacroUseImports {
             self.push_unique_macro_pat_ty(cx, pat.span);
         }
     }
-    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) {
+    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_, AmbigArg>) {
         if ty.span.from_expansion() {
             self.push_unique_macro_pat_ty(cx, ty.span);
         }
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index c31656f8a05..d2a2321dae8 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -6,11 +6,11 @@ use clippy_utils::source::snippet_with_context;
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::packed::Pu128;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
 use rustc_session::impl_lint_pass;
-use rustc_span::sym;
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -57,13 +57,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
             && let ctxt = expr.span.ctxt()
             && left_expr.span.ctxt() == ctxt
             && right_expr.span.ctxt() == ctxt
-            && let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr)
+            && let Some((real_ty_span, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr)
             && matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_))
             && let ExprKind::Lit(lit) = &other_expr.kind
             && let LitKind::Int(Pu128(8), _) = lit.node
         {
             let mut app = Applicability::MachineApplicable;
-            let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
+            let ty_snip = snippet_with_context(cx, real_ty_span, ctxt, "..", &mut app).0;
             let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
 
             span_lint_and_sugg(
@@ -85,21 +85,21 @@ fn get_one_size_of_ty<'tcx>(
     cx: &LateContext<'tcx>,
     expr1: &'tcx Expr<'_>,
     expr2: &'tcx Expr<'_>,
-) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> {
+) -> Option<(Span, Ty<'tcx>, &'tcx Expr<'tcx>)> {
     match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) {
-        (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)),
-        (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)),
+        (Some((real_ty_span, resolved_ty)), None) => Some((real_ty_span, resolved_ty, expr2)),
+        (None, Some((real_ty_span, resolved_ty))) => Some((real_ty_span, resolved_ty, expr1)),
         _ => None,
     }
 }
 
-fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
+fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Span, Ty<'tcx>)> {
     if let ExprKind::Call(count_func, []) = expr.kind
         && let ExprKind::Path(ref count_func_qpath) = count_func.kind
         && let QPath::Resolved(_, count_func_path) = count_func_qpath
         && let Some(segment_zero) = count_func_path.segments.first()
         && let Some(args) = segment_zero.args
-        && let Some(GenericArg::Type(real_ty)) = args.args.first()
+        && let Some(real_ty_span) = args.args.first().map(|arg| arg.span())
         && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id()
         && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
     {
@@ -107,7 +107,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
             .node_args(count_func.hir_id)
             .types()
             .next()
-            .map(|resolved_ty| (*real_ty, resolved_ty))
+            .map(|resolved_ty| (real_ty_span, resolved_ty))
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 5e58054a986..ebfd946b07e 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
                 Node::Param(..) => (),
                 Node::LetStmt(local) => {
                     let Some(ty) = local.ty else { return };
-                    if matches!(ty.kind, TyKind::Infer) {
+                    if matches!(ty.kind, TyKind::Infer(())) {
                         return;
                     }
                 },
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index b84594c0da1..1673a6f8b3a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(
                         fn_decl.output,
                         FnRetTy::DefaultReturn(_)
                             | FnRetTy::Return(hir::Ty {
-                                kind: hir::TyKind::Infer,
+                                kind: hir::TyKind::Infer(()),
                                 ..
                             })
                     ) {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
index 6dc8adb42df..10112b62878 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs
@@ -1,14 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res};
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
 
 use super::UNNECESSARY_LITERAL_UNWRAP;
 
-fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -> Option<&'a hir::Ty<'a>> {
+fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -> Option<&'a hir::Ty<'a, AmbigArg>> {
     let args = args?;
 
     if args.len() <= index {
@@ -16,10 +16,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -
     }
 
     match args[index] {
-        hir::GenericArg::Type(ty) => match ty.kind {
-            hir::TyKind::Infer => None,
-            _ => Some(ty),
-        },
+        hir::GenericArg::Type(ty) => Some(ty),
         _ => None,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
index 6c2ae9cc6bf..0aec26f1011 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -130,7 +130,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
 fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option<Span> {
     if let [tuple_ty] = fn_decl.inputs
         && let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind
-        && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer)
+        && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer(()))
     {
         Some(elem_ty.span)
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs
index e2ab5e98504..0ee851a4cf9 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs
@@ -1,7 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_hir};
 use clippy_utils::higher;
-use rustc_hir as hir;
-use rustc_hir::intravisit;
+use rustc_hir::{self as hir, AmbigArg, intravisit};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
@@ -34,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for MutMut {
         intravisit::walk_block(&mut MutVisitor { cx }, block);
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_, AmbigArg>) {
         if let hir::TyKind::Ref(_, mty) = ty.kind
             && mty.mutbl == hir::Mutability::Mut
             && let hir::TyKind::Ref(_, mty) = mty.ty.kind
diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
index 82b9d10fbeb..c3c09946c27 100644
--- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
@@ -190,7 +190,8 @@ fn in_impl<'tcx>(
         && let Some(generic_args) = seg.args
         && let Some(GenericArg::Type(other_ty)) = generic_args.args.last()
     {
-        Some((item.self_ty, other_ty))
+        // `_` is not permitted in impl headers
+        Some((item.self_ty, other_ty.as_unambig_ty()))
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
index 7d59bf24d93..074345e7532 100644
--- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::last_path_segment;
 use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
-use rustc_hir::{GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind};
+use rustc_hir::{AmbigArg, GenericArg, GenericArgsParentheses, Mutability, Ty, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -36,7 +36,7 @@ declare_clippy_lint! {
 declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]);
 
 impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) {
         if let TyKind::Ref(_, ref mut_ty) = ty.kind
             && mut_ty.mutbl == Mutability::Not
             && let TyKind::Path(qpath) = &mut_ty.ty.kind
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 99844beb8f0..790e0965198 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -10,8 +10,8 @@ use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
-    BoundPolarity, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
-    TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicateKind,
+    AmbigArg, BoundPolarity, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment,
+    PredicateOrigin, QPath, TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicateKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -171,7 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
         }
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) {
         if let TyKind::Ref(.., mut_ty) = &ty.kind
             && let TyKind::TraitObject(bounds, ..) = mut_ty.ty.kind
             && bounds.len() > 2
diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
index b2892d136fa..531422798a6 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
@@ -52,7 +52,6 @@ pub(super) fn check<'tcx>(
     let missing_generic = match args {
         Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| match arg {
             GenericArg::Infer(_) => true,
-            GenericArg::Type(ty) => matches!(ty.kind, TyKind::Infer),
             _ => false,
         }),
         _ => true,
@@ -65,7 +64,7 @@ pub(super) fn check<'tcx>(
         // ... which does have type annotations.
         if let Some(ty) = local.ty
             // If this is a `let x: _ =`, we should lint.
-            && !matches!(ty.kind, TyKind::Infer)
+            && !matches!(ty.kind, TyKind::Infer(()))
         {
             return false;
         }
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index bde88ab61ad..1a5fdf0cd64 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                     _ => None,
                 })
             {
-                if is_any_trait(cx, inner) {
+                if is_any_trait(cx, inner.as_unambig_ty()) {
                     // Ignore `Box<Any>` types; see issue #1884 for details.
                     return false;
                 }
@@ -47,7 +47,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                 // Originally reported as the issue #3128.
                 let inner_snippet = snippet(cx, inner.span, "..");
                 let suggestion = match &inner.kind {
-                    TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => {
+                    TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => {
                         format!("&{ltopt}({inner_snippet})")
                     },
                     TyKind::Path(qpath)
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 43cce625c64..391c36df492 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -560,7 +560,7 @@ impl Types {
                                     _ => None,
                                 })
                         }) {
-                            self.check_ty(cx, ty, context);
+                            self.check_ty(cx, ty.as_unambig_ty(), context);
                         }
                     },
                     QPath::Resolved(None, p) => {
@@ -574,7 +574,7 @@ impl Types {
                                     _ => None,
                                 })
                         }) {
-                            self.check_ty(cx, ty, context);
+                            self.check_ty(cx, ty.as_unambig_ty(), context);
                         }
                     },
                     QPath::TypeRelative(ty, seg) => {
@@ -585,7 +585,7 @@ impl Types {
                                 GenericArg::Type(ty) => Some(ty),
                                 _ => None,
                             }) {
-                                self.check_ty(cx, ty, context);
+                                self.check_ty(cx, ty.as_unambig_ty(), context);
                             }
                         }
                     },
diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
index b89bd6a8d05..7f51660293b 100644
--- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint;
-use rustc_hir as hir;
-use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty};
-use rustc_hir::{GenericParamKind, TyKind};
+use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
+use rustc_hir::{self as hir, AmbigArg, GenericParamKind, TyKind};
 use rustc_lint::LateContext;
+use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 use super::TYPE_COMPLEXITY;
@@ -10,7 +10,7 @@ use super::TYPE_COMPLEXITY;
 pub(super) fn check(cx: &LateContext<'_>, ty: &hir::Ty<'_>, type_complexity_threshold: u64) -> bool {
     let score = {
         let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 };
-        visitor.visit_ty(ty);
+        visitor.visit_ty_unambig(ty);
         visitor.score
     };
 
@@ -36,15 +36,15 @@ struct TypeComplexityVisitor {
 }
 
 impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
-    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
+    fn visit_infer(&mut self, inf_id: hir::HirId, _inf_span: Span, _kind: InferKind<'tcx>) -> Self::Result {
         self.score += 1;
-        walk_inf(self, inf);
+        self.visit_id(inf_id);
     }
 
-    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
+    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_, AmbigArg>) {
         let (add_score, sub_nest) = match ty.kind {
-            // _, &x and *x have only small overhead; don't mess with nesting level
-            TyKind::Infer | TyKind::Ptr(..) | TyKind::Ref(..) => (1, 0),
+            // &x and *x have only small overhead; don't mess with nesting level
+            TyKind::Ptr(..) | TyKind::Ref(..) => (1, 0),
 
             // the "normal" components of a type: named types, arrays/tuples
             TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1),
@@ -52,7 +52,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
             // function types bring a lot of overhead
             TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1),
 
-            TyKind::TraitObject(param_bounds, _, _) => {
+            TyKind::TraitObject(param_bounds, _) => {
                 let has_lifetime_parameters = param_bounds.iter().any(|bound| {
                     bound
                         .bound_generic_params
diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
index 9b236d3bda5..769244c675e 100644
--- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
@@ -35,7 +35,8 @@ pub(super) fn check<'tcx>(
             && let Some(GenericArg::Type(boxed_ty)) = last.args.first()
             // extract allocator from the Box for later
             && let boxed_alloc_ty = last.args.get(1)
-            && let ty_ty = lower_ty(cx.tcx, boxed_ty)
+            // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay
+            && let ty_ty = lower_ty(cx.tcx, boxed_ty.as_unambig_ty())
             && !ty_ty.has_escaping_bound_vars()
             && ty_ty.is_sized(cx.tcx, cx.typing_env())
             && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes())
@@ -55,7 +56,8 @@ pub(super) fn check<'tcx>(
                     }
                 },
                 (Some(GenericArg::Type(l)), Some(GenericArg::Type(r))) =>
-                    lower_ty(cx.tcx, l) == lower_ty(cx.tcx, r),
+                    // we don't expect to encounter `_` here so ignore `GenericArg::Infer` is okay
+                    lower_ty(cx.tcx, l.as_unambig_ty()) == lower_ty(cx.tcx, r.as_unambig_ty()),
                 _ => false
             }
         {
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index d2727968c0c..660bdb9e2be 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
             return;
         }
 
-        if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer))
+        if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer(())))
             || matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()))
             && expr_needs_inferred_result(cx, init)
         {
@@ -158,7 +158,7 @@ fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -
     }
     while let Some(id) = locals_to_check.pop() {
         if let Node::LetStmt(l) = cx.tcx.parent_hir_node(id) {
-            if !l.ty.is_none_or(|ty| matches!(ty.kind, TyKind::Infer)) {
+            if !l.ty.is_none_or(|ty| matches!(ty.kind, TyKind::Infer(()))) {
                 return false;
             }
             if let Some(e) = l.init {
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 05c5be03002..84b6430294f 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -7,10 +7,10 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty};
+use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
 use rustc_hir::{
-    self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl,
-    ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
+    self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind,
+    HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
 };
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -179,7 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
                 if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
                     let mut visitor = SkipTyCollector::default();
-                    visitor.visit_ty(impl_hir_ty);
+                    visitor.visit_ty_unambig(impl_hir_ty);
                     types_to_skip.extend(visitor.types_to_skip);
                 }
             }
@@ -201,7 +201,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
         }
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &Ty<'tcx>) {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &Ty<'tcx, AmbigArg>) {
         if !hir_ty.span.from_expansion()
             && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS)
             && let Some(&StackItem::Check {
@@ -218,7 +218,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             && let ty = if in_body > 0 {
                 cx.typeck_results().node_type(hir_ty.hir_id)
             } else {
-                lower_ty(cx.tcx, hir_ty)
+                // We don't care about ignoring infer vars here
+                lower_ty(cx.tcx, hir_ty.as_unambig_ty())
             }
             && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity()
             && same_type_and_consts(ty, impl_ty)
@@ -275,12 +276,14 @@ struct SkipTyCollector {
 }
 
 impl Visitor<'_> for SkipTyCollector {
-    fn visit_infer(&mut self, inf: &hir::InferArg) {
-        self.types_to_skip.push(inf.hir_id);
-
-        walk_inf(self, inf);
+    fn visit_infer(&mut self, inf_id: HirId, _inf_span: Span, kind: InferKind<'_>) -> Self::Result {
+        // Conservatively assume ambiguously kinded inferred arguments are type arguments
+        if let InferKind::Ambig(_) | InferKind::Ty(_) = kind {
+            self.types_to_skip.push(inf_id);
+        }
+        self.visit_id(inf_id);
     }
-    fn visit_ty(&mut self, hir_ty: &Ty<'_>) {
+    fn visit_ty(&mut self, hir_ty: &Ty<'_, AmbigArg>) {
         self.types_to_skip.push(hir_ty.hir_id);
 
         walk_ty(self, hir_ty);
diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
index e14480b8655..1221abec1ab 100644
--- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item};
-use rustc_hir::{self as hir, HirId, ItemKind, Node};
+use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node};
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf as _;
@@ -44,10 +44,11 @@ declare_clippy_lint! {
 declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]);
 
 impl LateLintPass<'_> for ZeroSizedMapValues {
-    fn check_ty<'tcx>(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) {
+    fn check_ty<'tcx>(&mut self, cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx, AmbigArg>) {
         if !hir_ty.span.from_expansion()
             && !in_trait_impl(cx, hir_ty.hir_id)
-            && let ty = ty_from_hir_ty(cx, hir_ty)
+            // We don't care about infer vars
+            && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty())
             && (is_type_diagnostic_item(cx, ty, sym::HashMap) || is_type_diagnostic_item(cx, ty, sym::BTreeMap))
             && let ty::Adt(_, args) = ty.kind()
             && let ty = args.type_at(1)
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 68e7f807bf5..cd6290ced33 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -399,8 +399,8 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
         TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1),
         TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
         TyKind::Path(qpath) => qpath_search_pat(&qpath),
-        TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
-        TyKind::TraitObject(_, _, TraitObjectSyntax::Dyn) => (Pat::Str("dyn"), Pat::Str("")),
+        TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")),
+        TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => (Pat::Str("dyn"), Pat::Str("")),
         // NOTE: `TraitObject` is incomplete. It will always return true then.
         _ => (Pat::Str(""), Pat::Str("")),
     }
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index a1c48d5c36c..d76231a6eea 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -459,9 +459,9 @@ impl HirEqInterExpr<'_, '_, '_> {
 
     fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
         match (left, right) {
-            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_const_arg(l, r),
+            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_const_arg(l.as_unambig_ct(), r.as_unambig_ct()),
             (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
-            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
+            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty.as_unambig_ty(), r_ty.as_unambig_ty()),
             (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
             _ => false,
         }
@@ -618,7 +618,7 @@ impl HirEqInterExpr<'_, '_, '_> {
             },
             (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
             (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
-            (&TyKind::Infer, &TyKind::Infer) => true,
+            (&TyKind::Infer(()), &TyKind::Infer(())) => true,
             _ => false,
         }
     }
@@ -1281,7 +1281,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 }
             },
             TyKind::Path(qpath) => self.hash_qpath(qpath),
-            TyKind::TraitObject(_, lifetime, _) => {
+            TyKind::TraitObject(_, lifetime) => {
                 self.hash_lifetime(lifetime);
             },
             TyKind::Typeof(anon_const) => {
@@ -1291,7 +1291,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 self.hash_ty(binder.inner_ty);
             },
             TyKind::Err(_)
-            | TyKind::Infer
+            | TyKind::Infer(())
             | TyKind::Never
             | TyKind::InferDelegation(..)
             | TyKind::OpaqueDef(_)
@@ -1318,8 +1318,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
         for arg in arg_list {
             match *arg {
                 GenericArg::Lifetime(l) => self.hash_lifetime(l),
-                GenericArg::Type(ty) => self.hash_ty(ty),
-                GenericArg::Const(ca) => self.hash_const_arg(ca),
+                GenericArg::Type(ty) => self.hash_ty(ty.as_unambig_ty()),
+                GenericArg::Const(ca) => self.hash_const_arg(ca.as_unambig_ct()),
                 GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
             }
         }
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index eecfc3fb13f..93e3fb36b35 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -437,7 +437,7 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc
         .map_or(&[][..], |a| a.args)
         .iter()
         .filter_map(|a| match a {
-            hir::GenericArg::Type(ty) => Some(*ty),
+            hir::GenericArg::Type(ty) => Some(ty.as_unambig_ty()),
             _ => None,
         })
 }
@@ -2148,7 +2148,7 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
 pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Closure(&Closure { body, fn_decl, .. })
-            if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer)) =>
+            if fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer(()))) =>
         {
             is_body_identity_function(cx, cx.tcx.hir().body(body))
         },
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index d7640ebfb00..b5cec31ba9d 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -14,8 +14,8 @@
 use crate::def_path_res;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{Visitor, walk_qpath, walk_ty};
-use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
+use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty};
+use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
 use rustc_span::{Span, Symbol};
@@ -116,14 +116,15 @@ impl<'cx> Visitor<'cx> for CertaintyVisitor<'cx, '_> {
         }
     }
 
-    fn visit_ty(&mut self, ty: &'cx hir::Ty<'_>) {
-        if matches!(ty.kind, TyKind::Infer) {
-            self.certainty = Certainty::Uncertain;
-        }
+    fn visit_ty(&mut self, ty: &'cx hir::Ty<'_, AmbigArg>) {
         if self.certainty != Certainty::Uncertain {
             walk_ty(self, ty);
         }
     }
+
+    fn visit_infer(&mut self, _inf_id: HirId, _inf_span: Span, _kind: InferKind<'cx>) -> Self::Result {
+        self.certainty = Certainty::Uncertain;
+    }
 }
 
 fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty {
@@ -139,7 +140,7 @@ fn type_certainty(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Certainty {
     }
 
     let mut visitor = CertaintyVisitor::new(cx);
-    visitor.visit_ty(ty);
+    visitor.visit_ty_unambig(ty);
     visitor.certainty
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 7a3a861a9ca..dcc763a8abd 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -2,7 +2,7 @@ use crate::ty::needs_ordered_drop;
 use crate::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_ast::visit::{VisitorResult, try_visit};
-use rustc_hir as hir;
+use rustc_hir::{self as hir, AmbigArg};
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
 use rustc_hir::{
@@ -122,7 +122,7 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>(
         }
 
         // Avoid unnecessary `walk_*` calls.
-        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result {
+        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
             ControlFlow::Continue(())
         }
         fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
@@ -172,7 +172,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>(
             ControlFlow::Continue(())
         }
         // Avoid unnecessary `walk_*` calls.
-        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result {
+        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
             ControlFlow::Continue(())
         }
         fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
diff --git a/tests/ui/closures/binder/forbid_ambig_const_infers.rs b/tests/ui/closures/binder/forbid_ambig_const_infers.rs
new file mode 100644
index 00000000000..e9d783711ee
--- /dev/null
+++ b/tests/ui/closures/binder/forbid_ambig_const_infers.rs
@@ -0,0 +1,9 @@
+#![feature(generic_arg_infer, closure_lifetime_binder)]
+
+struct Foo<const N: usize>([u32; N]);
+
+fn main() {
+    let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0[0] };
+    //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present
+    c(&Foo([1_u32; 1]));
+}
diff --git a/tests/ui/closures/binder/forbid_ambig_const_infers.stderr b/tests/ui/closures/binder/forbid_ambig_const_infers.stderr
new file mode 100644
index 00000000000..396c9e8c916
--- /dev/null
+++ b/tests/ui/closures/binder/forbid_ambig_const_infers.stderr
@@ -0,0 +1,10 @@
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/forbid_ambig_const_infers.rs:6:33
+   |
+LL |     let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0[0] };
+   |             -------             ^
+   |             |
+   |             `for<...>` is here
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/closures/binder/forbid_ambig_type_infers.rs b/tests/ui/closures/binder/forbid_ambig_type_infers.rs
new file mode 100644
index 00000000000..4e717ef3a17
--- /dev/null
+++ b/tests/ui/closures/binder/forbid_ambig_type_infers.rs
@@ -0,0 +1,9 @@
+#![feature(generic_arg_infer, closure_lifetime_binder)]
+
+struct Foo<T>(T);
+
+fn main() {
+    let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0 };
+    //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present
+    c(&Foo(1_u32));
+}
diff --git a/tests/ui/closures/binder/forbid_ambig_type_infers.stderr b/tests/ui/closures/binder/forbid_ambig_type_infers.stderr
new file mode 100644
index 00000000000..8f19d710073
--- /dev/null
+++ b/tests/ui/closures/binder/forbid_ambig_type_infers.stderr
@@ -0,0 +1,10 @@
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/forbid_ambig_type_infers.rs:6:33
+   |
+LL |     let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0 };
+   |             -------             ^
+   |             |
+   |             `for<...>` is here
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/closures/binder/forbid_const_infer.rs b/tests/ui/closures/binder/forbid_const_infer.rs
new file mode 100644
index 00000000000..f5b8bf188df
--- /dev/null
+++ b/tests/ui/closures/binder/forbid_const_infer.rs
@@ -0,0 +1,7 @@
+#![feature(generic_arg_infer, closure_lifetime_binder)]
+
+fn main() {
+    let c = for<'a> |b: &'a [u32; _]| -> u32 { b[0] };
+    //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present
+    c(&[1_u32; 2]);
+}
diff --git a/tests/ui/closures/binder/forbid_const_infer.stderr b/tests/ui/closures/binder/forbid_const_infer.stderr
new file mode 100644
index 00000000000..e93685d400e
--- /dev/null
+++ b/tests/ui/closures/binder/forbid_const_infer.stderr
@@ -0,0 +1,10 @@
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/forbid_const_infer.rs:4:35
+   |
+LL |     let c = for<'a> |b: &'a [u32; _]| -> u32 { b[0] };
+   |             -------               ^
+   |             |
+   |             `for<...>` is here
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs
new file mode 100644
index 00000000000..81c42183b38
--- /dev/null
+++ b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs
@@ -0,0 +1,12 @@
+//@ check-pass
+//@ revisions: gate nogate
+#![cfg_attr(gate, feature(generic_arg_infer))]
+
+fn main() {
+    // AST Types preserve parens for pretty printing reasons. This means
+    // that this is parsed as a `TyKind::Paren(TyKind::Infer)`. Generic
+    // arg lowering therefore needs to take into account not just `TyKind::Infer`
+    // but `TyKind::Infer` wrapped in arbitrarily many `TyKind::Paren`.
+    let a: Vec<(_)> = vec![1_u8];
+    let a: Vec<(((((_)))))> = vec![1_u8];
+}
diff --git a/tests/ui/const-generics/issues/issue-62878.min.stderr b/tests/ui/const-generics/issues/issue-62878.min.stderr
index bd17d70a50b..1bb111b188d 100644
--- a/tests/ui/const-generics/issues/issue-62878.min.stderr
+++ b/tests/ui/const-generics/issues/issue-62878.min.stderr
@@ -18,19 +18,17 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more
 LL + #![feature(adt_const_params)]
    |
 
-error[E0747]: type provided when a constant was expected
+error[E0658]: const arguments cannot yet be inferred with `_`
   --> $DIR/issue-62878.rs:10:11
    |
 LL |     foo::<_, { [1] }>();
    |           ^
    |
-   = help: const arguments cannot yet be inferred with `_`
-help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
-   |
-LL + #![feature(generic_arg_infer)]
-   |
+   = note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
+   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0747, E0770.
-For more information about an error, try `rustc --explain E0747`.
+Some errors have detailed explanations: E0658, E0770.
+For more information about an error, try `rustc --explain E0658`.
diff --git a/tests/ui/const-generics/issues/issue-62878.rs b/tests/ui/const-generics/issues/issue-62878.rs
index 0b5269df85e..c80b46ddbc4 100644
--- a/tests/ui/const-generics/issues/issue-62878.rs
+++ b/tests/ui/const-generics/issues/issue-62878.rs
@@ -8,5 +8,5 @@ fn foo<const N: usize, const A: [u8; N]>() {}
 
 fn main() {
     foo::<_, { [1] }>();
-    //[min]~^ ERROR: type provided when a constant was expected
+    //[min]~^ ERROR: const arguments cannot yet be inferred with `_`
 }
diff --git a/tests/ui/did_you_mean/bad-assoc-ty.stderr b/tests/ui/did_you_mean/bad-assoc-ty.stderr
index 41039ae82a6..5fc2f7c1fe6 100644
--- a/tests/ui/did_you_mean/bad-assoc-ty.stderr
+++ b/tests/ui/did_you_mean/bad-assoc-ty.stderr
@@ -233,11 +233,6 @@ LL | fn foo<X: K<_, _>>(x: X) {}
    |             ^  ^ not allowed in type signatures
    |             |
    |             not allowed in type signatures
-   |
-help: use type parameters instead
-   |
-LL | fn foo<X: K<T, T>, T>(x: X) {}
-   |             ~  ~ +++
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
   --> $DIR/bad-assoc-ty.rs:54:34
diff --git a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
index 96fb4a53609..73e6988b09c 100644
--- a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
+++ b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
@@ -8,17 +8,15 @@ LL |     let _y: [u8; _] = [0; 3];
    = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error[E0747]: type provided when a constant was expected
+error[E0658]: const arguments cannot yet be inferred with `_`
   --> $DIR/feature-gate-generic_arg_infer.rs:18:20
    |
-LL |     let _x = foo::<_>([1,2]);
+LL |     let _x = foo::<_>([1, 2]);
    |                    ^
    |
-   = help: const arguments cannot yet be inferred with `_`
-help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
-   |
-LL + #![feature(generic_arg_infer)]
-   |
+   = note: see issue #85077 <https://github.com/rust-lang/rust/issues/85077> for more information
+   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: using `_` for array lengths is unstable
   --> $DIR/feature-gate-generic_arg_infer.rs:11:27
@@ -32,5 +30,4 @@ LL |     let _x: [u8; 3] = [0; _];
 
 error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0658, E0747.
-For more information about an error, try `rustc --explain E0658`.
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs b/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs
index de4b7078ea6..147978b0557 100644
--- a/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs
+++ b/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs
@@ -4,7 +4,7 @@
 #![cfg_attr(feature, feature(generic_arg_infer))]
 
 fn foo<const N: usize>(_: [u8; N]) -> [u8; N] {
-  [0; N]
+    [0; N]
 }
 
 fn bar() {
@@ -15,7 +15,7 @@ fn bar() {
 }
 
 fn main() {
-    let _x = foo::<_>([1,2]);
-    //[normal]~^ ERROR: type provided when a constant was expected
+    let _x = foo::<_>([1, 2]);
+    //[normal]~^ ERROR: const arguments cannot yet be inferred with `_`
     bar();
 }
diff --git a/tests/ui/generics/issue-79605.stderr b/tests/ui/generics/issue-79605.stderr
index 67fed200f96..049f77a6584 100644
--- a/tests/ui/generics/issue-79605.stderr
+++ b/tests/ui/generics/issue-79605.stderr
@@ -3,11 +3,6 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
    |
 LL | impl X<'_, _> {}
    |            ^ not allowed in type signatures
-   |
-help: use type parameters instead
-   |
-LL | impl<T> X<'_, T> {}
-   |     +++       ~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/macros/macro-span-issue-116502.stderr b/tests/ui/macros/macro-span-issue-116502.stderr
index da02855660a..2a581f7031b 100644
--- a/tests/ui/macros/macro-span-issue-116502.stderr
+++ b/tests/ui/macros/macro-span-issue-116502.stderr
@@ -17,13 +17,6 @@ LL |         T: Trait<m!()>;
    |                  ---- in this macro invocation
    |
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: use type parameters instead
-   |
-LL ~             U
-LL |         };
-LL |     }
-LL ~     struct S<U>(m!(), T)
-   |
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/issues/issue-14303-fncall.full.stderr b/tests/ui/parser/issues/issue-14303-fncall.full.stderr
index 1986f70bf67..5a017c85c16 100644
--- a/tests/ui/parser/issues/issue-14303-fncall.full.stderr
+++ b/tests/ui/parser/issues/issue-14303-fncall.full.stderr
@@ -1,8 +1,8 @@
-error[E0747]: type provided when a lifetime was expected
-  --> $DIR/issue-14303-fncall.rs:15:26
+error[E0747]: placeholder provided when a lifetime was expected
+  --> $DIR/issue-14303-fncall.rs:12:77
    |
-LL |         .collect::<Vec<S<_, 'a>>>();
-   |                          ^
+LL |     let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::<Vec<S<_, 'a>>>();
+   |                                                                             ^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr b/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr
index 2de59b8c746..5a017c85c16 100644
--- a/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr
+++ b/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr
@@ -1,8 +1,8 @@
-error[E0747]: inferred provided when a lifetime was expected
-  --> $DIR/issue-14303-fncall.rs:15:26
+error[E0747]: placeholder provided when a lifetime was expected
+  --> $DIR/issue-14303-fncall.rs:12:77
    |
-LL |         .collect::<Vec<S<_, 'a>>>();
-   |                          ^
+LL |     let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::<Vec<S<_, 'a>>>();
+   |                                                                             ^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/issues/issue-14303-fncall.rs b/tests/ui/parser/issues/issue-14303-fncall.rs
index 59d4eab06d6..8f7fbec9470 100644
--- a/tests/ui/parser/issues/issue-14303-fncall.rs
+++ b/tests/ui/parser/issues/issue-14303-fncall.rs
@@ -3,18 +3,15 @@
 // we need the above to avoid ast borrowck failure in recovered code
 #![cfg_attr(generic_arg, feature(generic_arg_infer))]
 
-
 struct S<'a, T> {
     a: &'a T,
     b: &'a T,
 }
 
 fn foo<'a, 'b>(start: &'a usize, end: &'a usize) {
-    let _x = (*start..*end)
-        .map(|x| S { a: start, b: end })
-        .collect::<Vec<S<_, 'a>>>();
-        //[generic_arg]~^ ERROR inferred provided when a lifetime was expected
-        //[full]~^^ ERROR type provided when a lifetime was expected
+    let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::<Vec<S<_, 'a>>>();
+    //[generic_arg]~^ ERROR placeholder provided when a lifetime was expected
+    //[full]~^^ ERROR placeholder provided when a lifetime was expected
 }
 
 fn main() {}
diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr
index a4b65256574..6559845c23e 100644
--- a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr
+++ b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr
@@ -17,10 +17,6 @@ error[E0282]: type annotations needed
 LL |         .sum::<_>()
    |          ^^^ cannot infer type of the type parameter `S` declared on the method `sum`
    |
-help: consider specifying the generic argument
-   |
-LL |         .sum::<S>()
-   |             ~~~~~
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/type-alias-impl-trait/issue-77179.stderr b/tests/ui/type-alias-impl-trait/issue-77179.stderr
index 85a943c26e2..16bbc996d90 100644
--- a/tests/ui/type-alias-impl-trait/issue-77179.stderr
+++ b/tests/ui/type-alias-impl-trait/issue-77179.stderr
@@ -28,10 +28,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
   --> $DIR/issue-77179.rs:18:25
    |
 LL |     fn bar() -> Pointer<_>;
-   |                         ^
-   |                         |
-   |                         not allowed in type signatures
-   |                         help: use type parameters instead: `T`
+   |                         ^ not allowed in type signatures
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs
index 55f45ade388..c5f8b2764ec 100644
--- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs
+++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs
@@ -4,7 +4,7 @@
 type Pat<const START: u32, const END: u32> =
     std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
 //~^ ERROR type and const arguments are not allowed on const parameter `START`
-//~| ERROR type arguments are not allowed on const parameter `END`
+//~| ERROR generic arguments are not allowed on const parameter `END`
 //~| ERROR associated item constraints are not allowed here
 
 fn main() {}
diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr
index 7f4e6e314f5..f31809bf397 100644
--- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr
+++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr
@@ -12,11 +12,11 @@ note: const parameter `START` defined here
 LL | type Pat<const START: u32, const END: u32> =
    |                ^^^^^
 
-error[E0109]: type arguments are not allowed on const parameter `END`
+error[E0109]: generic arguments are not allowed on const parameter `END`
   --> $DIR/bad_const_generics_args_on_const_param.rs:5:64
    |
 LL |     std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>);
-   |                                                          ---   ^ type argument not allowed
+   |                                                          ---   ^ generic argument not allowed
    |                                                          |
    |                                                          not allowed on const parameter `END`
    |
diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr
index c97b9312076..d2a850d7dbf 100644
--- a/tests/ui/typeck/typeck_type_placeholder_item.stderr
+++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr
@@ -507,22 +507,12 @@ LL | impl BadTrait<_> for BadStruct<_> {}
    |               ^                ^ not allowed in type signatures
    |               |
    |               not allowed in type signatures
-   |
-help: use type parameters instead
-   |
-LL | impl<T> BadTrait<T> for BadStruct<T> {}
-   |     +++          ~                ~
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
   --> $DIR/typeck_type_placeholder_item.rs:162:34
    |
 LL | fn impl_trait() -> impl BadTrait<_> {
    |                                  ^ not allowed in type signatures
-   |
-help: use type parameters instead
-   |
-LL | fn impl_trait<T>() -> impl BadTrait<T> {
-   |              +++                    ~
 
 error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs
   --> $DIR/typeck_type_placeholder_item.rs:167:25