diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index a7ce92ea57917..f3f2b83c821d5 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1422,6 +1422,9 @@ pub type Lit = Spanned<LitKind>;
 /// These are usually found nested inside types (e.g., array lengths)
 /// or expressions (e.g., repeat counts), and also used to define
 /// explicit discriminant values for enum variants.
+///
+/// You can check if this anon const is a default in a const param
+/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_hir_id(..)`
 #[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)]
 pub struct AnonConst {
     pub hir_id: HirId,
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 07b39c97c492a..9d81407c330c4 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -901,6 +901,19 @@ impl<'hir> Map<'hir> {
     pub fn node_to_string(&self, id: HirId) -> String {
         hir_id_to_string(self, id)
     }
+
+    /// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when
+    /// called with the HirId for the `{ ... }` anon const
+    pub fn opt_const_param_default_param_hir_id(&self, anon_const: HirId) -> Option<HirId> {
+        match self.get(self.get_parent_node(anon_const)) {
+            Node::GenericParam(GenericParam {
+                hir_id: param_id,
+                kind: GenericParamKind::Const { .. },
+                ..
+            }) => Some(*param_id),
+            _ => None,
+        }
+    }
 }
 
 impl<'hir> intravisit::Map<'hir> for Map<'hir> {
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 583ba9392f062..a2ac3e2555f50 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1441,6 +1441,52 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
                 // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
                 None
             } else if tcx.lazy_normalization() {
+                if let Some(param_id) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) {
+                    // If the def_id we are calling generics_of on is an anon ct default i.e:
+                    //
+                    // struct Foo<const N: usize = { .. }>;
+                    //        ^^^       ^          ^^^^^^ def id of this anon const
+                    //        ^         ^ param_id
+                    //        ^ parent_def_id
+                    //
+                    // then we only want to return generics for params to the left of `N`. If we don't do that we
+                    // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, substs: [N#0])`.
+                    //
+                    // This causes ICEs (#86580) when building the substs for Foo in `fn foo() -> Foo { .. }` as
+                    // we substitute the defaults with the partially built substs when we build the substs. Subst'ing
+                    // the `N#0` on the unevaluated const indexes into the empty substs we're in the process of building.
+                    //
+                    // We fix this by having this function return the parent's generics ourselves and truncating the
+                    // generics to only include non-forward declared params (with the exception of the `Self` ty)
+                    //
+                    // For the above code example that means we want `substs: []`
+                    // For the following struct def we want `substs: [N#0]` when generics_of is called on
+                    // the def id of the `{ N + 1 }` anon const
+                    // struct Foo<const N: usize, const M: usize = { N + 1 }>;
+                    //
+                    // This has some implications for how we get the predicates available to the anon const
+                    // see `explicit_predicates_of` for more information on this
+                    let generics = tcx.generics_of(parent_def_id.to_def_id());
+                    let param_def = tcx.hir().local_def_id(param_id).to_def_id();
+                    let param_def_idx = generics.param_def_id_to_index[&param_def];
+                    // In the above example this would be .params[..N#0]
+                    let params = generics.params[..param_def_idx as usize].to_owned();
+                    let param_def_id_to_index =
+                        params.iter().map(|param| (param.def_id, param.index)).collect();
+
+                    return ty::Generics {
+                        // we set the parent of these generics to be our parent's parent so that we
+                        // dont end up with substs: [N, M, N] for the const default on a struct like this:
+                        // struct Foo<const N: usize, const M: usize = { ... }>;
+                        parent: generics.parent,
+                        parent_count: generics.parent_count,
+                        params,
+                        param_def_id_to_index,
+                        has_self: generics.has_self,
+                        has_late_bound_regions: generics.has_late_bound_regions,
+                    };
+                }
+
                 // HACK(eddyb) this provides the correct generics when
                 // `feature(const_generics)` is enabled, so that const expressions
                 // used with const generics, e.g. `Foo<{N+1}>`, can work at all.
@@ -2359,7 +2405,8 @@ fn trait_explicit_predicates_and_bounds(
 }
 
 fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
-    if let DefKind::Trait = tcx.def_kind(def_id) {
+    let def_kind = tcx.def_kind(def_id);
+    if let DefKind::Trait = def_kind {
         // Remove bounds on associated types from the predicates, they will be
         // returned by `explicit_item_bounds`.
         let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local());
@@ -2404,6 +2451,26 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
             }
         }
     } else {
+        if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() {
+            let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+            if let Some(_) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) {
+                // In `generics_of` we set the generics' parent to be our parent's parent which means that
+                // we lose out on the predicates of our actual parent if we dont return those predicates here.
+                // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
+                //
+                // struct Foo<T, const N: usize = { <T as Trait>::ASSOC }>(T) where T: Trait;
+                //        ^^^                     ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling
+                //        ^^^                                             explicit_predicates_of on
+                //        parent item we dont have set as the
+                //        parent of generics returned by `generics_of`
+                //
+                // In the above code we want the anon const to have predicates in its param env for `T: Trait`
+                let item_id = tcx.hir().get_parent_item(hir_id);
+                let item_def_id = tcx.hir().local_def_id(item_id).to_def_id();
+                // In the above code example we would be calling `explicit_predicates_of(Foo)` here
+                return tcx.explicit_predicates_of(item_def_id);
+            }
+        }
         gather_explicit_predicates_of(tcx, def_id)
     }
 }
diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs
index d7eb31c2abef5..70a2ba7fcd9d9 100644
--- a/compiler/rustc_typeck/src/outlives/mod.rs
+++ b/compiler/rustc_typeck/src/outlives/mod.rs
@@ -20,6 +20,27 @@ pub fn provide(providers: &mut Providers) {
 fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] {
     let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
 
+    if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
+    {
+        if let Some(_) = tcx.hir().opt_const_param_default_param_hir_id(id) {
+            // In `generics_of` we set the generics' parent to be our parent's parent which means that
+            // we lose out on the predicates of our actual parent if we dont return those predicates here.
+            // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
+            //
+            // struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ());
+            //        ^^^                          ^^^^^^^ the def id we are calling
+            //        ^^^                                  inferred_outlives_of on
+            //        parent item we dont have set as the
+            //        parent of generics returned by `generics_of`
+            //
+            // In the above code we want the anon const to have predicates in its param env for `'b: 'a`
+            let item_id = tcx.hir().get_parent_item(id);
+            let item_def_id = tcx.hir().local_def_id(item_id).to_def_id();
+            // In the above code example we would be calling `inferred_outlives_of(Foo)` here
+            return tcx.inferred_outlives_of(item_def_id);
+        }
+    }
+
     match tcx.hir().get(id) {
         Node::Item(item) => match item.kind {
             hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => {
diff --git a/src/test/ui/const-generics/defaults/cec-concrete-default.rs b/src/test/ui/const-generics/defaults/cec-concrete-default.rs
new file mode 100644
index 0000000000000..c2a41cf2ad7dd
--- /dev/null
+++ b/src/test/ui/const-generics/defaults/cec-concrete-default.rs
@@ -0,0 +1,14 @@
+#![feature(const_generics, const_evaluatable_checked, const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct Foo<const N: usize, const M: usize = { N + 1 }>;
+fn no_constraining() -> Foo<10> {
+    Foo::<10, 11>
+}
+
+pub fn different_than_default() -> Foo<10> {
+    Foo::<10, 12>
+    //~^ error: mismatched types
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/defaults/cec-concrete-default.stderr b/src/test/ui/const-generics/defaults/cec-concrete-default.stderr
new file mode 100644
index 0000000000000..090e507b7f34e
--- /dev/null
+++ b/src/test/ui/const-generics/defaults/cec-concrete-default.stderr
@@ -0,0 +1,12 @@
+error[E0308]: mismatched types
+  --> $DIR/cec-concrete-default.rs:10:5
+   |
+LL |     Foo::<10, 12>
+   |     ^^^^^^^^^^^^^ expected `11_usize`, found `12_usize`
+   |
+   = note: expected type `11_usize`
+              found type `12_usize`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.rs b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.rs
new file mode 100644
index 0000000000000..15822dfac1c5a
--- /dev/null
+++ b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.rs
@@ -0,0 +1,16 @@
+#![feature(const_generics, const_evaluatable_checked, const_generics_defaults)]
+#![allow(incomplete_features)]
+
+struct Foo<const N: usize, const M: usize = { N + 1 }>;
+fn should_unify<const N: usize>() -> Foo<N> where [(); { N + 1 }]: {
+    Foo::<N, { N + 1 }>
+}
+pub fn shouldnt_unify<const N: usize>() -> Foo<N>
+where
+    [(); { N + 1 }]:,
+    [(); { N + 2 }]:, {
+    Foo::<N, { N + 2 }>
+    //~^ error: mismatched types
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.stderr b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.stderr
new file mode 100644
index 0000000000000..f97fc26a07321
--- /dev/null
+++ b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.stderr
@@ -0,0 +1,12 @@
+error[E0308]: mismatched types
+  --> $DIR/cec-generic-default-mismatched-types.rs:12:5
+   |
+LL |     Foo::<N, { N + 2 }>
+   |     ^^^^^^^^^^^^^^^^^^^ expected `{ N + 1 }`, found `{ N + 2 }`
+   |
+   = note: expected type `{ N + 1 }`
+              found type `{ N + 2 }`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/const-generics/defaults/cec-generic-default.rs b/src/test/ui/const-generics/defaults/cec-generic-default.rs
new file mode 100644
index 0000000000000..76ff7c7801b06
--- /dev/null
+++ b/src/test/ui/const-generics/defaults/cec-generic-default.rs
@@ -0,0 +1,24 @@
+#![feature(const_evaluatable_checked, const_generics, const_generics_defaults)]
+#![allow(incomplete_features)]
+
+pub struct Foo<const N: usize, const M: usize = { N + 1 }>;
+pub fn needs_evaluatable_bound<const N1: usize>() -> Foo<N1> {
+    //~^ error: unconstrained generic constant
+    loop {}
+}
+pub fn has_evaluatable_bound<const N1: usize>() -> Foo<N1> where [(); N1 + 1]: {
+    loop {}
+}
+
+type FooAlias<const N: usize, const NP: usize = { N + 1 }> = [(); NP];
+fn needs_evaluatable_bound_alias<T, const N: usize>() -> FooAlias<N>
+{
+    //~^^ error: unconstrained generic constant
+    todo!()
+}
+fn has_evaluatable_bound_alias<const N: usize>() -> FooAlias<N>
+where [(); N + 1]: {
+    todo!()
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/defaults/cec-generic-default.stderr b/src/test/ui/const-generics/defaults/cec-generic-default.stderr
new file mode 100644
index 0000000000000..0234ea8b9a4a9
--- /dev/null
+++ b/src/test/ui/const-generics/defaults/cec-generic-default.stderr
@@ -0,0 +1,18 @@
+error: unconstrained generic constant
+  --> $DIR/cec-generic-default.rs:5:54
+   |
+LL | pub fn needs_evaluatable_bound<const N1: usize>() -> Foo<N1> {
+   |                                                      ^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:`
+
+error: unconstrained generic constant
+  --> $DIR/cec-generic-default.rs:14:58
+   |
+LL | fn needs_evaluatable_bound_alias<T, const N: usize>() -> FooAlias<N>
+   |                                                          ^^^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:`
+
+error: aborting due to 2 previous errors
+