about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-05-18 19:04:03 +0200
committerGitHub <noreply@github.com>2020-05-18 19:04:03 +0200
commitc6030c957a2bb4ddb36c9a06df5fcf9c5f626029 (patch)
tree3e31077070ab8aa454067a7951b4e384bea55160
parent58e644736521b2916a6734aa225603c539bfeeed (diff)
parent9da8a5ba6848cf246995b985b0982a7a9aa44890 (diff)
downloadrust-c6030c957a2bb4ddb36c9a06df5fcf9c5f626029.tar.gz
rust-c6030c957a2bb4ddb36c9a06df5fcf9c5f626029.zip
Rollup merge of #71973 - lcnr:lazy-norm, r=nikomatsakis
Lazy normalization of constants (Reprise)

Continuation of #67890 by @skinny121.

Initial implementation of #60471 for constants.

Perform normalization/evaluation of constants lazily, which is known as lazy normalization. Lazy normalization is only enabled when using `#![feature(lazy_normalization_consts)]`, by default constants are still evaluated eagerly as there are currently.

Lazy normalization of constants is achieved with a new ConstEquate predicate which type inferences uses to delay checking whether constants are equal to each other until later, avoiding cycle errors.

Note this doesn't allow the use of generics within repeat count expressions as that is still evaluated during conversion to mir. There are also quite a few other known problems with lazy normalization which will be fixed in future PRs.

r? @nikomatsakis

fixes #71922, fixes #71986
-rw-r--r--src/librustc_infer/infer/canonical/query_response.rs9
-rw-r--r--src/librustc_infer/infer/combine.rs43
-rw-r--r--src/librustc_infer/infer/equate.rs8
-rw-r--r--src/librustc_infer/infer/glb.rs7
-rw-r--r--src/librustc_infer/infer/lub.rs7
-rw-r--r--src/librustc_infer/infer/mod.rs20
-rw-r--r--src/librustc_infer/infer/nll_relate/mod.rs13
-rw-r--r--src/librustc_infer/infer/outlives/mod.rs3
-rw-r--r--src/librustc_infer/infer/sub.rs7
-rw-r--r--src/librustc_infer/traits/mod.rs3
-rw-r--r--src/librustc_infer/traits/structural_impls.rs3
-rw-r--r--src/librustc_infer/traits/util.rs6
-rw-r--r--src/librustc_lint/builtin.rs3
-rw-r--r--src/librustc_middle/ty/context.rs9
-rw-r--r--src/librustc_middle/ty/mod.rs12
-rw-r--r--src/librustc_middle/ty/outlives.rs6
-rw-r--r--src/librustc_middle/ty/print/pretty.rs7
-rw-r--r--src/librustc_middle/ty/relate.rs3
-rw-r--r--src/librustc_middle/ty/structural_impls.rs4
-rw-r--r--src/librustc_mir/borrow_check/type_check/relate_tys.rs6
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs3
-rw-r--r--src/librustc_trait_selection/opaque_types.rs3
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/mod.rs20
-rw-r--r--src/librustc_trait_selection/traits/fulfill.rs66
-rw-r--r--src/librustc_trait_selection/traits/object_safety.rs99
-rw-r--r--src/librustc_trait_selection/traits/project.rs8
-rw-r--r--src/librustc_trait_selection/traits/select.rs41
-rw-r--r--src/librustc_trait_selection/traits/wf.rs4
-rw-r--r--src/librustc_traits/chalk/lowering.rs13
-rw-r--r--src/librustc_traits/implied_outlives_bounds.rs3
-rw-r--r--src/librustc_traits/normalize_erasing_regions.rs3
-rw-r--r--src/librustc_typeck/check/method/probe.rs3
-rw-r--r--src/librustc_typeck/check/mod.rs11
-rw-r--r--src/librustc_typeck/collect.rs5
-rw-r--r--src/librustc_typeck/impl_wf_check/min_specialization.rs3
-rw-r--r--src/librustc_typeck/outlives/explicit.rs3
-rw-r--r--src/librustdoc/clean/mod.rs3
-rw-r--r--src/test/ui/const-generics/different_byref.stderr4
-rw-r--r--src/test/ui/const-generics/fn-const-param-infer.stderr16
-rw-r--r--src/test/ui/const-generics/issues/issue-61935.rs24
-rw-r--r--src/test/ui/const-generics/issues/issue-61935.stderr11
-rw-r--r--src/test/ui/const-generics/issues/issue-62504.rs3
-rw-r--r--src/test/ui/const-generics/issues/issue-62504.stderr12
-rw-r--r--src/test/ui/const-generics/issues/issue-66205.rs1
-rw-r--r--src/test/ui/const-generics/issues/issue-66205.stderr2
-rw-r--r--src/test/ui/const-generics/issues/issue-67185-1.rs32
-rw-r--r--src/test/ui/const-generics/issues/issue-67185-1.stderr11
-rw-r--r--src/test/ui/const-generics/issues/issue-67185-2.rs35
-rw-r--r--src/test/ui/const-generics/issues/issue-67185-2.stderr112
-rw-r--r--src/test/ui/const-generics/issues/issue-69654.rs18
-rw-r--r--src/test/ui/const-generics/issues/issue-69654.stderr14
-rw-r--r--src/test/ui/const-generics/lazy-normalization/issue-71922.rs19
-rw-r--r--src/test/ui/const-generics/lazy-normalization/issue-71986.rs8
-rw-r--r--src/test/ui/const-generics/raw-ptr-const-param.stderr8
-rw-r--r--src/test/ui/const-generics/types-mismatch-const-args.stderr12
-rw-r--r--src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr2
56 files changed, 669 insertions, 145 deletions
diff --git a/src/librustc_infer/infer/canonical/query_response.rs b/src/librustc_infer/infer/canonical/query_response.rs
index de93baff79b..c7a7cf89b4f 100644
--- a/src/librustc_infer/infer/canonical/query_response.rs
+++ b/src/librustc_infer/infer/canonical/query_response.rs
@@ -25,7 +25,7 @@ use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, BoundVar, Ty, TyCtxt};
+use rustc_middle::ty::{self, BoundVar, Const, Ty, TyCtxt};
 use std::fmt::Debug;
 
 impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
@@ -671,6 +671,13 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
         });
     }
 
+    fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {
+        span_bug!(
+            self.cause.span(self.infcx.tcx),
+            "lazy_normalization_consts: unreachable `const_equate`"
+        );
+    }
+
     fn normalization() -> NormalizationStrategy {
         NormalizationStrategy::Eager
     }
diff --git a/src/librustc_infer/infer/combine.rs b/src/librustc_infer/infer/combine.rs
index d4af4704996..3467457b449 100644
--- a/src/librustc_infer/infer/combine.rs
+++ b/src/librustc_infer/infer/combine.rs
@@ -39,7 +39,7 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
+use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::ty::{IntType, UintType};
 use rustc_span::{Span, DUMMY_SP};
 
@@ -126,7 +126,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
         b: &'tcx ty::Const<'tcx>,
     ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>
     where
-        R: TypeRelation<'tcx>,
+        R: ConstEquateRelation<'tcx>,
     {
         debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
         if a == b {
@@ -164,7 +164,22 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
             (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
                 return self.unify_const_variable(!a_is_expected, vid, a);
             }
-
+            (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
+                // FIXME(#59490): Need to remove the leak check to accomodate
+                // escaping bound variables here.
+                if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
+                    relation.const_equate_obligation(a, b);
+                }
+                return Ok(b);
+            }
+            (_, ty::ConstKind::Unevaluated(..)) if self.tcx.lazy_normalization() => {
+                // FIXME(#59490): Need to remove the leak check to accomodate
+                // escaping bound variables here.
+                if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() {
+                    relation.const_equate_obligation(a, b);
+                }
+                return Ok(a);
+            }
             _ => {}
         }
 
@@ -375,6 +390,20 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         debug!("generalize: success {{ {:?}, {:?} }}", ty, needs_wf);
         Ok(Generalization { ty, needs_wf })
     }
+
+    pub fn add_const_equate_obligation(
+        &mut self,
+        a_is_expected: bool,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) {
+        let predicate = if a_is_expected {
+            ty::Predicate::ConstEquate(a, b)
+        } else {
+            ty::Predicate::ConstEquate(b, a)
+        };
+        self.obligations.push(Obligation::new(self.trace.cause.clone(), self.param_env, predicate));
+    }
 }
 
 struct Generalizer<'cx, 'tcx> {
@@ -637,11 +666,19 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                     }
                 }
             }
+            ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c),
             _ => relate::super_relate_consts(self, c, c),
         }
     }
 }
 
+pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> {
+    /// Register an obligation that both constants must be equal to each other.
+    ///
+    /// If they aren't equal then the relation doesn't hold.
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
+}
+
 pub trait RelateResultCompare<'tcx, T> {
     fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T>
     where
diff --git a/src/librustc_infer/infer/equate.rs b/src/librustc_infer/infer/equate.rs
index d054070e292..e3cafb82719 100644
--- a/src/librustc_infer/infer/equate.rs
+++ b/src/librustc_infer/infer/equate.rs
@@ -1,4 +1,4 @@
-use super::combine::{CombineFields, RelationDir};
+use super::combine::{CombineFields, ConstEquateRelation, RelationDir};
 use super::Subtype;
 
 use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
@@ -140,3 +140,9 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> {
         }
     }
 }
+
+impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
diff --git a/src/librustc_infer/infer/glb.rs b/src/librustc_infer/infer/glb.rs
index f95d74a9340..ec219a95b94 100644
--- a/src/librustc_infer/infer/glb.rs
+++ b/src/librustc_infer/infer/glb.rs
@@ -3,6 +3,7 @@ use super::lattice::{self, LatticeDir};
 use super::InferCtxt;
 use super::Subtype;
 
+use crate::infer::combine::ConstEquateRelation;
 use crate::traits::ObligationCause;
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -116,3 +117,9 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
         Ok(())
     }
 }
+
+impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
diff --git a/src/librustc_infer/infer/lub.rs b/src/librustc_infer/infer/lub.rs
index 492f2b229d3..a0453db2cb4 100644
--- a/src/librustc_infer/infer/lub.rs
+++ b/src/librustc_infer/infer/lub.rs
@@ -3,6 +3,7 @@ use super::lattice::{self, LatticeDir};
 use super::InferCtxt;
 use super::Subtype;
 
+use crate::infer::combine::ConstEquateRelation;
 use crate::traits::ObligationCause;
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -100,6 +101,12 @@ impl TypeRelation<'tcx> for Lub<'combine, 'infcx, 'tcx> {
     }
 }
 
+impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
+
 impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> {
     fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> {
         self.fields.infcx
diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs
index 67632a97df7..9c81a115395 100644
--- a/src/librustc_infer/infer/mod.rs
+++ b/src/librustc_infer/infer/mod.rs
@@ -1490,6 +1490,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         self.report_and_explain_type_error(trace, &err)
     }
 
+    pub fn report_mismatched_consts(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        expected: &'tcx ty::Const<'tcx>,
+        actual: &'tcx ty::Const<'tcx>,
+        err: TypeError<'tcx>,
+    ) -> DiagnosticBuilder<'tcx> {
+        let trace = TypeTrace::consts(cause, true, expected, actual);
+        self.report_and_explain_type_error(trace, &err)
+    }
+
     pub fn replace_bound_vars_with_fresh_vars<T>(
         &self,
         span: Span,
@@ -1777,6 +1788,15 @@ impl<'tcx> TypeTrace<'tcx> {
         TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) }
     }
 
+    pub fn consts(
+        cause: &ObligationCause<'tcx>,
+        a_is_expected: bool,
+        a: &'tcx ty::Const<'tcx>,
+        b: &'tcx ty::Const<'tcx>,
+    ) -> TypeTrace<'tcx> {
+        TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) }
+    }
+
     pub fn dummy(tcx: TyCtxt<'tcx>) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: ObligationCause::dummy(),
diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs
index 7aea26987a2..8de89251006 100644
--- a/src/librustc_infer/infer/nll_relate/mod.rs
+++ b/src/librustc_infer/infer/nll_relate/mod.rs
@@ -21,6 +21,7 @@
 //!   thing we relate in chalk are basically domain goals and their
 //!   constituents)
 
+use crate::infer::combine::ConstEquateRelation;
 use crate::infer::InferCtxt;
 use crate::infer::{ConstVarValue, ConstVariableValue};
 use rustc_data_structures::fx::FxHashMap;
@@ -77,6 +78,8 @@ pub trait TypeRelatingDelegate<'tcx> {
     /// delegate.
     fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>);
 
+    fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
+
     /// Creates a new universe index. Used when instantiating placeholders.
     fn create_next_universe(&mut self) -> ty::UniverseIndex;
 
@@ -715,6 +718,15 @@ where
     }
 }
 
+impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D>
+where
+    D: TypeRelatingDelegate<'tcx>,
+{
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.delegate.const_equate(a, b);
+    }
+}
+
 /// When we encounter a binder like `for<..> fn(..)`, we actually have
 /// to walk the `fn` value to find all the values bound by the `for`
 /// (these are not explicitly present in the ty representation right
@@ -976,6 +988,7 @@ where
                     }
                 }
             }
+            ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a),
             _ => relate::super_relate_consts(self, a, a),
         }
     }
diff --git a/src/librustc_infer/infer/outlives/mod.rs b/src/librustc_infer/infer/outlives/mod.rs
index ed3d8605869..289457e2bd0 100644
--- a/src/librustc_infer/infer/outlives/mod.rs
+++ b/src/librustc_infer/infer/outlives/mod.rs
@@ -19,7 +19,8 @@ pub fn explicit_outlives_bounds<'tcx>(
         | ty::Predicate::ObjectSafe(..)
         | ty::Predicate::ClosureKind(..)
         | ty::Predicate::TypeOutlives(..)
-        | ty::Predicate::ConstEvaluatable(..) => None,
+        | ty::Predicate::ConstEvaluatable(..)
+        | ty::Predicate::ConstEquate(..) => None,
         ty::Predicate::RegionOutlives(ref data) => data
             .no_bound_vars()
             .map(|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a)),
diff --git a/src/librustc_infer/infer/sub.rs b/src/librustc_infer/infer/sub.rs
index 0abcc15d6fc..1ec67ef2efa 100644
--- a/src/librustc_infer/infer/sub.rs
+++ b/src/librustc_infer/infer/sub.rs
@@ -1,6 +1,7 @@
 use super::combine::{CombineFields, RelationDir};
 use super::SubregionOrigin;
 
+use crate::infer::combine::ConstEquateRelation;
 use crate::traits::Obligation;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
@@ -169,3 +170,9 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
         self.fields.higher_ranked_sub(a, b, self.a_is_expected)
     }
 }
+
+impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> {
+    fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+        self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
+    }
+}
diff --git a/src/librustc_infer/traits/mod.rs b/src/librustc_infer/traits/mod.rs
index a8585fd2935..8d95904b355 100644
--- a/src/librustc_infer/traits/mod.rs
+++ b/src/librustc_infer/traits/mod.rs
@@ -10,7 +10,7 @@ pub mod util;
 
 use rustc_hir as hir;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Const, Ty};
 use rustc_span::Span;
 
 pub use self::FulfillmentErrorCode::*;
@@ -81,6 +81,7 @@ pub enum FulfillmentErrorCode<'tcx> {
     CodeSelectionError(SelectionError<'tcx>),
     CodeProjectionError(MismatchedProjectionTypes<'tcx>),
     CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
+    CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>),
     CodeAmbiguity,
 }
 
diff --git a/src/librustc_infer/traits/structural_impls.rs b/src/librustc_infer/traits/structural_impls.rs
index 595a8cd7683..c48e58c0482 100644
--- a/src/librustc_infer/traits/structural_impls.rs
+++ b/src/librustc_infer/traits/structural_impls.rs
@@ -41,6 +41,9 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
             super::CodeSubtypeError(ref a, ref b) => {
                 write!(f, "CodeSubtypeError({:?}, {:?})", a, b)
             }
+            super::CodeConstEquateError(ref a, ref b) => {
+                write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
+            }
             super::CodeAmbiguity => write!(f, "Ambiguity"),
         }
     }
diff --git a/src/librustc_infer/traits/util.rs b/src/librustc_infer/traits/util.rs
index b34685006e2..ee903b676ba 100644
--- a/src/librustc_infer/traits/util.rs
+++ b/src/librustc_infer/traits/util.rs
@@ -42,6 +42,8 @@ pub fn anonymize_predicate<'tcx>(
         ty::Predicate::ConstEvaluatable(def_id, substs) => {
             ty::Predicate::ConstEvaluatable(def_id, substs)
         }
+
+        ty::Predicate::ConstEquate(c1, c2) => ty::Predicate::ConstEquate(c1, c2),
     }
 }
 
@@ -187,6 +189,10 @@ impl Elaborator<'tcx> {
                 // Currently, we do not elaborate const-evaluatable
                 // predicates.
             }
+            ty::Predicate::ConstEquate(..) => {
+                // Currently, we do not elaborate const-equate
+                // predicates.
+            }
             ty::Predicate::RegionOutlives(..) => {
                 // Nothing to elaborate from `'a: 'b`.
             }
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 327dbecba26..bca91fb7b5d 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1221,7 +1221,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints {
                     ObjectSafe(..) |
                     ClosureKind(..) |
                     Subtype(..) |
-                    ConstEvaluatable(..) => continue,
+                    ConstEvaluatable(..) |
+                    ConstEquate(..) => continue,
                 };
                 if predicate.is_global() {
                     cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs
index 33f5fbe7858..c005455a3aa 100644
--- a/src/librustc_middle/ty/context.rs
+++ b/src/librustc_middle/ty/context.rs
@@ -1339,7 +1339,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// What mode(s) of borrowck should we run? AST? MIR? both?
     /// (Also considers the `#![feature(nll)]` setting.)
-    pub fn borrowck_mode(&self) -> BorrowckMode {
+    pub fn borrowck_mode(self) -> BorrowckMode {
         // Here are the main constraints we need to deal with:
         //
         // 1. An opts.borrowck_mode of `BorrowckMode::Migrate` is
@@ -1369,6 +1369,13 @@ impl<'tcx> TyCtxt<'tcx> {
         self.sess.opts.borrowck_mode
     }
 
+    /// If `true`, we should use lazy normalization for constants, otherwise
+    /// we still evaluate them eagerly.
+    #[inline]
+    pub fn lazy_normalization(self) -> bool {
+        self.features().const_generics
+    }
+
     #[inline]
     pub fn local_crate_exports_generics(self) -> bool {
         debug_assert!(self.sess.opts.share_generics());
diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs
index 6d6e1699feb..36bc44f5e50 100644
--- a/src/librustc_middle/ty/mod.rs
+++ b/src/librustc_middle/ty/mod.rs
@@ -1054,6 +1054,9 @@ pub enum Predicate<'tcx> {
 
     /// Constant initializer must evaluate successfully.
     ConstEvaluatable(DefId, SubstsRef<'tcx>),
+
+    /// Constants must be equal. The first component is the const that is expected.
+    ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>),
 }
 
 /// The crate outlives map is computed during typeck and contains the
@@ -1172,6 +1175,9 @@ impl<'tcx> Predicate<'tcx> {
             Predicate::ConstEvaluatable(def_id, const_substs) => {
                 Predicate::ConstEvaluatable(def_id, const_substs.subst(tcx, substs))
             }
+            Predicate::ConstEquate(c1, c2) => {
+                Predicate::ConstEquate(c1.subst(tcx, substs), c2.subst(tcx, substs))
+            }
         }
     }
 }
@@ -1349,7 +1355,8 @@ impl<'tcx> Predicate<'tcx> {
             | Predicate::ObjectSafe(..)
             | Predicate::ClosureKind(..)
             | Predicate::TypeOutlives(..)
-            | Predicate::ConstEvaluatable(..) => None,
+            | Predicate::ConstEvaluatable(..)
+            | Predicate::ConstEquate(..) => None,
         }
     }
 
@@ -1363,7 +1370,8 @@ impl<'tcx> Predicate<'tcx> {
             | Predicate::WellFormed(..)
             | Predicate::ObjectSafe(..)
             | Predicate::ClosureKind(..)
-            | Predicate::ConstEvaluatable(..) => None,
+            | Predicate::ConstEvaluatable(..)
+            | Predicate::ConstEquate(..) => None,
         }
     }
 }
diff --git a/src/librustc_middle/ty/outlives.rs b/src/librustc_middle/ty/outlives.rs
index afd670b8577..3e6a12df688 100644
--- a/src/librustc_middle/ty/outlives.rs
+++ b/src/librustc_middle/ty/outlives.rs
@@ -83,6 +83,11 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
                 }
             }
 
+            ty::Array(element, _) => {
+                // Don't look into the len const as it doesn't affect regions
+                compute_components(tcx, element, out);
+            }
+
             ty::Closure(_, ref substs) => {
                 for upvar_ty in substs.as_closure().upvar_tys() {
                     compute_components(tcx, upvar_ty, out);
@@ -158,7 +163,6 @@ fn compute_components(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Compo
             ty::Opaque(..) |      // OutlivesNominalType (ish)
             ty::Foreign(..) |     // OutlivesNominalType
             ty::Str |             // OutlivesScalar (ish)
-            ty::Array(..) |       // ...
             ty::Slice(..) |       // ...
             ty::RawPtr(..) |      // ...
             ty::Ref(..) |         // OutlivesReference
diff --git a/src/librustc_middle/ty/print/pretty.rs b/src/librustc_middle/ty/print/pretty.rs
index 64909cd0c46..2502a4a13a8 100644
--- a/src/librustc_middle/ty/print/pretty.rs
+++ b/src/librustc_middle/ty/print/pretty.rs
@@ -2058,6 +2058,13 @@ define_print_and_forward_display! {
                    print_value_path(def_id, substs),
                    write("` can be evaluated"))
             }
+            ty::Predicate::ConstEquate(c1, c2) => {
+                p!(write("the constant `"),
+                   print(c1),
+                   write("` equals `"),
+                   print(c2),
+                   write("`"))
+            }
         }
     }
 
diff --git a/src/librustc_middle/ty/relate.rs b/src/librustc_middle/ty/relate.rs
index d68bc7221f9..594ffbcd836 100644
--- a/src/librustc_middle/ty/relate.rs
+++ b/src/librustc_middle/ty/relate.rs
@@ -431,6 +431,9 @@ pub fn super_relate_tys<R: TypeRelation<'tcx>>(
             let t = relation.relate(&a_t, &b_t)?;
             match relation.relate(&sz_a, &sz_b) {
                 Ok(sz) => Ok(tcx.mk_ty(ty::Array(t, sz))),
+                // FIXME(#72219) Implement improved diagnostics for mismatched array
+                // length?
+                Err(err) if relation.tcx().lazy_normalization() => Err(err),
                 Err(err) => {
                     // Check whether the lengths are both concrete/known values,
                     // but are unequal, for better diagnostics.
diff --git a/src/librustc_middle/ty/structural_impls.rs b/src/librustc_middle/ty/structural_impls.rs
index 680b7187921..cdb7e34546a 100644
--- a/src/librustc_middle/ty/structural_impls.rs
+++ b/src/librustc_middle/ty/structural_impls.rs
@@ -240,6 +240,7 @@ impl fmt::Debug for ty::Predicate<'tcx> {
             ty::Predicate::ConstEvaluatable(def_id, substs) => {
                 write!(f, "ConstEvaluatable({:?}, {:?})", def_id, substs)
             }
+            ty::Predicate::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2),
         }
     }
 }
@@ -492,6 +493,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> {
             ty::Predicate::ConstEvaluatable(def_id, substs) => {
                 tcx.lift(&substs).map(|substs| ty::Predicate::ConstEvaluatable(def_id, substs))
             }
+            ty::Predicate::ConstEquate(c1, c2) => {
+                tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::Predicate::ConstEquate(c1, c2))
+            }
         }
     }
 }
diff --git a/src/librustc_mir/borrow_check/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/type_check/relate_tys.rs
index 96ae534c396..7ff12820db8 100644
--- a/src/librustc_mir/borrow_check/type_check/relate_tys.rs
+++ b/src/librustc_mir/borrow_check/type_check/relate_tys.rs
@@ -2,7 +2,7 @@ use rustc_infer::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRe
 use rustc_infer::infer::{InferCtxt, NLLRegionVariableOrigin};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::relate::TypeRelation;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Const, Ty};
 use rustc_trait_selection::traits::query::Fallible;
 
 use crate::borrow_check::constraints::OutlivesConstraint;
@@ -99,6 +99,10 @@ impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
         }
     }
 
+    // We don't have to worry about the equality of consts during borrow checking
+    // as consts always have a static lifetime.
+    fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {}
+
     fn normalization() -> NormalizationStrategy {
         NormalizationStrategy::Eager
     }
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index c9982aeaa08..a2e2c1bf9ed 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -28,7 +28,8 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -
                 | Predicate::TypeOutlives(_)
                 | Predicate::WellFormed(_)
                 | Predicate::Projection(_)
-                | Predicate::ConstEvaluatable(..) => continue,
+                | Predicate::ConstEvaluatable(..)
+                | Predicate::ConstEquate(..) => continue,
                 Predicate::ObjectSafe(_) => {
                     bug!("object safe predicate on function: {:#?}", predicate)
                 }
diff --git a/src/librustc_trait_selection/opaque_types.rs b/src/librustc_trait_selection/opaque_types.rs
index 4f8075b0171..396965fcfb8 100644
--- a/src/librustc_trait_selection/opaque_types.rs
+++ b/src/librustc_trait_selection/opaque_types.rs
@@ -1277,7 +1277,8 @@ crate fn required_region_bounds(
                 | ty::Predicate::ObjectSafe(..)
                 | ty::Predicate::ClosureKind(..)
                 | ty::Predicate::RegionOutlives(..)
-                | ty::Predicate::ConstEvaluatable(..) => None,
+                | ty::Predicate::ConstEvaluatable(..)
+                | ty::Predicate::ConstEquate(..) => None,
                 ty::Predicate::TypeOutlives(predicate) => {
                     // Search for a bound of the form `erased_self_ty
                     // : 'a`, but be wary of something like `for<'a>
diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs
index 0d53df3bf4b..139b8600722 100644
--- a/src/librustc_trait_selection/traits/error_reporting/mod.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs
@@ -615,6 +615,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             obligation
                         )
                     }
+
+                    ty::Predicate::ConstEquate(..) => {
+                        // Errors for `ConstEquate` predicates show up as
+                        // `SelectionError::ConstEvalFailure`,
+                        // not `Unimplemented`.
+                        span_bug!(
+                            span,
+                            "const-equate requirement gave wrong error: `{:?}`",
+                            obligation
+                        )
+                    }
                 }
             }
 
@@ -1092,6 +1103,15 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
                 )
                 .emit();
             }
+            FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => {
+                self.report_mismatched_consts(
+                    &error.obligation.cause,
+                    expected_found.expected,
+                    expected_found.found,
+                    err.clone(),
+                )
+                .emit();
+            }
         }
     }
 
diff --git a/src/librustc_trait_selection/traits/fulfill.rs b/src/librustc_trait_selection/traits/fulfill.rs
index 1e056c96acd..98f6ac0e547 100644
--- a/src/librustc_trait_selection/traits/fulfill.rs
+++ b/src/librustc_trait_selection/traits/fulfill.rs
@@ -2,9 +2,11 @@ use crate::infer::{InferCtxt, TyOrConstInferVar};
 use rustc_data_structures::obligation_forest::ProcessResult;
 use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation};
 use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
+use rustc_errors::ErrorReported;
 use rustc_infer::traits::{TraitEngine, TraitEngineExt as _};
+use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::error::ExpectedFound;
-use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeFoldable};
+use rustc_middle::ty::{self, Const, ToPolyTraitRef, Ty, TypeFoldable};
 use std::marker::PhantomData;
 
 use super::project;
@@ -520,6 +522,68 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
                     Err(err) => ProcessResult::Error(CodeSelectionError(ConstEvalFailure(err))),
                 }
             }
+
+            ty::Predicate::ConstEquate(c1, c2) => {
+                debug!("equating consts: c1={:?} c2={:?}", c1, c2);
+
+                let stalled_on = &mut pending_obligation.stalled_on;
+
+                let mut evaluate = |c: &'tcx Const<'tcx>| {
+                    if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
+                        match self.selcx.infcx().const_eval_resolve(
+                            obligation.param_env,
+                            def_id,
+                            substs,
+                            promoted,
+                            Some(obligation.cause.span),
+                        ) {
+                            Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)),
+                            Err(ErrorHandled::TooGeneric) => {
+                                stalled_on.append(
+                                    &mut substs
+                                        .types()
+                                        .filter_map(|ty| TyOrConstInferVar::maybe_from_ty(ty))
+                                        .collect(),
+                                );
+                                Err(ErrorHandled::TooGeneric)
+                            }
+                            Err(err) => Err(err),
+                        }
+                    } else {
+                        Ok(c)
+                    }
+                };
+
+                match (evaluate(c1), evaluate(c2)) {
+                    (Ok(c1), Ok(c2)) => {
+                        match self
+                            .selcx
+                            .infcx()
+                            .at(&obligation.cause, obligation.param_env)
+                            .eq(c1, c2)
+                        {
+                            Ok(_) => ProcessResult::Changed(vec![]),
+                            Err(err) => {
+                                ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError(
+                                    ExpectedFound::new(true, c1, c2),
+                                    err,
+                                ))
+                            }
+                        }
+                    }
+                    (Err(ErrorHandled::Reported(ErrorReported)), _)
+                    | (_, Err(ErrorHandled::Reported(ErrorReported))) => ProcessResult::Error(
+                        CodeSelectionError(ConstEvalFailure(ErrorHandled::Reported(ErrorReported))),
+                    ),
+                    (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!(
+                        obligation.cause.span(self.selcx.tcx()),
+                        "ConstEquate: const_eval_resolve returned an unexpected error"
+                    ),
+                    (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
+                        ProcessResult::Unchanged
+                    }
+                }
+            }
         }
     }
 
diff --git a/src/librustc_trait_selection/traits/object_safety.rs b/src/librustc_trait_selection/traits/object_safety.rs
index 96b2b904e65..1bfcacd6ccd 100644
--- a/src/librustc_trait_selection/traits/object_safety.rs
+++ b/src/librustc_trait_selection/traits/object_safety.rs
@@ -16,8 +16,9 @@ use crate::traits::{self, Obligation, ObligationCause};
 use rustc_errors::{Applicability, FatalError};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst};
-use rustc_middle::ty::{self, Predicate, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
+use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
+use rustc_middle::ty::{Predicate, ToPredicate};
 use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY;
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
@@ -281,7 +282,8 @@ fn predicates_reference_self(
                 | ty::Predicate::RegionOutlives(..)
                 | ty::Predicate::ClosureKind(..)
                 | ty::Predicate::Subtype(..)
-                | ty::Predicate::ConstEvaluatable(..) => None,
+                | ty::Predicate::ConstEvaluatable(..)
+                | ty::Predicate::ConstEquate(..) => None,
             }
         })
         .collect()
@@ -313,7 +315,8 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
         | ty::Predicate::ObjectSafe(..)
         | ty::Predicate::ClosureKind(..)
         | ty::Predicate::TypeOutlives(..)
-        | ty::Predicate::ConstEvaluatable(..) => false,
+        | ty::Predicate::ConstEvaluatable(..)
+        | ty::Predicate::ConstEquate(..) => false,
     })
 }
 
@@ -724,51 +727,65 @@ fn contains_illegal_self_type_reference<'tcx>(
     // object type, and we cannot resolve `Self as SomeOtherTrait`
     // without knowing what `Self` is.
 
-    let mut supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>> = None;
-    let self_ty = tcx.types.self_param;
-
-    let mut walker = ty.walk();
-    while let Some(arg) = walker.next() {
-        if arg == self_ty.into() {
-            return true;
-        }
-
-        // Special-case projections (everything else is walked normally).
-        if let GenericArgKind::Type(ty) = arg.unpack() {
-            if let ty::Projection(ref data) = ty.kind {
-                // This is a projected type `<Foo as SomeTrait>::X`.
+    struct IllegalSelfTypeVisitor<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        self_ty: Ty<'tcx>,
+        trait_def_id: DefId,
+        supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>>,
+    }
 
-                // Compute supertraits of current trait lazily.
-                if supertraits.is_none() {
-                    let trait_ref = ty::Binder::bind(ty::TraitRef::identity(tcx, trait_def_id));
-                    supertraits = Some(traits::supertraits(tcx, trait_ref).collect());
-                }
+    impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+            match t.kind {
+                ty::Param(_) => t == self.self_ty,
+                ty::Projection(ref data) => {
+                    // This is a projected type `<Foo as SomeTrait>::X`.
+
+                    // Compute supertraits of current trait lazily.
+                    if self.supertraits.is_none() {
+                        let trait_ref =
+                            ty::Binder::bind(ty::TraitRef::identity(self.tcx, self.trait_def_id));
+                        self.supertraits = Some(traits::supertraits(self.tcx, trait_ref).collect());
+                    }
 
-                // Determine whether the trait reference `Foo as
-                // SomeTrait` is in fact a supertrait of the
-                // current trait. In that case, this type is
-                // legal, because the type `X` will be specified
-                // in the object type.  Note that we can just use
-                // direct equality here because all of these types
-                // are part of the formal parameter listing, and
-                // hence there should be no inference variables.
-                let projection_trait_ref = ty::Binder::bind(data.trait_ref(tcx));
-                let is_supertrait_of_current_trait =
-                    supertraits.as_ref().unwrap().contains(&projection_trait_ref);
-
-                if is_supertrait_of_current_trait {
-                    // Do not walk contained types, do not report error, do collect $200.
-                    walker.skip_current_subtree();
+                    // Determine whether the trait reference `Foo as
+                    // SomeTrait` is in fact a supertrait of the
+                    // current trait. In that case, this type is
+                    // legal, because the type `X` will be specified
+                    // in the object type.  Note that we can just use
+                    // direct equality here because all of these types
+                    // are part of the formal parameter listing, and
+                    // hence there should be no inference variables.
+                    let projection_trait_ref = ty::Binder::bind(data.trait_ref(self.tcx));
+                    let is_supertrait_of_current_trait =
+                        self.supertraits.as_ref().unwrap().contains(&projection_trait_ref);
+
+                    if is_supertrait_of_current_trait {
+                        false // do not walk contained types, do not report error, do collect $200
+                    } else {
+                        t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
+                    }
                 }
-
-                // DO walk contained types, POSSIBLY reporting an error.
+                _ => t.super_visit_with(self), // walk contained types, if any
             }
         }
 
-        // Walk contained types, if any.
+        fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool {
+            // FIXME(#72219) Look into the unevaluated constants for object safety violations.
+            // Do not walk substitutions of unevaluated consts, as they contain `Self`, even
+            // though the const expression doesn't necessary use it. Currently type variables
+            // inside array length expressions are forbidden, so they can't break the above
+            // rules.
+            false
+        }
     }
 
-    false
+    ty.visit_with(&mut IllegalSelfTypeVisitor {
+        tcx,
+        self_ty: tcx.types.self_param,
+        trait_def_id,
+        supertraits: None,
+    })
 }
 
 pub fn provide(providers: &mut ty::query::Providers<'_>) {
diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs
index 0779882b6dd..c4cb72fa08c 100644
--- a/src/librustc_trait_selection/traits/project.rs
+++ b/src/librustc_trait_selection/traits/project.rs
@@ -388,8 +388,12 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
     }
 
     fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
-        let constant = constant.super_fold_with(self);
-        constant.eval(self.selcx.tcx(), self.param_env)
+        if self.selcx.tcx().lazy_normalization() {
+            constant
+        } else {
+            let constant = constant.super_fold_with(self);
+            constant.eval(self.selcx.tcx(), self.param_env)
+        }
     }
 }
 
diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs
index f0ff30232b9..70c6cbef102 100644
--- a/src/librustc_trait_selection/traits/select.rs
+++ b/src/librustc_trait_selection/traits/select.rs
@@ -38,11 +38,13 @@ use crate::traits::project::ProjectionCacheKeyExt;
 use rustc_ast::attr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::ErrorReported;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
+use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::fast_reject;
 use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
@@ -503,9 +505,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     None,
                 ) {
                     Ok(_) => Ok(EvaluatedToOk),
+                    Err(ErrorHandled::TooGeneric) => Ok(EvaluatedToAmbig),
                     Err(_) => Ok(EvaluatedToErr),
                 }
             }
+
+            ty::Predicate::ConstEquate(c1, c2) => {
+                debug!("evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", c1, c2);
+
+                let evaluate = |c: &'tcx ty::Const<'tcx>| {
+                    if let ty::ConstKind::Unevaluated(def_id, substs, promoted) = c.val {
+                        self.infcx
+                            .const_eval_resolve(
+                                obligation.param_env,
+                                def_id,
+                                substs,
+                                promoted,
+                                Some(obligation.cause.span),
+                            )
+                            .map(|val| ty::Const::from_value(self.tcx(), val, c.ty))
+                    } else {
+                        Ok(c)
+                    }
+                };
+
+                match (evaluate(c1), evaluate(c2)) {
+                    (Ok(c1), Ok(c2)) => {
+                        match self.infcx().at(&obligation.cause, obligation.param_env).eq(c1, c2) {
+                            Ok(_) => Ok(EvaluatedToOk),
+                            Err(_) => Ok(EvaluatedToErr),
+                        }
+                    }
+                    (Err(ErrorHandled::Reported(ErrorReported)), _)
+                    | (_, Err(ErrorHandled::Reported(ErrorReported))) => Ok(EvaluatedToErr),
+                    (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => span_bug!(
+                        obligation.cause.span(self.tcx()),
+                        "ConstEquate: const_eval_resolve returned an unexpected error"
+                    ),
+                    (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => {
+                        Ok(EvaluatedToAmbig)
+                    }
+                }
+            }
         }
     }
 
diff --git a/src/librustc_trait_selection/traits/wf.rs b/src/librustc_trait_selection/traits/wf.rs
index ba7ec96775c..4d3bbfa77c3 100644
--- a/src/librustc_trait_selection/traits/wf.rs
+++ b/src/librustc_trait_selection/traits/wf.rs
@@ -102,6 +102,10 @@ pub fn predicate_obligations<'a, 'tcx>(
                 wf.compute(ty);
             }
         }
+        ty::Predicate::ConstEquate(c1, c2) => {
+            wf.compute(c1.ty);
+            wf.compute(c2.ty);
+        }
     }
 
     wf.normalize()
diff --git a/src/librustc_traits/chalk/lowering.rs b/src/librustc_traits/chalk/lowering.rs
index aacbd311d1d..184b9a9dc10 100644
--- a/src/librustc_traits/chalk/lowering.rs
+++ b/src/librustc_traits/chalk/lowering.rs
@@ -126,9 +126,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'
                     | ty::Predicate::ObjectSafe(..)
                     | ty::Predicate::ClosureKind(..)
                     | ty::Predicate::Subtype(..)
-                    | ty::Predicate::ConstEvaluatable(..) => {
-                        bug!("unexpected predicate {}", predicate)
-                    }
+                    | ty::Predicate::ConstEvaluatable(..)
+                    | ty::Predicate::ConstEquate(..) => bug!("unexpected predicate {}", predicate),
                 }
             }
             ChalkEnvironmentClause::TypeFromEnv(ty) => Some(
@@ -192,9 +191,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi
             Predicate::ObjectSafe(..)
             | Predicate::ClosureKind(..)
             | Predicate::Subtype(..)
-            | Predicate::ConstEvaluatable(..) => {
-                chalk_ir::GoalData::All(chalk_ir::Goals::new(interner))
-            }
+            | Predicate::ConstEvaluatable(..)
+            | Predicate::ConstEquate(..) => chalk_ir::GoalData::All(chalk_ir::Goals::new(interner)),
         }
     }
 }
@@ -459,7 +457,8 @@ impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'
             Predicate::ObjectSafe(..)
             | Predicate::ClosureKind(..)
             | Predicate::Subtype(..)
-            | Predicate::ConstEvaluatable(..) => bug!("unexpected predicate {}", &self),
+            | Predicate::ConstEvaluatable(..)
+            | Predicate::ConstEquate(..) => bug!("unexpected predicate {}", &self),
         }
     }
 }
diff --git a/src/librustc_traits/implied_outlives_bounds.rs b/src/librustc_traits/implied_outlives_bounds.rs
index 6db2e557fea..eaaab87ab74 100644
--- a/src/librustc_traits/implied_outlives_bounds.rs
+++ b/src/librustc_traits/implied_outlives_bounds.rs
@@ -100,7 +100,8 @@ fn compute_implied_outlives_bounds<'tcx>(
                 | ty::Predicate::Projection(..)
                 | ty::Predicate::ClosureKind(..)
                 | ty::Predicate::ObjectSafe(..)
-                | ty::Predicate::ConstEvaluatable(..) => vec![],
+                | ty::Predicate::ConstEvaluatable(..)
+                | ty::Predicate::ConstEquate(..) => vec![],
 
                 ty::Predicate::WellFormed(subty) => {
                     wf_types.push(subty);
diff --git a/src/librustc_traits/normalize_erasing_regions.rs b/src/librustc_traits/normalize_erasing_regions.rs
index ad6c753edff..ed30ed5313e 100644
--- a/src/librustc_traits/normalize_erasing_regions.rs
+++ b/src/librustc_traits/normalize_erasing_regions.rs
@@ -48,6 +48,7 @@ fn not_outlives_predicate(p: &ty::Predicate<'_>) -> bool {
         | ty::Predicate::ObjectSafe(..)
         | ty::Predicate::ClosureKind(..)
         | ty::Predicate::Subtype(..)
-        | ty::Predicate::ConstEvaluatable(..) => true,
+        | ty::Predicate::ConstEvaluatable(..)
+        | ty::Predicate::ConstEquate(..) => true,
     }
 }
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 7f1d77e5b97..e21db9035e2 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -810,7 +810,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             | ty::Predicate::ObjectSafe(..)
             | ty::Predicate::ClosureKind(..)
             | ty::Predicate::TypeOutlives(..)
-            | ty::Predicate::ConstEvaluatable(..) => None,
+            | ty::Predicate::ConstEvaluatable(..)
+            | ty::Predicate::ConstEquate(..) => None,
         });
 
         self.elaborate_bounds(bounds, |this, poly_trait_ref, item| {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 6bf836015d2..d72c74e4188 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1648,6 +1648,16 @@ fn check_opaque_for_inheriting_lifetimes(tcx: TyCtxt<'tcx>, def_id: LocalDefId,
 
             r.super_visit_with(self)
         }
+
+        fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
+            if let ty::ConstKind::Unevaluated(..) = c.val {
+                // FIXME(#72219) We currenctly don't detect lifetimes within substs
+                // which would violate this check. Even though the particular substitution is not used
+                // within the const, this should still be fixed.
+                return false;
+            }
+            c.super_visit_with(self)
+        }
     }
 
     let prohibit_opaque = match item.kind {
@@ -3858,6 +3868,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ty::Predicate::WellFormed(..) => None,
                 ty::Predicate::ObjectSafe(..) => None,
                 ty::Predicate::ConstEvaluatable(..) => None,
+                ty::Predicate::ConstEquate(..) => None,
                 // N.B., this predicate is created by breaking down a
                 // `ClosureType: FnFoo()` predicate, where
                 // `ClosureType` represents some `Closure`. It can't
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 854bd03b264..7cfb89f3ff0 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1164,7 +1164,8 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
             let parent_id = tcx.hir().get_parent_item(hir_id);
             Some(tcx.hir().local_def_id(parent_id).to_def_id())
         }
-        // FIXME(#43408) enable this always when we get lazy normalization.
+        // FIXME(#43408) always enable this once `lazy_normalization` is
+        // stable enough and does not need a feature gate anymore.
         Node::AnonConst(_) => {
             let parent_id = tcx.hir().get_parent_item(hir_id);
             let parent_def_id = tcx.hir().local_def_id(parent_id);
@@ -1172,7 +1173,7 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
             // 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.
-            if tcx.features().const_generics {
+            if tcx.lazy_normalization() {
                 Some(parent_def_id.to_def_id())
             } else {
                 let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
diff --git a/src/librustc_typeck/impl_wf_check/min_specialization.rs b/src/librustc_typeck/impl_wf_check/min_specialization.rs
index 8b12535b3a2..919bcc9943d 100644
--- a/src/librustc_typeck/impl_wf_check/min_specialization.rs
+++ b/src/librustc_typeck/impl_wf_check/min_specialization.rs
@@ -413,6 +413,7 @@ fn trait_predicate_kind<'tcx>(
         | ty::Predicate::Subtype(_)
         | ty::Predicate::ObjectSafe(_)
         | ty::Predicate::ClosureKind(..)
-        | ty::Predicate::ConstEvaluatable(..) => None,
+        | ty::Predicate::ConstEvaluatable(..)
+        | ty::Predicate::ConstEquate(..) => None,
     }
 }
diff --git a/src/librustc_typeck/outlives/explicit.rs b/src/librustc_typeck/outlives/explicit.rs
index 168f2077147..66daf0e7f7d 100644
--- a/src/librustc_typeck/outlives/explicit.rs
+++ b/src/librustc_typeck/outlives/explicit.rs
@@ -58,7 +58,8 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
                     | ty::Predicate::ObjectSafe(..)
                     | ty::Predicate::ClosureKind(..)
                     | ty::Predicate::Subtype(..)
-                    | ty::Predicate::ConstEvaluatable(..) => (),
+                    | ty::Predicate::ConstEvaluatable(..)
+                    | ty::Predicate::ConstEquate(..) => (),
                 }
             }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 6c001bc5484..c130ed3f46d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -493,7 +493,8 @@ impl<'a> Clean<Option<WherePredicate>> for ty::Predicate<'a> {
             Predicate::WellFormed(..)
             | Predicate::ObjectSafe(..)
             | Predicate::ClosureKind(..)
-            | Predicate::ConstEvaluatable(..) => panic!("not user writable"),
+            | Predicate::ConstEvaluatable(..)
+            | Predicate::ConstEquate(..) => panic!("not user writable"),
         }
     }
 }
diff --git a/src/test/ui/const-generics/different_byref.stderr b/src/test/ui/const-generics/different_byref.stderr
index 001d9852a69..7eb826b8a36 100644
--- a/src/test/ui/const-generics/different_byref.stderr
+++ b/src/test/ui/const-generics/different_byref.stderr
@@ -13,8 +13,8 @@ error[E0308]: mismatched types
 LL |     x = Const::<{ [4] }> {};
    |         ^^^^^^^^^^^^^^^^^^^ expected `3usize`, found `4usize`
    |
-   = note: expected struct `Const<[3usize]>`
-              found struct `Const<[4usize]>`
+   = note: expected type `[3usize]`
+              found type `[4usize]`
 
 error: aborting due to previous error; 1 warning emitted
 
diff --git a/src/test/ui/const-generics/fn-const-param-infer.stderr b/src/test/ui/const-generics/fn-const-param-infer.stderr
index 3e07393b9aa..de41d2984a6 100644
--- a/src/test/ui/const-generics/fn-const-param-infer.stderr
+++ b/src/test/ui/const-generics/fn-const-param-infer.stderr
@@ -11,12 +11,10 @@ error[E0308]: mismatched types
   --> $DIR/fn-const-param-infer.rs:16:31
    |
 LL |     let _: Checked<not_one> = Checked::<not_two>;
-   |            ----------------   ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
-   |            |
-   |            expected due to this
+   |                               ^^^^^^^^^^^^^^^^^^ expected `{not_one as fn(usize) -> bool}`, found `{not_two as fn(usize) -> bool}`
    |
-   = note: expected struct `Checked<{not_one as fn(usize) -> bool}>`
-              found struct `Checked<{not_two as fn(usize) -> bool}>`
+   = note: expected type `{not_one as fn(usize) -> bool}`
+              found type `{not_two as fn(usize) -> bool}`
 
 error[E0308]: mismatched types
   --> $DIR/fn-const-param-infer.rs:20:24
@@ -37,12 +35,10 @@ error[E0308]: mismatched types
   --> $DIR/fn-const-param-infer.rs:25:40
    |
 LL |     let _: Checked<{generic::<u32>}> = Checked::<{generic::<u16>}>;
-   |            -------------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
-   |            |
-   |            expected due to this
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{generic::<u32> as fn(usize) -> bool}`, found `{generic::<u16> as fn(usize) -> bool}`
    |
-   = note: expected struct `Checked<{generic::<u32> as fn(usize) -> bool}>`
-              found struct `Checked<{generic::<u16> as fn(usize) -> bool}>`
+   = note: expected type `{generic::<u32> as fn(usize) -> bool}`
+              found type `{generic::<u16> as fn(usize) -> bool}`
 
 error: aborting due to 4 previous errors; 1 warning emitted
 
diff --git a/src/test/ui/const-generics/issues/issue-61935.rs b/src/test/ui/const-generics/issues/issue-61935.rs
new file mode 100644
index 00000000000..5c987e63a9e
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-61935.rs
@@ -0,0 +1,24 @@
+// check-pass
+
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete
+
+trait Foo {}
+
+impl<const N: usize> Foo for [(); N]
+    where
+        Self:FooImpl<{N==0}>
+{}
+
+trait FooImpl<const IS_ZERO: bool>{}
+
+impl FooImpl<true> for [(); 0] {}
+
+impl<const N:usize> FooImpl<false> for [();N] {}
+
+fn foo(_: impl Foo) {}
+
+fn main() {
+    foo([]);
+    foo([()]);
+}
diff --git a/src/test/ui/const-generics/issues/issue-61935.stderr b/src/test/ui/const-generics/issues/issue-61935.stderr
new file mode 100644
index 00000000000..cf0c0e24a76
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-61935.stderr
@@ -0,0 +1,11 @@
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-61935.rs:3:12
+   |
+LL | #![feature(const_generics)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/const-generics/issues/issue-62504.rs b/src/test/ui/const-generics/issues/issue-62504.rs
index 264e693a008..4e05aadd393 100644
--- a/src/test/ui/const-generics/issues/issue-62504.rs
+++ b/src/test/ui/const-generics/issues/issue-62504.rs
@@ -16,8 +16,7 @@ struct ArrayHolder<const X: usize>([u32; X]);
 impl<const X: usize> ArrayHolder<X> {
     pub const fn new() -> Self {
         ArrayHolder([0; Self::SIZE])
-        //~^ ERROR: mismatched types
-        //~| ERROR constant expression depends on a generic parameter
+        //~^ ERROR constant expression depends on a generic parameter
     }
 }
 
diff --git a/src/test/ui/const-generics/issues/issue-62504.stderr b/src/test/ui/const-generics/issues/issue-62504.stderr
index 5d45e302888..f09af76325e 100644
--- a/src/test/ui/const-generics/issues/issue-62504.stderr
+++ b/src/test/ui/const-generics/issues/issue-62504.stderr
@@ -1,12 +1,3 @@
-error[E0308]: mismatched types
-  --> $DIR/issue-62504.rs:18:21
-   |
-LL |         ArrayHolder([0; Self::SIZE])
-   |                     ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`
-   |
-   = note: expected array `[u32; X]`
-              found array `[u32; _]`
-
 error: constant expression depends on a generic parameter
   --> $DIR/issue-62504.rs:18:25
    |
@@ -15,6 +6,5 @@ LL |         ArrayHolder([0; Self::SIZE])
    |
    = note: this may fail depending on what value the parameter takes
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/const-generics/issues/issue-66205.rs b/src/test/ui/const-generics/issues/issue-66205.rs
index 73ba4fa6aae..76bde1815be 100644
--- a/src/test/ui/const-generics/issues/issue-66205.rs
+++ b/src/test/ui/const-generics/issues/issue-66205.rs
@@ -1,5 +1,6 @@
 #![allow(incomplete_features, dead_code, unconditional_recursion)]
 #![feature(const_generics)]
+#![feature(lazy_normalization_consts)]
 
 fn fact<const N: usize>() {
     fact::<{ N - 1 }>();
diff --git a/src/test/ui/const-generics/issues/issue-66205.stderr b/src/test/ui/const-generics/issues/issue-66205.stderr
index 2bd013e8b41..416b675b56d 100644
--- a/src/test/ui/const-generics/issues/issue-66205.stderr
+++ b/src/test/ui/const-generics/issues/issue-66205.stderr
@@ -1,5 +1,5 @@
 error: constant expression depends on a generic parameter
-  --> $DIR/issue-66205.rs:5:12
+  --> $DIR/issue-66205.rs:6:12
    |
 LL |     fact::<{ N - 1 }>();
    |            ^^^^^^^^^
diff --git a/src/test/ui/const-generics/issues/issue-67185-1.rs b/src/test/ui/const-generics/issues/issue-67185-1.rs
new file mode 100644
index 00000000000..b08057851a1
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-67185-1.rs
@@ -0,0 +1,32 @@
+// check-pass
+
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete
+
+trait Baz {
+    type Quaks;
+}
+impl Baz for u8 {
+    type Quaks = [u16; 3];
+}
+
+trait Bar {}
+impl Bar for [u16; 3] {}
+impl Bar for [[u16; 3]; 2] {}
+
+trait Foo
+    where
+        [<u8 as Baz>::Quaks; 2]: Bar,
+        <u8 as Baz>::Quaks: Bar,
+{
+}
+
+struct FooImpl;
+
+impl Foo for FooImpl {}
+
+fn f(_: impl Foo) {}
+
+fn main() {
+    f(FooImpl)
+}
diff --git a/src/test/ui/const-generics/issues/issue-67185-1.stderr b/src/test/ui/const-generics/issues/issue-67185-1.stderr
new file mode 100644
index 00000000000..9cc797d6d8a
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-67185-1.stderr
@@ -0,0 +1,11 @@
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-67185-1.rs:3:12
+   |
+LL | #![feature(const_generics)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/const-generics/issues/issue-67185-2.rs b/src/test/ui/const-generics/issues/issue-67185-2.rs
new file mode 100644
index 00000000000..111b718dd5e
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-67185-2.rs
@@ -0,0 +1,35 @@
+#![feature(const_generics)]
+//~^ WARN the feature `const_generics` is incomplete
+
+trait Baz {
+    type Quaks;
+}
+impl Baz for u8 {
+    type Quaks = [u16; 3];
+}
+
+trait Bar {}
+impl Bar for [u16; 4] {}
+impl Bar for [[u16; 3]; 3] {}
+
+trait Foo  //~ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
+           //~^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
+    where
+        [<u8 as Baz>::Quaks; 2]: Bar,
+        <u8 as Baz>::Quaks: Bar,
+{
+}
+
+struct FooImpl;
+
+impl Foo for FooImpl {}
+//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
+//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
+
+fn f(_: impl Foo) {}
+//~^ ERROR the trait bound `[u16; 3]: Bar` is not satisfied [E0277]
+//~^^ ERROR the trait bound `[[u16; 3]; 2]: Bar` is not satisfied [E0277]
+
+fn main() {
+    f(FooImpl)
+}
diff --git a/src/test/ui/const-generics/issues/issue-67185-2.stderr b/src/test/ui/const-generics/issues/issue-67185-2.stderr
new file mode 100644
index 00000000000..7d947a907a0
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-67185-2.stderr
@@ -0,0 +1,112 @@
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-67185-2.rs:1:12
+   |
+LL | #![feature(const_generics)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
+  --> $DIR/issue-67185-2.rs:15:1
+   |
+LL | / trait Foo
+LL | |
+LL | |     where
+LL | |         [<u8 as Baz>::Quaks; 2]: Bar,
+LL | |         <u8 as Baz>::Quaks: Bar,
+LL | | {
+LL | | }
+   | |_^ the trait `Bar` is not implemented for `[u16; 3]`
+   |
+   = help: the following implementations were found:
+             <[[u16; 3]; 3] as Bar>
+             <[u16; 4] as Bar>
+   = help: see issue #48214
+   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+
+error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
+  --> $DIR/issue-67185-2.rs:15:1
+   |
+LL | / trait Foo
+LL | |
+LL | |     where
+LL | |         [<u8 as Baz>::Quaks; 2]: Bar,
+LL | |         <u8 as Baz>::Quaks: Bar,
+LL | | {
+LL | | }
+   | |_^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
+   |
+   = help: the following implementations were found:
+             <[[u16; 3]; 3] as Bar>
+             <[u16; 4] as Bar>
+   = help: see issue #48214
+   = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+
+error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
+  --> $DIR/issue-67185-2.rs:25:6
+   |
+LL | trait Foo
+   |       --- required by a bound in this
+...
+LL |         <u8 as Baz>::Quaks: Bar,
+   |                             --- required by this bound in `Foo`
+...
+LL | impl Foo for FooImpl {}
+   |      ^^^ the trait `Bar` is not implemented for `[u16; 3]`
+   |
+   = help: the following implementations were found:
+             <[[u16; 3]; 3] as Bar>
+             <[u16; 4] as Bar>
+
+error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
+  --> $DIR/issue-67185-2.rs:25:6
+   |
+LL | trait Foo
+   |       --- required by a bound in this
+...
+LL |         [<u8 as Baz>::Quaks; 2]: Bar,
+   |                                  --- required by this bound in `Foo`
+...
+LL | impl Foo for FooImpl {}
+   |      ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
+   |
+   = help: the following implementations were found:
+             <[[u16; 3]; 3] as Bar>
+             <[u16; 4] as Bar>
+
+error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
+  --> $DIR/issue-67185-2.rs:29:14
+   |
+LL | trait Foo
+   |       --- required by a bound in this
+...
+LL |         [<u8 as Baz>::Quaks; 2]: Bar,
+   |                                  --- required by this bound in `Foo`
+...
+LL | fn f(_: impl Foo) {}
+   |              ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
+   |
+   = help: the following implementations were found:
+             <[[u16; 3]; 3] as Bar>
+             <[u16; 4] as Bar>
+
+error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
+  --> $DIR/issue-67185-2.rs:29:14
+   |
+LL | trait Foo
+   |       --- required by a bound in this
+...
+LL |         <u8 as Baz>::Quaks: Bar,
+   |                             --- required by this bound in `Foo`
+...
+LL | fn f(_: impl Foo) {}
+   |              ^^^ the trait `Bar` is not implemented for `[u16; 3]`
+   |
+   = help: the following implementations were found:
+             <[[u16; 3]; 3] as Bar>
+             <[u16; 4] as Bar>
+
+error: aborting due to 6 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/issues/issue-69654.rs b/src/test/ui/const-generics/issues/issue-69654.rs
deleted file mode 100644
index 2befbe56d85..00000000000
--- a/src/test/ui/const-generics/issues/issue-69654.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-#![feature(const_generics)]
-#![allow(incomplete_features)]
-
-trait Bar<O> {}
-impl<O> Bar<O> for [u8; O] {}
-//~^ ERROR expected value, found type parameter `O`
-
-struct Foo<const O: usize> {}
-impl<const O: usize> Foo<O>
-where
-    [u8; O]: Bar<[(); O]>,
-{
-    fn foo() {}
-}
-
-fn main() {
-    Foo::foo();
-}
diff --git a/src/test/ui/const-generics/issues/issue-69654.stderr b/src/test/ui/const-generics/issues/issue-69654.stderr
deleted file mode 100644
index 9d52603f462..00000000000
--- a/src/test/ui/const-generics/issues/issue-69654.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error[E0423]: expected value, found type parameter `O`
-  --> $DIR/issue-69654.rs:5:25
-   |
-LL | impl<O> Bar<O> for [u8; O] {}
-   |                         ^ help: a tuple variant with a similar name exists: `Ok`
-   | 
-  ::: $SRC_DIR/libcore/result.rs:LL:COL
-   |
-LL |     Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
-   |     --------------------------------------------------- similarly named tuple variant `Ok` defined here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0423`.
diff --git a/src/test/ui/const-generics/lazy-normalization/issue-71922.rs b/src/test/ui/const-generics/lazy-normalization/issue-71922.rs
new file mode 100644
index 00000000000..36513f94a9e
--- /dev/null
+++ b/src/test/ui/const-generics/lazy-normalization/issue-71922.rs
@@ -0,0 +1,19 @@
+// run-pass
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+trait Foo {}
+
+impl<const N: usize> Foo for [(); N] where Self: FooImpl<{ N == 0 }> {}
+
+trait FooImpl<const IS_ZERO: bool> {}
+
+impl FooImpl<{ 0u8 == 0u8 }> for [(); 0] {}
+
+impl<const N: usize> FooImpl<{ 0u8 != 0u8 }> for [(); N] {}
+
+fn foo<T: Foo>(_: T) {}
+
+fn main() {
+    foo([]);
+    foo([()]);
+}
diff --git a/src/test/ui/const-generics/lazy-normalization/issue-71986.rs b/src/test/ui/const-generics/lazy-normalization/issue-71986.rs
new file mode 100644
index 00000000000..048ed18c927
--- /dev/null
+++ b/src/test/ui/const-generics/lazy-normalization/issue-71986.rs
@@ -0,0 +1,8 @@
+// check-pass
+#![allow(incomplete_features)]
+#![feature(const_generics)]
+
+pub trait Foo<const B: bool> {}
+pub fn bar<T: Foo<{ true }>>() {}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/raw-ptr-const-param.stderr b/src/test/ui/const-generics/raw-ptr-const-param.stderr
index 6644c72236b..7a665397c12 100644
--- a/src/test/ui/const-generics/raw-ptr-const-param.stderr
+++ b/src/test/ui/const-generics/raw-ptr-const-param.stderr
@@ -11,12 +11,10 @@ error[E0308]: mismatched types
   --> $DIR/raw-ptr-const-param.rs:7:40
    |
 LL |     let _: Const<{ 15 as *const _ }> = Const::<{ 10 as *const _ }>;
-   |            -------------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
-   |            |
-   |            expected due to this
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{0xf as *const u32}`, found `{0xa as *const u32}`
    |
-   = note: expected struct `Const<{0xf as *const u32}>`
-              found struct `Const<{0xa as *const u32}>`
+   = note: expected type `{0xf as *const u32}`
+              found type `{0xa as *const u32}`
 
 error: aborting due to previous error; 1 warning emitted
 
diff --git a/src/test/ui/const-generics/types-mismatch-const-args.stderr b/src/test/ui/const-generics/types-mismatch-const-args.stderr
index 2131738554f..53328c2e89b 100644
--- a/src/test/ui/const-generics/types-mismatch-const-args.stderr
+++ b/src/test/ui/const-generics/types-mismatch-const-args.stderr
@@ -11,12 +11,10 @@ error[E0308]: mismatched types
   --> $DIR/types-mismatch-const-args.rs:13:41
    |
 LL |     let _: A<'a, u32, {2u32}, {3u32}> = A::<'a, u32, {4u32}, {3u32}> { data: PhantomData };
-   |            --------------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32`
-   |            |
-   |            expected due to this
+   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2u32`, found `4u32`
    |
-   = note: expected struct `A<'_, _, 2u32, _>`
-              found struct `A<'_, _, 4u32, _>`
+   = note: expected type `2u32`
+              found type `4u32`
 
 error[E0308]: mismatched types
   --> $DIR/types-mismatch-const-args.rs:15:41
@@ -26,8 +24,8 @@ LL |     let _: A<'a, u16, {2u32}, {3u32}> = A::<'b, u32, {2u32}, {3u32}> { data
    |            |
    |            expected due to this
    |
-   = note: expected struct `A<'a, u16, _, _>`
-              found struct `A<'b, u32, _, _>`
+   = note: expected struct `A<'a, u16, {2u32}, {3u32}>`
+              found struct `A<'b, u32, {2u32}, {3u32}>`
 
 error: aborting due to 2 previous errors; 1 warning emitted
 
diff --git a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr
index b0ffc4a5ef6..88f8dbe1a7d 100644
--- a/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr
+++ b/src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr
@@ -25,7 +25,7 @@ error: non-defining opaque type use in defining scope
 LL | fn concrete_const() -> OneConst<{123}> {
    |                        ^^^^^^^^^^^^^^^
    |
-note: used non-generic constant `123usize` for generic parameter
+note: used non-generic constant `{123}` for generic parameter
   --> $DIR/generic_nondefining_use.rs:10:21
    |
 LL | type OneConst<const X: usize> = impl Debug;