about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-27 16:24:45 +0000
committerbors <bors@rust-lang.org>2021-07-27 16:24:45 +0000
commitfd853c00e255559255885aadff9e93a1760c8728 (patch)
tree49fd574977dbf99203919c48b27fb35438117d0b
parent3bc9dd0dd293ab82945e35888ed6d7ab802761ef (diff)
parent8759f00c73641d44b3ab7a2290e3c58168d3e30f (diff)
downloadrust-fd853c00e255559255885aadff9e93a1760c8728.tar.gz
rust-fd853c00e255559255885aadff9e93a1760c8728.zip
Auto merge of #83484 - JulianKnodt:infer, r=oli-obk,lcnr
Add hir::GenericArg::Infer

In order to extend inference to consts, make an Infer type on hir::GenericArg.
-rw-r--r--compiler/rustc_ast/src/ast.rs6
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs93
-rw-r--r--compiler/rustc_feature/src/active.rs3
-rw-r--r--compiler/rustc_hir/src/hir.rs70
-rw-r--r--compiler/rustc_hir/src/intravisit.rs8
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs8
-rw-r--r--compiler/rustc_lint/src/late.rs5
-rw-r--r--compiler/rustc_lint/src/passes.rs1
-rw-r--r--compiler/rustc_middle/src/hir/map/collector.rs8
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs3
-rw-r--r--compiler/rustc_privacy/src/lib.rs40
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs6
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs43
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs45
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs15
-rw-r--r--compiler/rustc_typeck/src/check/method/confirm.rs7
-rw-r--r--compiler/rustc_typeck/src/check/writeback.rs9
-rw-r--r--compiler/rustc_typeck/src/collect.rs22
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs3
-rw-r--r--src/librustdoc/clean/mod.rs1
-rw-r--r--src/librustdoc/clean/types.rs1
-rw-r--r--src/librustdoc/html/format.rs1
-rw-r--r--src/librustdoc/json/conversions.rs1
-rw-r--r--src/rustdoc-json-types/lib.rs1
-rw-r--r--src/test/ui/const-generics/issues/issue-62878.full.stderr10
-rw-r--r--src/test/ui/const-generics/issues/issue-62878.rs6
-rw-r--r--src/test/ui/const-generics/min_const_generics/inferred_const.rs9
-rw-r--r--src/test/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr (renamed from src/test/ui/const-generics/min_const_generics/inferred_const.stderr)7
-rw-r--r--src/test/ui/feature-gates/feature-gate-generic_arg_infer.rs13
-rw-r--r--src/test/ui/inference/infer-arg-test.rs24
-rw-r--r--src/test/ui/inference/infer-arg-test.stderr36
-rw-r--r--src/test/ui/object-lifetime-default-inferred.rs35
-rw-r--r--src/test/ui/parser/issue-14303-fncall.full.stderr (renamed from src/test/ui/parser/issue-14303-fncall.stderr)2
-rw-r--r--src/test/ui/parser/issue-14303-fncall.generic_arg.stderr9
-rw-r--r--src/test/ui/parser/issue-14303-fncall.rs8
-rw-r--r--src/test/ui/span/issue-42234-unknown-receiver-type.full.stderr (renamed from src/test/ui/span/issue-42234-unknown-receiver-type.stderr)4
-rw-r--r--src/test/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr21
-rw-r--r--src/test/ui/span/issue-42234-unknown-receiver-type.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/types/type_complexity.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs15
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs2
44 files changed, 524 insertions, 108 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f851725058d..8cab83707dc 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -336,6 +336,9 @@ pub enum ParamKindOrd {
     // is active. Specifically, if it's only `min_const_generics`, it will still require
     // ordering consts after types.
     Const { unordered: bool },
+    // `Infer` is not actually constructed directly from the AST, but is implicitly constructed
+    // during HIR lowering, and `ParamKindOrd` will implicitly order inferred variables last.
+    Infer,
 }
 
 impl Ord for ParamKindOrd {
@@ -343,7 +346,7 @@ impl Ord for ParamKindOrd {
         use ParamKindOrd::*;
         let to_int = |v| match v {
             Lifetime => 0,
-            Type | Const { unordered: true } => 1,
+            Infer | Type | Const { unordered: true } => 1,
             // technically both consts should be ordered equally,
             // but only one is ever encountered at a time, so this is
             // fine.
@@ -371,6 +374,7 @@ impl fmt::Display for ParamKindOrd {
             ParamKindOrd::Lifetime => "lifetime".fmt(f),
             ParamKindOrd::Type => "type".fmt(f),
             ParamKindOrd::Const { .. } => "const".fmt(f),
+            ParamKindOrd::Infer => "infer".fmt(f),
         }
     }
 }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 57bf7be4056..3bd42ba6090 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -51,7 +51,7 @@ use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{DefId, DefIdMap, DefPathHash, LocalDefId, CRATE_DEF_ID};
 use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
 use rustc_hir::intravisit;
-use rustc_hir::{ConstArg, GenericArg, ParamName};
+use rustc_hir::{ConstArg, GenericArg, InferKind, ParamName};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_session::lint::builtin::{BARE_TRAIT_OBJECTS, MISSING_ABI};
 use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
@@ -1243,48 +1243,59 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         match arg {
             ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(&lt)),
             ast::GenericArg::Type(ty) => {
-                // 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.
-                if let TyKind::Path(ref qself, ref path) = ty.kind {
-                    if let Some(partial_res) = self.resolver.get_partial_res(ty.id) {
-                        let res = partial_res.base_res();
-                        if !res.matches_ns(Namespace::TypeNS) {
-                            debug!(
-                                "lower_generic_arg: Lowering type argument as const argument: {:?}",
-                                ty,
-                            );
-
-                            // Construct a AnonConst where the expr is the "ty"'s path.
-
-                            let parent_def_id = self.current_hir_id_owner.0;
-                            let node_id = self.resolver.next_node_id();
-
-                            // Add a definition for the in-band const def.
-                            self.resolver.create_def(
-                                parent_def_id,
-                                node_id,
-                                DefPathData::AnonConst,
-                                ExpnId::root(),
-                                ty.span,
-                            );
-
-                            let path_expr = Expr {
-                                id: ty.id,
-                                kind: ExprKind::Path(qself.clone(), path.clone()),
-                                span: ty.span,
-                                attrs: AttrVec::new(),
-                                tokens: None,
-                            };
-
-                            let ct = self.with_new_scopes(|this| hir::AnonConst {
-                                hir_id: this.lower_node_id(node_id),
-                                body: this.lower_const_body(path_expr.span, Some(&path_expr)),
-                            });
-                            return GenericArg::Const(ConstArg { value: ct, span: ty.span });
+                match ty.kind {
+                    TyKind::Infer if self.sess.features_untracked().generic_arg_infer => {
+                        let hir_id = self.lower_node_id(ty.id);
+                        return GenericArg::Infer(hir::InferArg {
+                            hir_id,
+                            span: ty.span,
+                            kind: InferKind::Type,
+                        });
+                    }
+                    // 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.
+                    TyKind::Path(ref qself, ref path) => {
+                        if let Some(partial_res) = self.resolver.get_partial_res(ty.id) {
+                            let res = partial_res.base_res();
+                            if !res.matches_ns(Namespace::TypeNS) {
+                                debug!(
+                                    "lower_generic_arg: Lowering type argument as const argument: {:?}",
+                                    ty,
+                                );
+
+                                // Construct a AnonConst where the expr is the "ty"'s path.
+
+                                let parent_def_id = self.current_hir_id_owner.0;
+                                let node_id = self.resolver.next_node_id();
+
+                                // Add a definition for the in-band const def.
+                                self.resolver.create_def(
+                                    parent_def_id,
+                                    node_id,
+                                    DefPathData::AnonConst,
+                                    ExpnId::root(),
+                                    ty.span,
+                                );
+
+                                let path_expr = Expr {
+                                    id: ty.id,
+                                    kind: ExprKind::Path(qself.clone(), path.clone()),
+                                    span: ty.span,
+                                    attrs: AttrVec::new(),
+                                    tokens: None,
+                                };
+
+                                let ct = self.with_new_scopes(|this| hir::AnonConst {
+                                    hir_id: this.lower_node_id(node_id),
+                                    body: this.lower_const_body(path_expr.span, Some(&path_expr)),
+                                });
+                                return GenericArg::Const(ConstArg { value: ct, span: ty.span });
+                            }
                         }
                     }
+                    _ => {}
                 }
                 GenericArg::Type(self.lower_ty_direct(&ty, itctx))
             }
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 952fe3f9f81..803e4a2e59d 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -680,6 +680,9 @@ declare_features! (
     /// Allows `cfg(target_abi = "...")`.
     (active, cfg_target_abi, "1.55.0", Some(80970), None),
 
+    /// Infer generic args for both consts and types.
+    (active, generic_arg_infer, "1.55.0", Some(85077), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 389d1e5899f..db7fe6cb12f 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -254,11 +254,38 @@ pub struct ConstArg {
     pub span: Span,
 }
 
+#[derive(Copy, Clone, Encodable, Debug, HashStable_Generic)]
+pub enum InferKind {
+    Const,
+    Type,
+}
+
+impl InferKind {
+    #[inline]
+    pub fn is_type(self) -> bool {
+        matches!(self, InferKind::Type)
+    }
+}
+
+#[derive(Encodable, Debug, HashStable_Generic)]
+pub struct InferArg {
+    pub hir_id: HirId,
+    pub kind: InferKind,
+    pub span: Span,
+}
+
+impl InferArg {
+    pub fn to_ty(&self) -> Ty<'_> {
+        Ty { kind: TyKind::Infer, span: self.span, hir_id: self.hir_id }
+    }
+}
+
 #[derive(Debug, HashStable_Generic)]
 pub enum GenericArg<'hir> {
     Lifetime(Lifetime),
     Type(Ty<'hir>),
     Const(ConstArg),
+    Infer(InferArg),
 }
 
 impl GenericArg<'_> {
@@ -267,6 +294,7 @@ impl GenericArg<'_> {
             GenericArg::Lifetime(l) => l.span,
             GenericArg::Type(t) => t.span,
             GenericArg::Const(c) => c.span,
+            GenericArg::Infer(i) => i.span,
         }
     }
 
@@ -275,6 +303,7 @@ impl GenericArg<'_> {
             GenericArg::Lifetime(l) => l.hir_id,
             GenericArg::Type(t) => t.hir_id,
             GenericArg::Const(c) => c.value.hir_id,
+            GenericArg::Infer(i) => i.hir_id,
         }
     }
 
@@ -291,6 +320,7 @@ impl GenericArg<'_> {
             GenericArg::Lifetime(_) => "lifetime",
             GenericArg::Type(_) => "type",
             GenericArg::Const(_) => "constant",
+            GenericArg::Infer(_) => "inferred",
         }
     }
 
@@ -301,6 +331,7 @@ impl GenericArg<'_> {
             GenericArg::Const(_) => {
                 ast::ParamKindOrd::Const { unordered: feats.unordered_const_ty_params() }
             }
+            GenericArg::Infer(_) => ast::ParamKindOrd::Infer,
         }
     }
 }
@@ -342,27 +373,36 @@ impl GenericArgs<'_> {
                         break;
                     }
                     GenericArg::Const(_) => {}
+                    GenericArg::Infer(_) => {}
                 }
             }
         }
         panic!("GenericArgs::inputs: not a `Fn(T) -> U`");
     }
 
-    pub fn own_counts(&self) -> GenericParamCount {
-        // We could cache this as a property of `GenericParamCount`, but
-        // the aim is to refactor this away entirely eventually and the
-        // presence of this method will be a constant reminder.
-        let mut own_counts: GenericParamCount = Default::default();
+    #[inline]
+    pub fn has_type_params(&self) -> bool {
+        self.args.iter().any(|arg| matches!(arg, GenericArg::Type(_)))
+    }
 
-        for arg in self.args {
-            match arg {
-                GenericArg::Lifetime(_) => own_counts.lifetimes += 1,
-                GenericArg::Type(_) => own_counts.types += 1,
-                GenericArg::Const(_) => own_counts.consts += 1,
-            };
-        }
+    #[inline]
+    pub fn num_type_params(&self) -> usize {
+        self.args.iter().filter(|arg| matches!(arg, GenericArg::Type(_))).count()
+    }
 
-        own_counts
+    #[inline]
+    pub fn num_lifetime_params(&self) -> usize {
+        self.args.iter().filter(|arg| matches!(arg, GenericArg::Lifetime(_))).count()
+    }
+
+    #[inline]
+    pub fn has_lifetime_params(&self) -> bool {
+        self.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
+    }
+
+    #[inline]
+    pub fn num_generic_params(&self) -> usize {
+        self.args.iter().filter(|arg| !matches!(arg, GenericArg::Lifetime(_))).count()
     }
 
     /// The span encompassing the text inside the surrounding brackets.
@@ -485,6 +525,7 @@ pub struct GenericParamCount {
     pub lifetimes: usize,
     pub types: usize,
     pub consts: usize,
+    pub infer: usize,
 }
 
 /// Represents lifetimes and type parameters attached to a declaration
@@ -3130,6 +3171,8 @@ pub enum Node<'hir> {
     Visibility(&'hir Visibility<'hir>),
 
     Crate(&'hir Mod<'hir>),
+
+    Infer(&'hir InferArg),
 }
 
 impl<'hir> Node<'hir> {
@@ -3198,6 +3241,7 @@ impl<'hir> Node<'hir> {
             | Node::Local(Local { hir_id, .. })
             | Node::Lifetime(Lifetime { hir_id, .. })
             | Node::Param(Param { hir_id, .. })
+            | Node::Infer(InferArg { hir_id, .. })
             | Node::GenericParam(GenericParam { hir_id, .. }) => Some(*hir_id),
             Node::TraitRef(TraitRef { hir_ref_id, .. }) => Some(*hir_ref_id),
             Node::PathSegment(PathSegment { hir_id, .. }) => *hir_id,
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 711b62c4a31..17835493cda 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -436,11 +436,15 @@ pub trait Visitor<'v>: Sized {
     fn visit_label(&mut self, label: &'v Label) {
         walk_label(self, label)
     }
+    fn visit_infer(&mut self, inf: &'v InferArg) {
+        walk_inf(self, inf);
+    }
     fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) {
         match generic_arg {
             GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
             GenericArg::Type(ty) => self.visit_ty(ty),
             GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
+            GenericArg::Infer(inf) => self.visit_infer(inf),
         }
     }
     fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
@@ -747,6 +751,10 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
     }
 }
 
+pub fn walk_inf<'v, V: Visitor<'v>>(visitor: &mut V, inf: &'v InferArg) {
+    visitor.visit_id(inf.hir_id);
+}
+
 pub fn walk_qpath<'v, V: Visitor<'v>>(
     visitor: &mut V,
     qpath: &'v QPath<'v>,
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 4177c2f8d52..90ceb1d5c91 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -103,6 +103,7 @@ impl<'a> State<'a> {
             Node::TraitRef(a) => self.print_trait_ref(&a),
             Node::Binding(a) | Node::Pat(a) => self.print_pat(&a),
             Node::Arm(a) => self.print_arm(&a),
+            Node::Infer(_) => self.s.word("_"),
             Node::Block(a) => {
                 // Containing cbox, will be closed by print-block at `}`.
                 self.cbox(INDENT_UNIT);
@@ -437,14 +438,14 @@ impl<'a> State<'a> {
                 self.print_anon_const(e);
                 self.s.word(")");
             }
-            hir::TyKind::Infer => {
-                self.s.word("_");
-            }
             hir::TyKind::Err => {
                 self.popen();
                 self.s.word("/*ERROR*/");
                 self.pclose();
             }
+            hir::TyKind::Infer => {
+                self.s.word("_");
+            }
         }
         self.end()
     }
@@ -1851,6 +1852,7 @@ impl<'a> State<'a> {
                         GenericArg::Lifetime(_) => {}
                         GenericArg::Type(ty) => s.print_type(ty),
                         GenericArg::Const(ct) => s.print_anon_const(&ct.value),
+                        GenericArg::Infer(_inf) => s.word("_"),
                     },
                 );
             }
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index e504662bb5e..052efa851f7 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -244,6 +244,11 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
         hir_visit::walk_ty(self, t);
     }
 
+    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
+        lint_callback!(self, check_infer, inf);
+        hir_visit::walk_inf(self, inf);
+    }
+
     fn visit_name(&mut self, sp: Span, name: Symbol) {
         lint_callback!(self, check_name, sp, name);
     }
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index bbe17dcf4b7..2d047ac7a08 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -33,6 +33,7 @@ macro_rules! late_lint_methods {
             fn check_expr(a: &$hir hir::Expr<$hir>);
             fn check_expr_post(a: &$hir hir::Expr<$hir>);
             fn check_ty(a: &$hir hir::Ty<$hir>);
+            fn check_infer(a: &$hir hir::InferArg);
             fn check_generic_arg(a: &$hir hir::GenericArg<$hir>);
             fn check_generic_param(a: &$hir hir::GenericParam<$hir>);
             fn check_generics(a: &$hir hir::Generics<$hir>);
diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs
index 0eb0c3eca4e..5c166c74004 100644
--- a/compiler/rustc_middle/src/hir/map/collector.rs
+++ b/compiler/rustc_middle/src/hir/map/collector.rs
@@ -336,6 +336,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
         });
     }
 
+    fn visit_infer(&mut self, inf: &'hir InferArg) {
+        self.insert(inf.span, inf.hir_id, Node::Infer(inf));
+
+        self.with_parent(inf.hir_id, |this| {
+            intravisit::walk_inf(this, inf);
+        });
+    }
+
     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_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 1a3d875c5ba..b1606ef8f63 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -276,6 +276,7 @@ impl<'hir> Map<'hir> {
             Node::Stmt(_)
             | Node::PathSegment(_)
             | Node::Ty(_)
+            | Node::Infer(_)
             | Node::TraitRef(_)
             | Node::Pat(_)
             | Node::Binding(_)
@@ -871,6 +872,7 @@ impl<'hir> Map<'hir> {
                 node: VisibilityKind::Restricted { ref path, .. },
                 ..
             }) => path.span,
+            Node::Infer(i) => i.span,
             Node::Visibility(v) => bug!("unexpected Visibility {:?}", v),
             Node::Local(local) => local.span,
             Node::MacroDef(macro_def) => macro_def.span,
@@ -1118,6 +1120,7 @@ fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String {
         Some(Node::Param(_)) => node_str("param"),
         Some(Node::Arm(_)) => node_str("arm"),
         Some(Node::Block(_)) => node_str("block"),
+        Some(Node::Infer(_)) => node_str("infer"),
         Some(Node::Local(_)) => node_str("local"),
         Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str),
         Some(Node::Lifetime(_)) => node_str("lifetime"),
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index d969f50c1d9..cd91ecdf2ba 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -1188,6 +1188,14 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
         self.maybe_typeck_results = old_maybe_typeck_results;
     }
 
+    fn visit_generic_arg(&mut self, generic_arg: &'tcx hir::GenericArg<'tcx>) {
+        match generic_arg {
+            hir::GenericArg::Type(t) => self.visit_ty(t),
+            hir::GenericArg::Infer(inf) => self.visit_infer(inf),
+            hir::GenericArg::Lifetime(_) | hir::GenericArg::Const(_) => {}
+        }
+    }
+
     fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
         self.span = hir_ty.span;
         if let Some(typeck_results) = self.maybe_typeck_results {
@@ -1207,6 +1215,30 @@ 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;
+        if let Some(typeck_results) = self.maybe_typeck_results {
+            if let Some(ty) = typeck_results.node_type_opt(inf.hir_id) {
+                if self.visit(ty).is_break() {
+                    return;
+                }
+            }
+        } else {
+            let local_id = self.tcx.hir().local_def_id(inf.hir_id);
+            if let Some(did) = self.tcx.opt_const_param_of(local_id) {
+                if self.visit_def_id(did, "inferred", &"").is_break() {
+                    return;
+                }
+            }
+
+            // FIXME see above note for same issue.
+            if self.visit(rustc_typeck::hir_ty_to_ty(self.tcx, &inf.to_ty())).is_break() {
+                return;
+            }
+        }
+        intravisit::walk_inf(self, inf);
+    }
+
     fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef<'tcx>) {
         self.span = trait_ref.path.span;
         if self.maybe_typeck_results.is_none() {
@@ -1443,6 +1475,14 @@ impl<'a, 'b, 'tcx, 'v> Visitor<'v> for ObsoleteCheckTypeForPrivatenessVisitor<'a
         NestedVisitorMap::None
     }
 
+    fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) {
+        match generic_arg {
+            hir::GenericArg::Type(t) => self.visit_ty(t),
+            hir::GenericArg::Infer(inf) => self.visit_ty(&inf.to_ty()),
+            hir::GenericArg::Lifetime(_) | hir::GenericArg::Const(_) => {}
+        }
+    }
+
     fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
         if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = ty.kind {
             if self.inner.path_is_private_type(path) {
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 43294774042..e18dae0b8ee 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -2550,6 +2550,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                 GenericArg::Const(ct) => {
                     self.visit_anon_const(&ct.value);
                 }
+                GenericArg::Infer(inf) => {
+                    self.visit_id(inf.hir_id);
+                    if inf.kind.is_type() {
+                        i += 1;
+                    }
+                }
             }
         }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index c0f63f40853..536ebdef426 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -622,6 +622,7 @@ symbols! {
         generator,
         generator_state,
         generators,
+        generic_arg_infer,
         generic_associated_types,
         generic_param_attrs,
         get_context,
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 456f2a908a8..eb6265dec89 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -39,8 +39,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         );
 
         if let GenericParamDefKind::Const { .. } = param.kind {
-            if let GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. }) = arg {
+            if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) {
                 err.help("const arguments cannot yet be inferred with `_`");
+                if sess.is_nightly_build() {
+                    err.help(
+                        "add `#![feature(generic_arg_infer)]` to the crate attributes to enable",
+                    );
+                }
             }
         }
 
@@ -249,14 +254,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     (Some(&arg), Some(&param)) => {
                         match (arg, &param.kind, arg_count.explicit_late_bound) {
                             (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _)
-                            | (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _)
-                            | (GenericArg::Const(_), GenericParamDefKind::Const { .. }, _) => {
+                            | (
+                                GenericArg::Type(_) | GenericArg::Infer(_),
+                                GenericParamDefKind::Type { .. },
+                                _,
+                            )
+                            | (
+                                GenericArg::Const(_) | GenericArg::Infer(_),
+                                GenericParamDefKind::Const { .. },
+                                _,
+                            ) => {
                                 substs.push(ctx.provided_kind(param, arg));
                                 args.next();
                                 params.next();
                             }
                             (
-                                GenericArg::Type(_) | GenericArg::Const(_),
+                                GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_),
                                 GenericParamDefKind::Lifetime,
                                 _,
                             ) => {
@@ -325,6 +338,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                                                     .features()
                                                     .unordered_const_ty_params(),
                                             },
+                                            GenericArg::Infer(_) => ParamKindOrd::Infer,
                                         }),
                                         Some(&format!(
                                             "reorder the arguments: {}: `<{}>`",
@@ -446,8 +460,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let default_counts = gen_params.own_defaults();
         let param_counts = gen_params.own_counts();
         let named_type_param_count = param_counts.types - has_self as usize;
-        let arg_counts = gen_args.own_counts();
-        let infer_lifetimes = gen_pos != GenericArgPosition::Type && arg_counts.lifetimes == 0;
+        let infer_lifetimes =
+            gen_pos != GenericArgPosition::Type && !gen_args.has_lifetime_params();
 
         if gen_pos != GenericArgPosition::Type && !gen_args.bindings.is_empty() {
             Self::prohibit_assoc_ty_binding(tcx, gen_args.bindings[0].span);
@@ -505,7 +519,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes };
         let max_expected_lifetime_args = param_counts.lifetimes;
-        let num_provided_lifetime_args = arg_counts.lifetimes;
+        let num_provided_lifetime_args = gen_args.num_lifetime_params();
 
         let lifetimes_correct = check_lifetime_args(
             min_expected_lifetime_args,
@@ -576,14 +590,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     - default_counts.consts
             };
             debug!("expected_min: {:?}", expected_min);
-            debug!("arg_counts.lifetimes: {:?}", arg_counts.lifetimes);
+            debug!("arg_counts.lifetimes: {:?}", gen_args.num_lifetime_params());
 
             check_types_and_consts(
                 expected_min,
                 param_counts.consts + named_type_param_count,
-                arg_counts.consts + arg_counts.types,
+                gen_args.num_generic_params(),
                 param_counts.lifetimes + has_self as usize,
-                arg_counts.lifetimes,
+                gen_args.num_lifetime_params(),
             )
         };
 
@@ -622,7 +636,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 .args
                 .iter()
                 .filter_map(|arg| match arg {
-                    GenericArg::Type(_) | GenericArg::Const(_) => Some(arg.span()),
+                    GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_) => {
+                        Some(arg.span())
+                    }
                     _ => None,
                 })
                 .collect::<Vec<_>>();
@@ -659,8 +675,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         position: GenericArgPosition,
     ) -> ExplicitLateBound {
         let param_counts = def.own_counts();
-        let arg_counts = args.own_counts();
-        let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0;
+        let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params();
 
         if infer_lifetimes {
             return ExplicitLateBound::No;
@@ -673,7 +688,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             let span = args.args[0].span();
 
             if position == GenericArgPosition::Value
-                && arg_counts.lifetimes != param_counts.lifetimes
+                && args.num_lifetime_params() != param_counts.lifetimes
             {
                 let mut err = tcx.sess.struct_span_err(span, msg);
                 err.span_note(span_late, note);
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index b82437120ce..9871b14754e 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -461,6 +461,43 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         )
                         .into()
                     }
+                    (&GenericParamDefKind::Const { has_default }, hir::GenericArg::Infer(inf)) => {
+                        if has_default {
+                            tcx.const_param_default(param.def_id).into()
+                        } else if self.astconv.allow_ty_infer() {
+                            // FIXME(const_generics): Actually infer parameter here?
+                            todo!()
+                        } else {
+                            self.inferred_params.push(inf.span);
+                            tcx.ty_error().into()
+                        }
+                    }
+                    (
+                        &GenericParamDefKind::Type { has_default, .. },
+                        hir::GenericArg::Infer(inf),
+                    ) => {
+                        if has_default {
+                            tcx.check_optional_stability(
+                                param.def_id,
+                                Some(arg.id()),
+                                arg.span(),
+                                None,
+                                |_, _| {
+                                    // Default generic parameters may not be marked
+                                    // with stability attributes, i.e. when the
+                                    // default parameter was defined at the same time
+                                    // as the rest of the type. As such, we ignore missing
+                                    // stability attributes.
+                                },
+                            );
+                        }
+                        if self.astconv.allow_ty_infer() {
+                            self.astconv.ast_ty_to_ty(&inf.to_ty()).into()
+                        } else {
+                            self.inferred_params.push(inf.span);
+                            tcx.ty_error().into()
+                        }
+                    }
                     _ => unreachable!(),
                 }
             }
@@ -1930,6 +1967,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         has_err = true;
                         (ct.span, "const")
                     }
+                    hir::GenericArg::Infer(inf) => {
+                        if err_for_ty {
+                            continue;
+                        }
+                        has_err = true;
+                        err_for_ty = true;
+                        (inf.span, "generic")
+                    }
                 };
                 let mut err = struct_span_err!(
                     self.tcx().sess,
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index 865e4ccc0b6..f5776ae7cf6 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -581,6 +581,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> {
+        match self.typeck_results.borrow().node_types().get(id) {
+            Some(&t) => Some(t),
+            None if self.is_tainted_by_errors() => Some(self.tcx.ty_error()),
+            None => None,
+        }
+    }
+
     /// Registers an obligation for checking later, during regionck, that `arg` is well-formed.
     pub fn register_wf_obligation(
         &self,
@@ -1471,6 +1479,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
                         self.fcx.const_arg_to_const(&ct.value, param.def_id).into()
                     }
+                    (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
+                        self.fcx.ty_infer(Some(param), inf.span).into()
+                    }
+                    (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
+                        let tcx = self.fcx.tcx();
+                        self.fcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into()
+                    }
                     _ => unreachable!(),
                 }
             }
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs
index 75299bae5dd..3aceaba882d 100644
--- a/compiler/rustc_typeck/src/check/method/confirm.rs
+++ b/compiler/rustc_typeck/src/check/method/confirm.rs
@@ -366,6 +366,13 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                     (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
                         self.cfcx.const_arg_to_const(&ct.value, param.def_id).into()
                     }
+                    (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
+                        self.cfcx.ty_infer(Some(param), inf.span).into()
+                    }
+                    (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
+                        let tcx = self.cfcx.tcx();
+                        self.cfcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into()
+                    }
                     _ => unreachable!(),
                 }
             }
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index 935bcc9f32e..e6eac16667d 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -331,6 +331,15 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
         let ty = self.resolve(ty, &hir_ty.span);
         self.write_ty_to_typeck_results(hir_ty.hir_id, ty);
     }
+
+    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);
+        }
+    }
 }
 
 impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 46b3ec5cf40..997fdcefe03 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -129,6 +129,16 @@ impl<'v> Visitor<'v> for PlaceholderHirTyCollector {
         }
         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.0.push(inf.span);
+                intravisit::walk_inf(self, inf);
+            }
+            hir::GenericArg::Type(t) => self.visit_ty(t),
+            _ => {}
+        }
+    }
 }
 
 struct CollectItemTypesVisitor<'tcx> {
@@ -1714,13 +1724,11 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
 }
 
 fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool {
-    generic_args
-        .iter()
-        .filter_map(|arg| match arg {
-            hir::GenericArg::Type(ty) => Some(ty),
-            _ => None,
-        })
-        .any(is_suggestable_infer_ty)
+    generic_args.iter().any(|arg| match arg {
+        hir::GenericArg::Type(ty) => is_suggestable_infer_ty(ty),
+        hir::GenericArg::Infer(_) => true,
+        _ => false,
+    })
 }
 
 /// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 50e4ba4fe6c..96b3fa9aa01 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -20,6 +20,9 @@ use super::{bad_placeholder_type, is_suggestable_infer_ty};
 ///
 /// This should be called using the query `tcx.opt_const_param_of`.
 pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
+    // FIXME(generic_arg_infer): allow for returning DefIds of inference of
+    // GenericArg::Infer below. This may require a change where GenericArg::Infer has some flag
+    // for const or type.
     use hir::*;
     let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 1a2852dc6c7..27c2f6cc87e 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1768,6 +1768,7 @@ impl Clean<GenericArgs> for hir::GenericArgs<'_> {
                         hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
                         hir::GenericArg::Type(ty) => GenericArg::Type(ty.clean(cx)),
                         hir::GenericArg::Const(ct) => GenericArg::Const(ct.clean(cx)),
+                        hir::GenericArg::Infer(_inf) => GenericArg::Infer,
                     })
                     .collect(),
                 bindings: self.bindings.clean(cx),
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 101d3b400c3..2460fa127f1 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -2007,6 +2007,7 @@ crate enum GenericArg {
     Lifetime(Lifetime),
     Type(Type),
     Const(Constant),
+    Infer,
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 08499cef33e..cd74006530b 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1424,6 +1424,7 @@ impl clean::GenericArg {
             clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
             clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
             clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
+            clean::GenericArg::Infer => fmt::Display::fmt("_", f),
         })
     }
 }
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index bf8db79416b..f3eeea6c6ae 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -140,6 +140,7 @@ impl FromWithTcx<clean::GenericArg> for GenericArg {
             Lifetime(l) => GenericArg::Lifetime(l.0.to_string()),
             Type(t) => GenericArg::Type(t.into_tcx(tcx)),
             Const(c) => GenericArg::Const(c.into_tcx(tcx)),
+            Infer => GenericArg::Infer,
         }
     }
 }
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 6d9a5cb515a..38ba87322c2 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -127,6 +127,7 @@ pub enum GenericArg {
     Lifetime(String),
     Type(Type),
     Const(Constant),
+    Infer,
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
diff --git a/src/test/ui/const-generics/issues/issue-62878.full.stderr b/src/test/ui/const-generics/issues/issue-62878.full.stderr
index 6e6aa196633..08f6454fa2d 100644
--- a/src/test/ui/const-generics/issues/issue-62878.full.stderr
+++ b/src/test/ui/const-generics/issues/issue-62878.full.stderr
@@ -4,13 +4,13 @@ error[E0770]: the type of const parameters must not depend on other generic para
 LL | fn foo<const N: usize, const A: [u8; N]>() {}
    |                                      ^ the type must not depend on the parameter `N`
 
-error[E0747]: type provided when a constant was expected
-  --> $DIR/issue-62878.rs:10:11
+error: constant expression depends on a generic parameter
+  --> $DIR/issue-62878.rs:10:14
    |
 LL |     foo::<_, {[1]}>();
-   |           ^
+   |              ^^^^^
    |
-   = help: const arguments cannot yet be inferred with `_`
+   = note: this may fail depending on what value the parameter takes
 
 error[E0308]: mismatched types
   --> $DIR/issue-62878.rs:10:15
@@ -20,5 +20,5 @@ LL |     foo::<_, {[1]}>();
 
 error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0308, E0747, E0770.
+Some errors have detailed explanations: E0308, E0770.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/const-generics/issues/issue-62878.rs b/src/test/ui/const-generics/issues/issue-62878.rs
index a70606c4a7d..fb6257696b9 100644
--- a/src/test/ui/const-generics/issues/issue-62878.rs
+++ b/src/test/ui/const-generics/issues/issue-62878.rs
@@ -1,5 +1,5 @@
 // revisions: full min
-#![cfg_attr(full, feature(const_generics))]
+#![cfg_attr(full, feature(const_generics, generic_arg_infer))]
 #![cfg_attr(full, allow(incomplete_features))]
 
 fn foo<const N: usize, const A: [u8; N]>() {}
@@ -8,6 +8,6 @@ fn foo<const N: usize, const A: [u8; N]>() {}
 
 fn main() {
     foo::<_, {[1]}>();
-    //[full]~^ ERROR type provided when a constant was expected
-    //[full]~| ERROR mismatched types
+    //[full]~^ ERROR mismatched types
+    //[full]~| ERROR constant expression
 }
diff --git a/src/test/ui/const-generics/min_const_generics/inferred_const.rs b/src/test/ui/const-generics/min_const_generics/inferred_const.rs
index dcd069ce3b0..57d6941587a 100644
--- a/src/test/ui/const-generics/min_const_generics/inferred_const.rs
+++ b/src/test/ui/const-generics/min_const_generics/inferred_const.rs
@@ -1,8 +1,9 @@
-#![feature(min_const_generics)]
-fn foo<const N: usize, const K: usize>(data: [u32; N]) -> [u32; K] {
+#![feature(generic_arg_infer)]
+// run-pass
+
+fn foo<const N: usize, const K: usize>(_data: [u32; N]) -> [u32; K] {
     [0; K]
 }
 fn main() {
-    let a = foo::<_, 2>([0, 1, 2]);
-               //~^ ERROR type provided when a constant was expected
+    let _a = foo::<_, 2>([0, 1, 2]);
 }
diff --git a/src/test/ui/const-generics/min_const_generics/inferred_const.stderr b/src/test/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
index e17105b2aa9..c17f02d58f3 100644
--- a/src/test/ui/const-generics/min_const_generics/inferred_const.stderr
+++ b/src/test/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr
@@ -1,10 +1,11 @@
 error[E0747]: type provided when a constant was expected
-  --> $DIR/inferred_const.rs:6:19
+  --> $DIR/feature-gate-generic_arg_infer.rs:11:20
    |
-LL |     let a = foo::<_, 2>([0, 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
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/feature-gates/feature-gate-generic_arg_infer.rs b/src/test/ui/feature-gates/feature-gate-generic_arg_infer.rs
new file mode 100644
index 00000000000..4729773b12e
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-generic_arg_infer.rs
@@ -0,0 +1,13 @@
+// [feature] run-pass
+// revisions: normal feature
+
+#![cfg_attr(feature, feature(generic_arg_infer))]
+
+fn foo<const N: usize>(_: [u8; N]) -> [u8; N] {
+  [0; N]
+}
+
+fn main() {
+    let _x = foo::<_>([1,2]);
+    //[normal]~^ ERROR: type provided when a constant was expected
+}
diff --git a/src/test/ui/inference/infer-arg-test.rs b/src/test/ui/inference/infer-arg-test.rs
new file mode 100644
index 00000000000..1b67ccd6c43
--- /dev/null
+++ b/src/test/ui/inference/infer-arg-test.rs
@@ -0,0 +1,24 @@
+#![feature(generic_arg_infer)]
+
+struct All<'a, T, const N: usize> {
+  v: &'a T,
+}
+
+struct BadInfer<_>;
+//~^ ERROR expected identifier
+//~| ERROR parameter `_` is never used
+
+fn all_fn<'a, T, const N: usize>() {}
+
+fn bad_infer_fn<_>() {}
+//~^ ERROR expected identifier
+
+
+fn main() {
+  let a: All<_, _, _>;
+  all_fn();
+  let v: [u8; _];
+  //~^ ERROR in expressions
+  let v: [u8; 10] = [0; _];
+  //~^ ERROR in expressions
+}
diff --git a/src/test/ui/inference/infer-arg-test.stderr b/src/test/ui/inference/infer-arg-test.stderr
new file mode 100644
index 00000000000..30e171eac21
--- /dev/null
+++ b/src/test/ui/inference/infer-arg-test.stderr
@@ -0,0 +1,36 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/infer-arg-test.rs:7:17
+   |
+LL | struct BadInfer<_>;
+   |                 ^ expected identifier, found reserved identifier
+
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/infer-arg-test.rs:13:17
+   |
+LL | fn bad_infer_fn<_>() {}
+   |                 ^ expected identifier, found reserved identifier
+
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/infer-arg-test.rs:20:15
+   |
+LL |   let v: [u8; _];
+   |               ^ `_` not allowed here
+
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/infer-arg-test.rs:22:25
+   |
+LL |   let v: [u8; 10] = [0; _];
+   |                         ^ `_` not allowed here
+
+error[E0392]: parameter `_` is never used
+  --> $DIR/infer-arg-test.rs:7:17
+   |
+LL | struct BadInfer<_>;
+   |                 ^ unused parameter
+   |
+   = help: consider removing `_`, referring to it in a field, or using a marker such as `PhantomData`
+   = help: if you intended `_` to be a const parameter, use `const _: usize` instead
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0392`.
diff --git a/src/test/ui/object-lifetime-default-inferred.rs b/src/test/ui/object-lifetime-default-inferred.rs
new file mode 100644
index 00000000000..8a1156b8fc8
--- /dev/null
+++ b/src/test/ui/object-lifetime-default-inferred.rs
@@ -0,0 +1,35 @@
+// run-pass
+// Test that even with prior inferred parameters, object lifetimes of objects after are still
+// valid.
+
+// pretty-expanded FIXME #23616
+
+#![allow(dead_code)]
+#![feature(generic_arg_infer)]
+
+trait Test {
+    fn foo(&self) { }
+}
+
+struct Foo;
+impl Test for Foo {}
+
+struct SomeStruct<'a> {
+    t: &'a dyn Test,
+    u: &'a (dyn Test+'a),
+}
+
+fn a<'a, const N: usize>(_: [u8; N], t: &'a (dyn Test+'a), mut ss: SomeStruct<'a>) {
+    ss.t = t;
+}
+
+fn b<'a, T>(_: T, t: &'a (dyn Test+'a), mut ss: SomeStruct<'a>) {
+    ss.u = t;
+}
+
+fn main() {
+    // Inside a function body, we can just infer both
+    // lifetimes, to allow &'tmp (Display+'static).
+    a::<_>([], &Foo as &dyn Test, SomeStruct{t:&Foo,u:&Foo});
+    b::<_>(0u8, &Foo as &dyn Test, SomeStruct{t:&Foo,u:&Foo});
+}
diff --git a/src/test/ui/parser/issue-14303-fncall.stderr b/src/test/ui/parser/issue-14303-fncall.full.stderr
index cdda0d001c7..02af61e8539 100644
--- a/src/test/ui/parser/issue-14303-fncall.stderr
+++ b/src/test/ui/parser/issue-14303-fncall.full.stderr
@@ -1,5 +1,5 @@
 error[E0747]: type provided when a lifetime was expected
-  --> $DIR/issue-14303-fncall.rs:13:26
+  --> $DIR/issue-14303-fncall.rs:16:26
    |
 LL |         .collect::<Vec<S<_, 'a>>>();
    |                          ^
diff --git a/src/test/ui/parser/issue-14303-fncall.generic_arg.stderr b/src/test/ui/parser/issue-14303-fncall.generic_arg.stderr
new file mode 100644
index 00000000000..9f3359b3f68
--- /dev/null
+++ b/src/test/ui/parser/issue-14303-fncall.generic_arg.stderr
@@ -0,0 +1,9 @@
+error[E0747]: inferred provided when a lifetime was expected
+  --> $DIR/issue-14303-fncall.rs:16:26
+   |
+LL |         .collect::<Vec<S<_, 'a>>>();
+   |                          ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0747`.
diff --git a/src/test/ui/parser/issue-14303-fncall.rs b/src/test/ui/parser/issue-14303-fncall.rs
index 46ece84d69e..976a79a59b1 100644
--- a/src/test/ui/parser/issue-14303-fncall.rs
+++ b/src/test/ui/parser/issue-14303-fncall.rs
@@ -1,6 +1,9 @@
-// can't run rustfix because it doesn't handle multipart suggestions correctly
+// revisions: full generic_arg
 // compile-flags: -Zborrowck=mir
+// can't run rustfix because it doesn't handle multipart suggestions correctly
 // 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,
@@ -11,7 +14,8 @@ 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>>>();
-        //~^ ERROR type provided when a lifetime was expected
+        //[generic_arg]~^ ERROR inferred provided when a lifetime was expected
+        //[full]~^^ ERROR type provided when a lifetime was expected
 }
 
 fn main() {}
diff --git a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr b/src/test/ui/span/issue-42234-unknown-receiver-type.full.stderr
index c64c5b1c28f..278d0492244 100644
--- a/src/test/ui/span/issue-42234-unknown-receiver-type.stderr
+++ b/src/test/ui/span/issue-42234-unknown-receiver-type.full.stderr
@@ -1,5 +1,5 @@
 error[E0282]: type annotations needed for `Option<_>`
-  --> $DIR/issue-42234-unknown-receiver-type.rs:7:7
+  --> $DIR/issue-42234-unknown-receiver-type.rs:10:7
    |
 LL |     let x: Option<_> = None;
    |         - consider giving `x` the explicit type `Option<_>`, where the type parameter `T` is specified
@@ -9,7 +9,7 @@ LL |     x.unwrap().method_that_could_exist_on_some_type();
    = note: type must be known at this point
 
 error[E0282]: type annotations needed
-  --> $DIR/issue-42234-unknown-receiver-type.rs:13:10
+  --> $DIR/issue-42234-unknown-receiver-type.rs:16:10
    |
 LL |         .sum::<_>()
    |          ^^^ cannot infer type
diff --git a/src/test/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr b/src/test/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr
new file mode 100644
index 00000000000..8d4ed4aea6a
--- /dev/null
+++ b/src/test/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr
@@ -0,0 +1,21 @@
+error[E0282]: type annotations needed for `Option<_>`
+  --> $DIR/issue-42234-unknown-receiver-type.rs:10:7
+   |
+LL |     let x: Option<_> = None;
+   |         - consider giving `x` the explicit type `Option<_>`, where the type parameter `T` is specified
+LL |     x.unwrap().method_that_could_exist_on_some_type();
+   |       ^^^^^^ cannot infer type for type parameter `T`
+   |
+   = note: type must be known at this point
+
+error[E0282]: type annotations needed
+  --> $DIR/issue-42234-unknown-receiver-type.rs:16:16
+   |
+LL |         .sum::<_>()
+   |                ^ cannot infer type for type parameter `S` declared on the associated function `sum`
+   |
+   = note: type must be known at this point
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/span/issue-42234-unknown-receiver-type.rs b/src/test/ui/span/issue-42234-unknown-receiver-type.rs
index d3292bbecba..15b00de44b9 100644
--- a/src/test/ui/span/issue-42234-unknown-receiver-type.rs
+++ b/src/test/ui/span/issue-42234-unknown-receiver-type.rs
@@ -1,3 +1,6 @@
+// revisions: full generic_arg
+#![cfg_attr(generic_arg, feature(generic_arg_infer))]
+
 // When the type of a method call's receiver is unknown, the span should point
 // to the receiver (and not the entire call, as was previously the case before
 // the fix of which this tests).
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index 9a040ca572a..ad4898d1ccb 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -5,7 +5,7 @@ use std::collections::BTreeMap;
 
 use rustc_errors::DiagnosticBuilder;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, walk_inf, NestedVisitorMap, Visitor};
 use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
@@ -295,6 +295,14 @@ impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
         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);
+    }
+
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
         NestedVisitorMap::None
     }
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 d8c4b67520d..b438d680d2c 100644
--- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{walk_ty, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{walk_ty, walk_inf, NestedVisitorMap, Visitor};
 use rustc_hir::{GenericParamKind, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
@@ -39,6 +39,11 @@ struct TypeComplexityVisitor {
 impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
     type Map = Map<'tcx>;
 
+    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
+      self.score += 1;
+      walk_inf(self, inf);
+    }
+
     fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
         let (add_score, sub_nest) = match ty.kind {
             // _, &x and *x have only small overhead; don't mess with nesting level
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 71117e967e3..d5ee717accd 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -8,8 +8,9 @@ use rustc_hir::{
     self as hir,
     def::{CtorOf, DefKind, Res},
     def_id::LocalDefId,
-    intravisit::{walk_ty, NestedVisitorMap, Visitor},
-    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind,
+    intravisit::{walk_ty, walk_inf, NestedVisitorMap, Visitor},
+    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path,
+    QPath, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
@@ -263,6 +264,11 @@ struct SkipTyCollector {
 impl<'tcx> Visitor<'tcx> for SkipTyCollector {
     type Map = Map<'tcx>;
 
+    fn visit_infer(&mut self, inf: &hir::InferArg) {
+      self.types_to_skip.push(inf.hir_id);
+
+      walk_inf(self, inf)
+    }
     fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
         self.types_to_skip.push(hir_ty.hir_id);
 
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index a21ad42c061..6ea360a88a6 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -288,6 +288,8 @@ impl HirEqInterExpr<'_, '_, '_> {
             (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
             (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::Infer(l_inf), GenericArg::Infer(r_inf)) =>
+              self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
             _ => false,
         }
     }
@@ -885,7 +887,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 
     pub fn hash_ty(&mut self, ty: &Ty<'_>) {
         std::mem::discriminant(&ty.kind).hash(&mut self.s);
-        match ty.kind {
+        self.hash_tykind(&ty.kind);
+    }
+
+    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
+        match ty {
             TyKind::Slice(ty) => {
                 self.hash_ty(ty);
             },
@@ -898,7 +904,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 mut_ty.mutbl.hash(&mut self.s);
             },
             TyKind::Rptr(lifetime, ref mut_ty) => {
-                self.hash_lifetime(lifetime);
+                self.hash_lifetime(*lifetime);
                 self.hash_ty(mut_ty.ty);
                 mut_ty.mutbl.hash(&mut self.s);
             },
@@ -918,7 +924,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 bfn.decl.c_variadic.hash(&mut self.s);
             },
             TyKind::Tup(ty_list) => {
-                for ty in ty_list {
+                for ty in *ty_list {
                     self.hash_ty(ty);
                 }
             },
@@ -927,7 +933,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 self.hash_generic_args(arg_list);
             },
             TyKind::TraitObject(_, lifetime, _) => {
-                self.hash_lifetime(lifetime);
+                self.hash_lifetime(*lifetime);
             },
             TyKind::Typeof(anon_const) => {
                 self.hash_body(anon_const.body);
@@ -949,6 +955,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 GenericArg::Lifetime(l) => self.hash_lifetime(l),
                 GenericArg::Type(ref ty) => self.hash_ty(ty),
                 GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
+                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
             }
         }
     }
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 523d55219ab..e914dc1c222 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -180,7 +180,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 }
 
 // FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
-// this function can be removed once the `normalizie` method does not panic when normalization does
+// this function can be removed once the `normalize` method does not panic when normalization does
 // not succeed
 /// Checks if `Ty` is normalizable. This function is useful
 /// to avoid crashes on `layout_of`.