diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 85692e109be4a..ffbcedf46292e 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -21,7 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
 use rustc_middle::traits::select;
-use rustc_middle::ty::abstract_const::AbstractConst;
+use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind};
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::relate::RelateResult;
@@ -1683,7 +1683,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     #[instrument(skip(self), level = "debug")]
     pub fn const_eval_resolve(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
+        mut param_env: ty::ParamEnv<'tcx>,
         unevaluated: ty::Unevaluated<'tcx>,
         span: Option<Span>,
     ) -> EvalToValTreeResult<'tcx> {
@@ -1694,10 +1694,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         // variables
         if substs.has_infer_types_or_consts() {
             let ac = AbstractConst::new(self.tcx, unevaluated.shrink());
-            if let Ok(None) = ac {
-                substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did);
-            } else {
-                return Err(ErrorHandled::TooGeneric);
+            match ac {
+                Ok(None) => {
+                    substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did);
+                    param_env = self.tcx.param_env(unevaluated.def.did);
+                }
+                Ok(Some(ct)) => {
+                    if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete {
+                        substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs);
+                    } else {
+                        return Err(ErrorHandled::TooGeneric);
+                    }
+                }
+                Err(guar) => return Err(ErrorHandled::Reported(guar)),
             }
         }
 
@@ -2017,3 +2026,43 @@ impl<'tcx> fmt::Debug for RegionObligation<'tcx> {
         )
     }
 }
+
+/// Replaces substs that reference param or infer variables with suitable
+/// placeholders. This function is meant to remove these param and infer
+/// substs when they're not actually needed to evaluate a constant.
+fn replace_param_and_infer_substs_with_placeholder<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    substs: SubstsRef<'tcx>,
+) -> SubstsRef<'tcx> {
+    tcx.mk_substs(substs.iter().enumerate().map(|(idx, arg)| {
+        match arg.unpack() {
+            GenericArgKind::Type(_)
+                if arg.has_param_types_or_consts() || arg.has_infer_types_or_consts() =>
+            {
+                tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
+                    universe: ty::UniverseIndex::ROOT,
+                    name: ty::BoundVar::from_usize(idx),
+                }))
+                .into()
+            }
+            GenericArgKind::Const(ct)
+                if ct.has_infer_types_or_consts() || ct.has_param_types_or_consts() =>
+            {
+                let ty = ct.ty();
+                // If the type references param or infer, replace that too...
+                if ty.has_param_types_or_consts() || ty.has_infer_types_or_consts() {
+                    bug!("const `{ct}`'s type should not reference params or types");
+                }
+                tcx.mk_const(ty::ConstS {
+                    ty,
+                    kind: ty::ConstKind::Placeholder(ty::PlaceholderConst {
+                        universe: ty::UniverseIndex::ROOT,
+                        name: ty::BoundConst { ty, var: ty::BoundVar::from_usize(idx) },
+                    }),
+                })
+                .into()
+            }
+            _ => arg,
+        }
+    }))
+}
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index decbf0133114f..254bc4ab66386 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -185,21 +185,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
         }
         let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span));
         match concrete {
-            Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() {
-                NotConstEvaluatable::MentionsInfer
-            } else if uv.has_param_types_or_consts() {
-                infcx
-                    .tcx
-                    .sess
-                    .delay_span_bug(span, &format!("unexpected `TooGeneric` for {:?}", uv));
-                NotConstEvaluatable::MentionsParam
-            } else {
-                let guar = infcx.tcx.sess.delay_span_bug(
+            Err(ErrorHandled::TooGeneric) => {
+                Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug(
                     span,
                     format!("Missing value for constant, but no error reported?"),
-                );
-                NotConstEvaluatable::Error(guar)
-            }),
+                )))
+            }
             Err(ErrorHandled::Linted) => {
                 let reported = infcx
                     .tcx
diff --git a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs
index 18f33acaabbba..5874625adff61 100644
--- a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs
+++ b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs
@@ -1,3 +1,5 @@
+// check-pass
+
 #![feature(generic_const_exprs)]
 #![allow(incomplete_features)]
 
@@ -21,11 +23,6 @@ where
 }
 
 fn main() {
-    // FIXME(generic_const_exprs): We can't correctly infer `T` which requires
-    // evaluating `{ N + 1 }` which has substs containing an inference var
     let mut _q = Default::default();
-    //~^ ERROR type annotations needed
-
     _q = foo::<_, 2>(_q);
-    //~^ ERROR type annotations needed
 }
diff --git a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr
deleted file mode 100644
index 9e8328d37017e..0000000000000
--- a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr
+++ /dev/null
@@ -1,38 +0,0 @@
-error[E0282]: type annotations needed
-  --> $DIR/const_eval_resolve_canonical.rs:26:9
-   |
-LL |     let mut _q = Default::default();
-   |         ^^^^^^
-   |
-help: consider giving `_q` an explicit type
-   |
-LL |     let mut _q: _ = Default::default();
-   |               +++
-
-error[E0283]: type annotations needed
-  --> $DIR/const_eval_resolve_canonical.rs:29:10
-   |
-LL |     _q = foo::<_, 2>(_q);
-   |          ^^^^^^^^^^^ cannot infer the value of the constant `{ N + 1 }`
-   |
-note: multiple `impl`s satisfying `(): Foo<{ N + 1 }>` found
-  --> $DIR/const_eval_resolve_canonical.rs:8:1
-   |
-LL | impl Foo<0> for () {
-   | ^^^^^^^^^^^^^^^^^^
-...
-LL | impl Foo<3> for () {
-   | ^^^^^^^^^^^^^^^^^^
-note: required by a bound in `foo`
-  --> $DIR/const_eval_resolve_canonical.rs:18:9
-   |
-LL | fn foo<T, const N: usize>(_: T) -> <() as Foo<{ N + 1 }>>::Assoc
-   |    --- required by a bound in this
-LL | where
-LL |     (): Foo<{ N + 1 }>,
-   |         ^^^^^^^^^^^^^^ required by this bound in `foo`
-
-error: aborting due to 2 previous errors
-
-Some errors have detailed explanations: E0282, E0283.
-For more information about an error, try `rustc --explain E0282`.