about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-05-18 22:50:40 +0000
committerbors <bors@rust-lang.org>2020-05-18 22:50:40 +0000
commit89988fe727a5f055da78a4278448cfbca0c49e19 (patch)
tree627511ba79ef59483b8162d6a03d15aa59be3cfd
parentd8878868c8d7ef3779e7243953fc050cbb0e0565 (diff)
parent256ce18dcb8f3c3c77c18e087f5642d30e1e68f1 (diff)
downloadrust-89988fe727a5f055da78a4278448cfbca0c49e19.tar.gz
rust-89988fe727a5f055da78a4278448cfbca0c49e19.zip
Auto merge of #72330 - Dylan-DPC:rollup-yuxadv8, r=Dylan-DPC
Rollup of 5 pull requests

Successful merges:

 - #71599 (Support coercion between (FnDef | Closure) and (FnDef | Closure))
 - #71973 (Lazy normalization of constants (Reprise))
 - #72283 (Drop Elaboration Elaboration)
 - #72290 (Add newer rust versions to linker-plugin-lto.md)
 - #72318 (Add help text for remote-test-client)

Failed merges:

r? @ghost
-rw-r--r--src/doc/rustc/src/linker-plugin-lto.md18
-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.rs30
-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/mod.rs2
-rw-r--r--src/librustc_mir/borrow_check/type_check/relate_tys.rs6
-rw-r--r--src/librustc_mir/shim.rs13
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs3
-rw-r--r--src/librustc_mir/util/elaborate_drops.rs95
-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/coercion.rs91
-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/closures/closure_cap_coerce_many_fail.rs39
-rw-r--r--src/test/ui/closures/closure_cap_coerce_many_fail.stderr73
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs166
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs59
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs22
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr19
-rw-r--r--src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs23
-rw-r--r--src/test/ui/closures/issue-46742.rs9
-rw-r--r--src/test/ui/closures/issue-48109.rs14
-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/issues/issue-24036.rs2
-rw-r--r--src/test/ui/issues/issue-24036.stderr23
-rw-r--r--src/test/ui/type-alias-impl-trait/generic_nondefining_use.stderr2
-rw-r--r--src/tools/remote-test-client/src/main.rs56
73 files changed, 1343 insertions, 216 deletions
diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md
index 6f1bbe60569..c0b14352b7d 100644
--- a/src/doc/rustc/src/linker-plugin-lto.md
+++ b/src/doc/rustc/src/linker-plugin-lto.md
@@ -100,11 +100,17 @@ LLVM. However, the approximation is usually reliable.
 
 The following table shows known good combinations of toolchain versions.
 
-|           |  Clang 7  |  Clang 8  |
-|-----------|-----------|-----------|
-| Rust 1.34 |     ✗     |     ✓     |
-| Rust 1.35 |     ✗     |     ✓     |
-| Rust 1.36 |     ✗     |     ✓     |
-| Rust 1.37 |     ✗     |     ✓     |
+|           |  Clang 7  |  Clang 8  |  Clang 9  |
+|-----------|-----------|-----------|-----------|
+| Rust 1.34 |     ✗     |     ✓     |     ✗     |
+| Rust 1.35 |     ✗     |     ✓     |     ✗     |
+| Rust 1.36 |     ✗     |     ✓     |     ✗     |
+| Rust 1.37 |     ✗     |     ✓     |     ✗     |
+| Rust 1.38 |     ✗     |     ✗     |     ✓     |
+| Rust 1.39 |     ✗     |     ✗     |     ✓     |
+| Rust 1.40 |     ✗     |     ✗     |     ✓     |
+| Rust 1.41 |     ✗     |     ✗     |     ✓     |
+| Rust 1.42 |     ✗     |     ✗     |     ✓     |
+| Rust 1.43 |     ✗     |     ✗     |     ✓     |
 
 Note that the compatibility policy for this feature might change in the future.
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 7feb080d4b8..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());
@@ -2069,24 +2076,25 @@ impl<'tcx> TyCtxt<'tcx> {
         self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }))
     }
 
-    /// Given a closure signature `sig`, returns an equivalent `fn`
-    /// type with the same signature. Detuples and so forth -- so
-    /// e.g., if we have a sig with `Fn<(u32, i32)>` then you would get
-    /// a `fn(u32, i32)`.
-    /// `unsafety` determines the unsafety of the `fn` type. If you pass
+    /// Given a closure signature, returns an equivalent fn signature. Detuples
+    /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then
+    /// you would get a `fn(u32, i32)`.
+    /// `unsafety` determines the unsafety of the fn signature. If you pass
     /// `hir::Unsafety::Unsafe` in the previous example, then you would get
     /// an `unsafe fn (u32, i32)`.
     /// It cannot convert a closure that requires unsafe.
-    pub fn coerce_closure_fn_ty(self, sig: PolyFnSig<'tcx>, unsafety: hir::Unsafety) -> Ty<'tcx> {
-        let converted_sig = sig.map_bound(|s| {
+    pub fn signature_unclosure(
+        self,
+        sig: PolyFnSig<'tcx>,
+        unsafety: hir::Unsafety,
+    ) -> PolyFnSig<'tcx> {
+        sig.map_bound(|s| {
             let params_iter = match s.inputs()[0].kind {
                 ty::Tuple(params) => params.into_iter().map(|k| k.expect_ty()),
                 _ => bug!(),
             };
             self.mk_fn_sig(params_iter, s.output(), s.c_variadic, unsafety, abi::Abi::Rust)
-        });
-
-        self.mk_fn_ptr(converted_sig)
+        })
     }
 
     #[allow(rustc::usage_of_ty_tykind)]
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/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs
index bad176c603f..526914721be 100644
--- a/src/librustc_mir/borrow_check/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/type_check/mod.rs
@@ -2088,7 +2088,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             ty::Closure(_, substs) => substs.as_closure().sig(),
                             _ => bug!(),
                         };
-                        let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig, *unsafety);
+                        let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
 
                         if let Err(terr) = self.eq_types(
                             ty_fn_ptr_from,
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/shim.rs b/src/librustc_mir/shim.rs
index d025468d28b..e3982c654d5 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -277,7 +277,18 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> {
     }
 
     fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle {
-        if let DropFlagMode::Shallow = mode { DropStyle::Static } else { DropStyle::Open }
+        match mode {
+            DropFlagMode::Shallow => {
+                // Drops for the contained fields are "shallow" and "static" - they will simply call
+                // the field's own drop glue.
+                DropStyle::Static
+            }
+            DropFlagMode::Deep => {
+                // The top-level drop is "deep" and "open" - it will be elaborated to a drop ladder
+                // dropping each field contained in the value.
+                DropStyle::Open
+            }
+        }
     }
 
     fn get_drop_flag(&mut self, _path: Self::Path) -> Option<Operand<'tcx>> {
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_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs
index af7c88b178d..ed999c6871b 100644
--- a/src/librustc_mir/util/elaborate_drops.rs
+++ b/src/librustc_mir/util/elaborate_drops.rs
@@ -12,10 +12,15 @@ use std::fmt;
 
 use std::convert::TryInto;
 
+/// The value of an inserted drop flag.
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
 pub enum DropFlagState {
-    Present, // i.e., initialized
-    Absent,  // i.e., deinitialized or "moved"
+    /// The tracked value is initialized and needs to be dropped when leaving its scope.
+    Present,
+
+    /// The tracked value is uninitialized or was moved out of and does not need to be dropped when
+    /// leaving its scope.
+    Absent,
 }
 
 impl DropFlagState {
@@ -27,23 +32,42 @@ impl DropFlagState {
     }
 }
 
+/// Describes how/if a value should be dropped.
 #[derive(Debug)]
 pub enum DropStyle {
+    /// The value is already dead at the drop location, no drop will be executed.
     Dead,
+
+    /// The value is known to always be initialized at the drop location, drop will always be
+    /// executed.
     Static,
+
+    /// Whether the value needs to be dropped depends on its drop flag.
     Conditional,
+
+    /// An "open" drop is one where only the fields of a value are dropped.
+    ///
+    /// For example, this happens when moving out of a struct field: The rest of the struct will be
+    /// dropped in such an "open" drop. It is also used to generate drop glue for the individual
+    /// components of a value, for example for dropping array elements.
     Open,
 }
 
+/// Which drop flags to affect/check with an operation.
 #[derive(Debug)]
 pub enum DropFlagMode {
+    /// Only affect the top-level drop flag, not that of any contained fields.
     Shallow,
+    /// Affect all nested drop flags in addition to the top-level one.
     Deep,
 }
 
+/// Describes if unwinding is necessary and where to unwind to if a panic occurs.
 #[derive(Copy, Clone, Debug)]
 pub enum Unwind {
+    /// Unwind to this block.
     To(BasicBlock),
+    /// Already in an unwind path, any panic will cause an abort.
     InCleanup,
 }
 
@@ -74,20 +98,58 @@ impl Unwind {
 }
 
 pub trait DropElaborator<'a, 'tcx>: fmt::Debug {
+    /// The type representing paths that can be moved out of.
+    ///
+    /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to
+    /// represent such move paths. Sometimes tracking individual move paths is not necessary, in
+    /// which case this may be set to (for example) `()`.
     type Path: Copy + fmt::Debug;
 
+    // Accessors
+
     fn patch(&mut self) -> &mut MirPatch<'tcx>;
     fn body(&self) -> &'a Body<'tcx>;
     fn tcx(&self) -> TyCtxt<'tcx>;
     fn param_env(&self) -> ty::ParamEnv<'tcx>;
 
+    // Drop logic
+
+    /// Returns how `path` should be dropped, given `mode`.
     fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle;
+
+    /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag).
     fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>>;
+
+    /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`.
+    ///
+    /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting
+    /// additional statements.
     fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode);
 
+    // Subpaths
+
+    /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath).
+    ///
+    /// If this returns `None`, `field` will not get a dedicated drop flag.
     fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path>;
+
+    /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath).
+    ///
+    /// If this returns `None`, `*path` will not get a dedicated drop flag.
+    ///
+    /// This is only relevant for `Box<T>`, where the contained `T` can be moved out of the box.
     fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path>;
+
+    /// Returns the subpath of downcasting `path` to one of its variants.
+    ///
+    /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag.
     fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option<Self::Path>;
+
+    /// Returns the subpath of indexing a fixed-size array `path`.
+    ///
+    /// If this returns `None`, elements of `path` will not get a dedicated drop flag.
+    ///
+    /// This is only relevant for array patterns, which can move out of individual array elements.
     fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option<Self::Path>;
 }
 
@@ -106,6 +168,14 @@ where
     unwind: Unwind,
 }
 
+/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it.
+///
+/// The passed `elaborator` is used to determine what should happen at the drop terminator. It
+/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag,
+/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped
+/// value.
+///
+/// When this returns, the MIR patch in the `elaborator` contains the necessary changes.
 pub fn elaborate_drop<'b, 'tcx, D>(
     elaborator: &mut D,
     source_info: SourceInfo,
@@ -346,9 +416,7 @@ where
         let interior = self.tcx().mk_place_deref(self.place);
         let interior_path = self.elaborator.deref_subpath(self.path);
 
-        let succ = self.succ; // FIXME(#43234)
-        let unwind = self.unwind;
-        let succ = self.box_free_block(adt, substs, succ, unwind);
+        let succ = self.box_free_block(adt, substs, self.succ, self.unwind);
         let unwind_succ =
             self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup));
 
@@ -829,6 +897,8 @@ where
         self.drop_flag_test_block(drop_block, succ, unwind)
     }
 
+    /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will
+    /// also be cleared.
     fn drop_flag_reset_block(
         &mut self,
         mode: DropFlagMode,
@@ -850,13 +920,15 @@ where
 
     fn elaborated_drop_block(&mut self) -> BasicBlock {
         debug!("elaborated_drop_block({:?})", self);
-        let unwind = self.unwind; // FIXME(#43234)
-        let succ = self.succ;
-        let blk = self.drop_block(succ, unwind);
+        let blk = self.drop_block(self.succ, self.unwind);
         self.elaborate_drop(blk);
         blk
     }
 
+    /// Creates a block that frees the backing memory of a `Box` if its drop is required (either
+    /// statically or by checking its drop flag).
+    ///
+    /// The contained value will not be dropped.
     fn box_free_block(
         &mut self,
         adt: &'tcx ty::AdtDef,
@@ -868,6 +940,8 @@ where
         self.drop_flag_test_block(block, target, unwind)
     }
 
+    /// Creates a block that frees the backing memory of a `Box` (without dropping the contained
+    /// value).
     fn unelaborated_free_block(
         &mut self,
         adt: &'tcx ty::AdtDef,
@@ -914,6 +988,11 @@ where
         self.new_block(unwind, block)
     }
 
+    /// Returns the block to jump to in order to test the drop flag and execute the drop.
+    ///
+    /// Depending on the required `DropStyle`, this might be a generated block with an `if`
+    /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case
+    /// the drop can be statically determined.
     fn drop_flag_test_block(
         &mut self,
         on_set: BasicBlock,
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/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 4ac3f2625ab..54562cf38ad 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -793,7 +793,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 //     `unsafe fn(arg0,arg1,...) -> _`
                 let closure_sig = substs_a.as_closure().sig();
                 let unsafety = fn_ty.unsafety();
-                let pointer_ty = self.tcx.coerce_closure_fn_ty(closure_sig, unsafety);
+                let pointer_ty =
+                    self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety));
                 debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
                 self.unify_and(
                     pointer_ty,
@@ -909,23 +910,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty);
 
         // Special-case that coercion alone cannot handle:
-        // Two function item types of differing IDs or InternalSubsts.
-        if let (&ty::FnDef(..), &ty::FnDef(..)) = (&prev_ty.kind, &new_ty.kind) {
-            // Don't reify if the function types have a LUB, i.e., they
-            // are the same function and their parameters have a LUB.
-            let lub_ty = self
-                .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
-                .map(|ok| self.register_infer_ok_obligations(ok));
-
-            if lub_ty.is_ok() {
-                // We have a LUB of prev_ty and new_ty, just return it.
-                return lub_ty;
+        // Function items or non-capturing closures of differing IDs or InternalSubsts.
+        let (a_sig, b_sig) = {
+            let is_capturing_closure = |ty| {
+                if let &ty::Closure(_, substs) = ty {
+                    substs.as_closure().upvar_tys().next().is_some()
+                } else {
+                    false
+                }
+            };
+            if is_capturing_closure(&prev_ty.kind) || is_capturing_closure(&new_ty.kind) {
+                (None, None)
+            } else {
+                match (&prev_ty.kind, &new_ty.kind) {
+                    (&ty::FnDef(..), &ty::FnDef(..)) => {
+                        // Don't reify if the function types have a LUB, i.e., they
+                        // are the same function and their parameters have a LUB.
+                        match self
+                            .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty))
+                        {
+                            // We have a LUB of prev_ty and new_ty, just return it.
+                            Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)),
+                            Err(_) => {
+                                (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx)))
+                            }
+                        }
+                    }
+                    (&ty::Closure(_, substs), &ty::FnDef(..)) => {
+                        let b_sig = new_ty.fn_sig(self.tcx);
+                        let a_sig = self
+                            .tcx
+                            .signature_unclosure(substs.as_closure().sig(), b_sig.unsafety());
+                        (Some(a_sig), Some(b_sig))
+                    }
+                    (&ty::FnDef(..), &ty::Closure(_, substs)) => {
+                        let a_sig = prev_ty.fn_sig(self.tcx);
+                        let b_sig = self
+                            .tcx
+                            .signature_unclosure(substs.as_closure().sig(), a_sig.unsafety());
+                        (Some(a_sig), Some(b_sig))
+                    }
+                    (&ty::Closure(_, substs_a), &ty::Closure(_, substs_b)) => (
+                        Some(self.tcx.signature_unclosure(
+                            substs_a.as_closure().sig(),
+                            hir::Unsafety::Normal,
+                        )),
+                        Some(self.tcx.signature_unclosure(
+                            substs_b.as_closure().sig(),
+                            hir::Unsafety::Normal,
+                        )),
+                    ),
+                    _ => (None, None),
+                }
             }
-
+        };
+        if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) {
             // The signature must match.
-            let a_sig = prev_ty.fn_sig(self.tcx);
             let a_sig = self.normalize_associated_types_in(new.span, &a_sig);
-            let b_sig = new_ty.fn_sig(self.tcx);
             let b_sig = self.normalize_associated_types_in(new.span, &b_sig);
             let sig = self
                 .at(cause, self.param_env)
@@ -935,17 +976,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // Reify both sides and return the reified fn pointer type.
             let fn_ptr = self.tcx.mk_fn_ptr(sig);
-            for expr in exprs.iter().map(|e| e.as_coercion_site()).chain(Some(new)) {
-                // The only adjustment that can produce an fn item is
-                // `NeverToAny`, so this should always be valid.
+            let prev_adjustment = match prev_ty.kind {
+                ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
+                ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
+                _ => unreachable!(),
+            };
+            let next_adjustment = match new_ty.kind {
+                ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())),
+                ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
+                _ => unreachable!(),
+            };
+            for expr in exprs.iter().map(|e| e.as_coercion_site()) {
                 self.apply_adjustments(
                     expr,
-                    vec![Adjustment {
-                        kind: Adjust::Pointer(PointerCast::ReifyFnPointer),
-                        target: fn_ptr,
-                    }],
+                    vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }],
                 );
             }
+            self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]);
             return Ok(fn_ptr);
         }
 
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/closures/closure_cap_coerce_many_fail.rs b/src/test/ui/closures/closure_cap_coerce_many_fail.rs
new file mode 100644
index 00000000000..9133a292103
--- /dev/null
+++ b/src/test/ui/closures/closure_cap_coerce_many_fail.rs
@@ -0,0 +1,39 @@
+fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+fn main() {
+    // We shouldn't coerce capturing closure to a function
+    let cap = 0;
+    let _ = match "+" {
+        "+" => add,
+        "-" => |a, b| (a - b + cap) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+
+
+    // We shouldn't coerce capturing closure to a non-capturing closure
+    let _ = match "+" {
+        "+" => |a, b| (a + b) as i32,
+        "-" => |a, b| (a - b + cap) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+
+
+    // We shouldn't coerce non-capturing closure to a capturing closure
+    let _ = match "+" {
+        "+" => |a, b| (a + b + cap) as i32,
+        "-" => |a, b| (a - b) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+
+    // We shouldn't coerce capturing closure to a capturing closure
+    let _ = match "+" {
+        "+" => |a, b| (a + b + cap) as i32,
+        "-" => |a, b| (a - b + cap) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+}
diff --git a/src/test/ui/closures/closure_cap_coerce_many_fail.stderr b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr
new file mode 100644
index 00000000000..63eb0bd8fab
--- /dev/null
+++ b/src/test/ui/closures/closure_cap_coerce_many_fail.stderr
@@ -0,0 +1,73 @@
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:9:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => add,
+   | |                --- this is found to be of type `fn(i32, i32) -> i32 {add}`
+LL | |         "-" => |a, b| (a - b + cap) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected type `fn(i32, i32) -> i32 {add}`
+           found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:43 cap:_]`
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:18:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => |a, b| (a + b) as i32,
+   | |                --------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]`
+LL | |         "-" => |a, b| (a - b + cap) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:37]`
+           found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:43 cap:_]`
+   = note: no two closures, even if identical, have the same type
+   = help: consider boxing your closure and/or using it as a trait object
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:27:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => |a, b| (a + b + cap) as i32,
+   | |                --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]`
+LL | |         "-" => |a, b| (a - b) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:43 cap:_]`
+           found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:37]`
+   = note: no two closures, even if identical, have the same type
+   = help: consider boxing your closure and/or using it as a trait object
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:35:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => |a, b| (a + b + cap) as i32,
+   | |                --------------------------- this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]`
+LL | |         "-" => |a, b| (a - b + cap) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:43 cap:_]`
+           found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:43 cap:_]`
+   = note: no two closures, even if identical, have the same type
+   = help: consider boxing your closure and/or using it as a trait object
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs b/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs
new file mode 100644
index 00000000000..ce461810ec9
--- /dev/null
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_check_pass.rs
@@ -0,0 +1,166 @@
+// check-pass
+// Ensure non-capturing Closure passes CoerceMany.
+fn foo(x: usize) -> usize {
+    0
+}
+
+fn bar(x: usize) -> usize {
+    1
+}
+
+fn main() {
+    // One FnDef and one non-capturing Closure
+    let _ = match 0 {
+        0 => foo,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        2 => |a| 2,
+        0 => foo,
+        _ => unimplemented!(),
+    };
+
+    let _ = [foo, |a| 2];
+    let _ = [|a| 2, foo];
+
+
+
+    // Two FnDefs and one non-capturing Closure
+    let _ = match 0 {
+        0 => foo,
+        1 => bar,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        0 => foo,
+        2 => |a| 2,
+        1 => bar,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        2 => |a| 2,
+        0 => foo,
+        1 => bar,
+        _ => unimplemented!(),
+    };
+
+    let _ = [foo, bar, |a| 2];
+    let _ = [foo, |a| 2, bar];
+    let _ = [|a| 2, foo, bar];
+
+
+
+    // One FnDef and two non-capturing Closures
+    let _ = match 0 {
+        0 => foo,
+        1 => |a| 1,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        1 => |a| 1,
+        0 => foo,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        1 => |a| 1,
+        2 => |a| 2,
+        0 => foo,
+        _ => unimplemented!(),
+    };
+
+    let _ = [foo, |a| 1, |a| 2];
+    let _ = [|a| 1, foo, |a| 2];
+    let _ = [|a| 1, |a| 2, foo];
+
+
+
+    // Three non-capturing Closures
+    let _ = match 0 {
+        0 => |a: usize| 0,
+        1 => |a| 1,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = [|a: usize| 0, |a| 1, |a| 2];
+
+
+
+    // Three non-capturing Closures variable
+    let clo0 = |a: usize| 0;
+    let clo1 = |a| 1;
+    let clo2 = |a| 2;
+    let _ = match 0 {
+        0 => clo0,
+        1 => clo1,
+        2 => clo2,
+        _ => unimplemented!(),
+    };
+
+    let clo0 = |a: usize| 0;
+    let clo1 = |a| 1;
+    let clo2 = |a| 2;
+    let _ = [clo0, clo1, clo2];
+
+
+
+    // --- Function pointer related part
+
+    // Closure is not in a variable
+    type FnPointer = fn(usize) -> usize;
+
+    let _ = match 0 {
+        0 => foo as FnPointer,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+    let _ = match 0 {
+        2 => |a| 2,
+        0 => foo as FnPointer,
+        _ => unimplemented!(),
+    };
+    let _ = [foo as FnPointer, |a| 2];
+    let _ = [|a| 2, foo as FnPointer];
+    let _ = [foo, bar, |x| x];
+    let _ = [foo as FnPointer, bar, |x| x];
+    let _ = [foo, bar as FnPointer, |x| x];
+    let _ = [foo, bar, (|x| x) as FnPointer];
+    let _ = [foo as FnPointer, bar as FnPointer, |x| x];
+
+    // Closure is in a variable
+    let x = |a| 2;
+    let _ = match 0 {
+        0 => foo as FnPointer,
+        2 => x,
+        _ => unimplemented!(),
+    };
+    let x = |a| 2;
+    let _ = match 0 {
+        2 => x,
+        0 => foo as FnPointer,
+        _ => unimplemented!(),
+    };
+    let x = |a| 2;
+    let _ = [foo as FnPointer, x];
+    let _ = [x, foo as FnPointer];
+
+    let x = |a| 2;
+    let _ = [foo, bar, x];
+    let x: FnPointer = |a| 2;
+    let _ = [foo, bar, x];
+    let x = |a| 2;
+    let _ = [foo, bar as FnPointer, x];
+    let x = |a| 2;
+    let _ = [foo as FnPointer, bar, x];
+    let x = |a| 2;
+    let _ = [foo as FnPointer, bar as FnPointer, x];
+}
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs b/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs
new file mode 100644
index 00000000000..3c5fe8a5502
--- /dev/null
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_run_pass.rs
@@ -0,0 +1,59 @@
+// run-pass
+// Ensure non-capturing Closure passing CoerceMany work correctly.
+fn foo(_: usize) -> usize {
+    0
+}
+
+fn bar(_: usize) -> usize {
+    1
+}
+
+fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+
+fn main() {
+    // Coerce result check
+
+    type FnPointer = fn(usize) -> usize;
+
+    let c = |x| x;
+    let c_pointer: FnPointer = c;
+    assert_eq!(c_pointer(42), 42);
+
+    let f = match 0 {
+        0 => foo,
+        1 => |_| 1,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 0);
+
+    let f = match 2 {
+        2 => |_| 2,
+        0 => foo,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 2);
+
+    let f = match 1 {
+        0 => foo,
+        1 => bar,
+        2 => |_| 2,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 1);
+
+    let clo0 = |_: usize| 0;
+    let clo1 = |_| 1;
+    let clo2 = |_| 2;
+    let f = match 0 {
+        0 => clo0,
+        1 => clo1,
+        2 => clo2,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 0);
+
+    let funcs = [add, |a, b| (a - b) as i32];
+    assert_eq!([funcs[0](5, 5), funcs[1](5, 5)], [10, 0]);
+}
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs
new file mode 100644
index 00000000000..76a0f291410
--- /dev/null
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs
@@ -0,0 +1,22 @@
+// Ensure we get unsafe function after coercion
+unsafe fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+fn main() {
+    // We can coerce non-capturing closure to unsafe function
+    let foo = match "+" {
+        "+" => add,
+        "-" => |a, b| (a - b) as i32,
+        _ => unimplemented!(),
+    };
+    let result: i32 = foo(5, 5); //~ ERROR call to unsafe function
+
+
+    // We can coerce unsafe function to non-capturing closure
+    let foo = match "+" {
+        "-" => |a, b| (a - b) as i32,
+        "+" => add,
+        _ => unimplemented!(),
+    };
+    let result: i32 = foo(5, 5); //~ ERROR call to unsafe function
+}
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr
new file mode 100644
index 00000000000..190b4792ebc
--- /dev/null
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_0.stderr
@@ -0,0 +1,19 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:12:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:21:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs
new file mode 100644
index 00000000000..a6d6125a1b9
--- /dev/null
+++ b/src/test/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs
@@ -0,0 +1,23 @@
+// run-pass
+// Ensure we get correct unsafe function after coercion
+unsafe fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+fn main() {
+    // We can coerce non-capturing closure to unsafe function
+    let foo = match "+" {
+        "+" => add,
+        "-" => |a, b| (a - b) as i32,
+        _ => unimplemented!(),
+    };
+    assert_eq!(unsafe { foo(5, 5) }, 10);
+
+
+    // We can coerce unsafe function to non-capturing closure
+    let foo = match "-" {
+        "-" => |a, b| (a - b) as i32,
+        "+" => add,
+        _ => unimplemented!(),
+    };
+    assert_eq!(unsafe { foo(5, 5) }, 0);
+}
diff --git a/src/test/ui/closures/issue-46742.rs b/src/test/ui/closures/issue-46742.rs
new file mode 100644
index 00000000000..cd8dc486906
--- /dev/null
+++ b/src/test/ui/closures/issue-46742.rs
@@ -0,0 +1,9 @@
+// check-pass
+fn main() {
+    let _: i32 = (match "" {
+        "+" => ::std::ops::Add::add,
+        "-" => ::std::ops::Sub::sub,
+        "<" => |a,b| (a < b) as i32,
+        _ => unimplemented!(),
+    })(5, 5);
+}
diff --git a/src/test/ui/closures/issue-48109.rs b/src/test/ui/closures/issue-48109.rs
new file mode 100644
index 00000000000..ce1f2a03647
--- /dev/null
+++ b/src/test/ui/closures/issue-48109.rs
@@ -0,0 +1,14 @@
+// check-pass
+fn useful(i: usize) -> usize {
+    i
+}
+
+fn useful2(i: usize) -> usize {
+    i
+}
+
+fn main() {
+    for f in &[useful, useful2, |x| x] {
+        println!("{}", f(6));
+    }
+}
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/issues/issue-24036.rs b/src/test/ui/issues/issue-24036.rs
index bd82f95c9ef..7df036c8e3a 100644
--- a/src/test/ui/issues/issue-24036.rs
+++ b/src/test/ui/issues/issue-24036.rs
@@ -10,7 +10,7 @@ fn closure_from_match() {
         2 => |c| c - 1,
         _ => |c| c - 1
     };
-    //~^^^ ERROR `match` arms have incompatible types
+    //~^^^^ ERROR type annotations needed
 }
 
 fn main() { }
diff --git a/src/test/ui/issues/issue-24036.stderr b/src/test/ui/issues/issue-24036.stderr
index 036c05fc848..e6b8367f74f 100644
--- a/src/test/ui/issues/issue-24036.stderr
+++ b/src/test/ui/issues/issue-24036.stderr
@@ -11,24 +11,13 @@ LL |     x = |c| c + 1;
    = note: no two closures, even if identical, have the same type
    = help: consider boxing your closure and/or using it as a trait object
 
-error[E0308]: `match` arms have incompatible types
-  --> $DIR/issue-24036.rs:10:14
+error[E0282]: type annotations needed
+  --> $DIR/issue-24036.rs:9:15
    |
-LL |       let x = match 1usize {
-   |  _____________-
-LL | |         1 => |c| c + 1,
-   | |              --------- this is found to be of type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
-LL | |         2 => |c| c - 1,
-   | |              ^^^^^^^^^ expected closure, found a different closure
-LL | |         _ => |c| c - 1
-LL | |     };
-   | |_____- `match` arms have incompatible types
-   |
-   = note: expected type `[closure@$DIR/issue-24036.rs:9:14: 9:23]`
-           found closure `[closure@$DIR/issue-24036.rs:10:14: 10:23]`
-   = note: no two closures, even if identical, have the same type
-   = help: consider boxing your closure and/or using it as a trait object
+LL |         1 => |c| c + 1,
+   |               ^ consider giving this closure parameter a type
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0282, E0308.
+For more information about an error, try `rustc --explain E0282`.
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;
diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs
index d0ae8300bd6..3379d82eda8 100644
--- a/src/tools/remote-test-client/src/main.rs
+++ b/src/tools/remote-test-client/src/main.rs
@@ -18,6 +18,7 @@ use std::thread;
 use std::time::Duration;
 
 const REMOTE_ADDR_ENV: &str = "TEST_DEVICE_ADDR";
+const DEFAULT_ADDR: &str = "127.0.0.1:12345";
 
 macro_rules! t {
     ($e:expr) => {
@@ -30,8 +31,12 @@ macro_rules! t {
 
 fn main() {
     let mut args = env::args().skip(1);
+    let next = args.next();
+    if next.is_none() {
+        return help();
+    }
 
-    match &args.next().unwrap()[..] {
+    match &next.unwrap()[..] {
         "spawn-emulator" => spawn_emulator(
             &args.next().unwrap(),
             Path::new(&args.next().unwrap()),
@@ -40,12 +45,16 @@ fn main() {
         ),
         "push" => push(Path::new(&args.next().unwrap())),
         "run" => run(args.next().unwrap(), args.collect()),
-        cmd => panic!("unknown command: {}", cmd),
+        "help" | "-h" | "--help" => help(),
+        cmd => {
+            println!("unknown command: {}", cmd);
+            help();
+        }
     }
 }
 
 fn spawn_emulator(target: &str, server: &Path, tmpdir: &Path, rootfs: Option<PathBuf>) {
-    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
+    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
 
     if env::var(REMOTE_ADDR_ENV).is_ok() {
         println!("Connecting to remote device {} ...", device_address);
@@ -172,7 +181,7 @@ fn start_qemu_emulator(target: &str, rootfs: &Path, server: &Path, tmpdir: &Path
 }
 
 fn push(path: &Path) {
-    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
+    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
     let client = t!(TcpStream::connect(device_address));
     let mut client = BufWriter::new(client);
     t!(client.write_all(b"push"));
@@ -189,7 +198,7 @@ fn push(path: &Path) {
 }
 
 fn run(files: String, args: Vec<String>) {
-    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or("127.0.0.1:12345".to_string());
+    let device_address = env::var(REMOTE_ADDR_ENV).unwrap_or(DEFAULT_ADDR.to_string());
     let client = t!(TcpStream::connect(device_address));
     let mut client = BufWriter::new(client);
     t!(client.write_all(b"run "));
@@ -284,3 +293,40 @@ fn send(path: &Path, dst: &mut dyn Write) {
     t!(dst.write_all(&[(amt >> 24) as u8, (amt >> 16) as u8, (amt >> 8) as u8, (amt >> 0) as u8,]));
     t!(io::copy(&mut file, dst));
 }
+
+fn help() {
+    println!(
+        "
+Usage: {0} <command> [<args>]
+
+Sub-commands:
+    spawn-emulator <target> <server> <tmpdir> [rootfs]   See below
+    push <path>                                          Copy <path> to emulator
+    run <files> [args...]                                Run program on emulator
+    help                                                 Display help message
+
+Spawning an emulator:
+
+For Android <target>s, adb will push the <server>, set up TCP forwarding and run
+the <server>. Otherwise qemu emulates the target using a rootfs image created in
+<tmpdir> and generated from <rootfs> plus the <server> executable.
+If {1} is set in the environment, this step is skipped.
+
+Pushing a path to a running emulator:
+
+A running emulator or adb device is connected to at the IP address and port in
+the {1} environment variable or {2} if this isn't
+specified. The file at <path> is sent to this target.
+
+Executing commands on a running emulator:
+
+First the target emulator/adb session is connected to as for pushing files. Next
+the colon separated list of <files> is pushed to the target. Finally, the first
+file in <files> is executed in the emulator, preserving the current environment.
+That command's status code is returned.
+",
+        env::args().next().unwrap(),
+        REMOTE_ADDR_ENV,
+        DEFAULT_ADDR
+    );
+}