about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-03-25 01:34:30 +0100
committerGitHub <noreply@github.com>2022-03-25 01:34:30 +0100
commit1fcb8fc3e0bc82fc63bbb8baa6c6325d361115fc (patch)
tree5453b5ffbb5164b6b6afde0b0b1e45ecb978cb54
parentc66e0c87267859a46f1a8851f30892985760cfd6 (diff)
parent11a70dbc8ac49416598b0ff4525096845a0dc498 (diff)
downloadrust-1fcb8fc3e0bc82fc63bbb8baa6c6325d361115fc.tar.gz
rust-1fcb8fc3e0bc82fc63bbb8baa6c6325d361115fc.zip
Rollup merge of #95179 - b-naber:eval-in-try-unify, r=lcnr
Try to evaluate in try unify and postpone resolution of constants that contain inference variables

We want code like that in [`ui/const-generics/generic_const_exprs/eval-try-unify.rs`](https://github.com/rust-lang/rust/compare/master...b-naber:eval-in-try-unify?expand=1#diff-8027038201cf07a6c96abf3cbf0b0f4fdd8a64ce6292435f01c8ed995b87fe9b) to compile. To do that we need to try to evaluate constants in `try_unify_abstract_consts`, this requires us to be more careful about what constants we try to resolve, specifically we cannot try to resolve constants that still contain inference variables.

r? `@lcnr`
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs28
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs11
-rw-r--r--compiler/rustc_middle/src/query/mod.rs8
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs187
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs6
-rw-r--r--compiler/rustc_typeck/src/check/dropck.rs2
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs26
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr11
-rw-r--r--src/test/ui/const-generics/issues/issue-83765.rs129
-rw-r--r--src/test/ui/const-generics/issues/issue-83765.stderr137
13 files changed, 277 insertions, 281 deletions
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 5697e73f73a..2886d921c70 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -20,8 +20,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
-use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::mir::interpret::EvalToConstValueResult;
+use rustc_middle::mir::interpret::{ErrorHandled, EvalToConstValueResult};
 use rustc_middle::traits::select;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
@@ -71,7 +70,6 @@ mod sub;
 pub mod type_variable;
 mod undo_log;
 
-use crate::infer::canonical::OriginalQueryValues;
 pub use rustc_middle::infer::unify_key;
 
 #[must_use]
@@ -687,15 +685,28 @@ pub struct CombinedSnapshot<'a, 'tcx> {
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// calls `tcx.try_unify_abstract_consts` after
     /// canonicalizing the consts.
+    #[instrument(skip(self), level = "debug")]
     pub fn try_unify_abstract_consts(
         &self,
         a: ty::Unevaluated<'tcx, ()>,
         b: ty::Unevaluated<'tcx, ()>,
+        param_env: ty::ParamEnv<'tcx>,
     ) -> bool {
-        let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default());
-        debug!("canonical consts: {:?}", &canonical.value);
+        // Reject any attempt to unify two unevaluated constants that contain inference
+        // variables, since inference variables in queries lead to ICEs.
+        if a.substs.has_infer_types_or_consts()
+            || b.substs.has_infer_types_or_consts()
+            || param_env.has_infer_types_or_consts()
+        {
+            debug!("a or b or param_env contain infer vars in its substs -> cannot unify");
+            return false;
+        }
+
+        let param_env_and = param_env.and((a, b));
+        let erased = self.tcx.erase_regions(param_env_and);
+        debug!("after erase_regions: {:?}", erased);
 
-        self.tcx.try_unify_abstract_consts(canonical.value)
+        self.tcx.try_unify_abstract_consts(erased)
     }
 
     pub fn is_in_snapshot(&self) -> bool {
@@ -1598,6 +1609,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     ///
     /// This handles inferences variables within both `param_env` and `substs` by
     /// performing the operation on their respective canonical forms.
+    #[instrument(skip(self), level = "debug")]
     pub fn const_eval_resolve(
         &self,
         param_env: ty::ParamEnv<'tcx>,
@@ -1605,15 +1617,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         span: Option<Span>,
     ) -> EvalToConstValueResult<'tcx> {
         let substs = self.resolve_vars_if_possible(unevaluated.substs);
+        debug!(?substs);
 
         // Postpone the evaluation of constants whose substs depend on inference
         // variables
         if substs.has_infer_types_or_consts() {
+            debug!("substs have infer types or consts: {:?}", substs);
             return Err(ErrorHandled::TooGeneric);
         }
 
         let param_env_erased = self.tcx.erase_regions(param_env);
         let substs_erased = self.tcx.erase_regions(substs);
+        debug!(?param_env_erased);
+        debug!(?substs_erased);
 
         let unevaluated = ty::Unevaluated {
             def: unevaluated.def,
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 8b0f92d23d7..7e5989b4112 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -1,6 +1,7 @@
 use super::{ErrorHandled, EvalToConstValueResult, GlobalId};
 
 use crate::mir;
+use crate::ty::fold::TypeFoldable;
 use crate::ty::subst::InternalSubsts;
 use crate::ty::{self, TyCtxt};
 use rustc_hir::def_id::DefId;
@@ -38,6 +39,16 @@ impl<'tcx> TyCtxt<'tcx> {
         ct: ty::Unevaluated<'tcx>,
         span: Option<Span>,
     ) -> EvalToConstValueResult<'tcx> {
+        // Cannot resolve `Unevaluated` constants that contain inference
+        // variables. We reject those here since `resolve_opt_const_arg`
+        // would fail otherwise.
+        //
+        // When trying to evaluate constants containing inference variables,
+        // use `Infcx::const_eval_resolve` instead.
+        if ct.substs.has_infer_types_or_consts() {
+            bug!("did not expect inference variables here");
+        }
+
         match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) {
             Ok(Some(instance)) => {
                 let cid = GlobalId { instance, promoted: ct.promoted };
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 87af90d059b..cd6ff8254ad 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -331,12 +331,12 @@ rustc_queries! {
         }
     }
 
-    query try_unify_abstract_consts(key: (
-        ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>
-    )) -> bool {
+    query try_unify_abstract_consts(key:
+        ty::ParamEnvAnd<'tcx, (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>
+    )>) -> bool {
         desc {
             |tcx| "trying to unify the generic constants {} and {}",
-            tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did)
+            tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did)
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 81ee7942c4d..5d6cbcf6907 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -585,7 +585,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
         (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu))
             if tcx.features().generic_const_exprs =>
         {
-            tcx.try_unify_abstract_consts((au.shrink(), bu.shrink()))
+            tcx.try_unify_abstract_consts(relation.param_env().and((au.shrink(), bu.shrink())))
         }
 
         // While this is slightly incorrect, it shouldn't matter for `min_const_generics`
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 490c04e7be2..959b644becd 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -188,6 +188,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>(
     }
 }
 
+#[instrument(skip(tcx), level = "debug")]
 fn satisfied_from_param_env<'tcx>(
     tcx: TyCtxt<'tcx>,
     ct: AbstractConst<'tcx>,
@@ -197,14 +198,17 @@ fn satisfied_from_param_env<'tcx>(
         match pred.kind().skip_binder() {
             ty::PredicateKind::ConstEvaluatable(uv) => {
                 if let Some(b_ct) = AbstractConst::new(tcx, uv)? {
+                    let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
+
                     // Try to unify with each subtree in the AbstractConst to allow for
                     // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable`
                     // predicate for `(N + 1) * 2`
-                    let result =
-                        walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) {
+                    let result = walk_abstract_const(tcx, b_ct, |b_ct| {
+                        match const_unify_ctxt.try_unify(ct, b_ct) {
                             true => ControlFlow::BREAK,
                             false => ControlFlow::CONTINUE,
-                        });
+                        }
+                    });
 
                     if let ControlFlow::Break(()) = result {
                         debug!("is_const_evaluatable: abstract_const ~~> ok");
@@ -637,11 +641,13 @@ pub(super) fn thir_abstract_const<'tcx>(
 pub(super) fn try_unify_abstract_consts<'tcx>(
     tcx: TyCtxt<'tcx>,
     (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>),
+    param_env: ty::ParamEnv<'tcx>,
 ) -> bool {
     (|| {
         if let Some(a) = AbstractConst::new(tcx, a)? {
             if let Some(b) = AbstractConst::new(tcx, b)? {
-                return Ok(try_unify(tcx, a, b));
+                let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env };
+                return Ok(const_unify_ctxt.try_unify(a, b));
             }
         }
 
@@ -689,88 +695,115 @@ where
     recurse(tcx, ct, &mut f)
 }
 
-/// Tries to unify two abstract constants using structural equality.
-pub(super) fn try_unify<'tcx>(
+struct ConstUnifyCtxt<'tcx> {
     tcx: TyCtxt<'tcx>,
-    mut a: AbstractConst<'tcx>,
-    mut b: AbstractConst<'tcx>,
-) -> bool {
-    // We substitute generics repeatedly to allow AbstractConsts to unify where a
+    param_env: ty::ParamEnv<'tcx>,
+}
+
+impl<'tcx> ConstUnifyCtxt<'tcx> {
+    // Substitutes generics repeatedly to allow AbstractConsts to unify where a
     // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g.
     // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])]
-    while let Node::Leaf(a_ct) = a.root(tcx) {
-        match AbstractConst::from_const(tcx, a_ct) {
-            Ok(Some(a_act)) => a = a_act,
-            Ok(None) => break,
-            Err(_) => return true,
-        }
-    }
-    while let Node::Leaf(b_ct) = b.root(tcx) {
-        match AbstractConst::from_const(tcx, b_ct) {
-            Ok(Some(b_act)) => b = b_act,
-            Ok(None) => break,
-            Err(_) => return true,
+    #[inline]
+    #[instrument(skip(self), level = "debug")]
+    fn try_replace_substs_in_root(
+        &self,
+        mut abstr_const: AbstractConst<'tcx>,
+    ) -> Option<AbstractConst<'tcx>> {
+        while let Node::Leaf(ct) = abstr_const.root(self.tcx) {
+            match AbstractConst::from_const(self.tcx, ct) {
+                Ok(Some(act)) => abstr_const = act,
+                Ok(None) => break,
+                Err(_) => return None,
+            }
         }
-    }
 
-    match (a.root(tcx), b.root(tcx)) {
-        (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
-            if a_ct.ty() != b_ct.ty() {
-                return false;
-            }
+        Some(abstr_const)
+    }
 
-            match (a_ct.val(), b_ct.val()) {
-                // We can just unify errors with everything to reduce the amount of
-                // emitted errors here.
-                (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
-                (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
-                    a_param == b_param
+    /// Tries to unify two abstract constants using structural equality.
+    #[instrument(skip(self), level = "debug")]
+    fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool {
+        let a = if let Some(a) = self.try_replace_substs_in_root(a) {
+            a
+        } else {
+            return true;
+        };
+
+        let b = if let Some(b) = self.try_replace_substs_in_root(b) {
+            b
+        } else {
+            return true;
+        };
+
+        let a_root = a.root(self.tcx);
+        let b_root = b.root(self.tcx);
+        debug!(?a_root, ?b_root);
+
+        match (a_root, b_root) {
+            (Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
+                let a_ct = a_ct.eval(self.tcx, self.param_env);
+                debug!("a_ct evaluated: {:?}", a_ct);
+                let b_ct = b_ct.eval(self.tcx, self.param_env);
+                debug!("b_ct evaluated: {:?}", b_ct);
+
+                if a_ct.ty() != b_ct.ty() {
+                    return false;
                 }
-                (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
-                // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
-                // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
-                // means that we only allow inference variables if they are equal.
-                (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
-                // We expand generic anonymous constants at the start of this function, so this
-                // branch should only be taking when dealing with associated constants, at
-                // which point directly comparing them seems like the desired behavior.
-                //
-                // FIXME(generic_const_exprs): This isn't actually the case.
-                // We also take this branch for concrete anonymous constants and
-                // expand generic anonymous constants with concrete substs.
-                (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
-                    a_uv == b_uv
+
+                match (a_ct.val(), b_ct.val()) {
+                    // We can just unify errors with everything to reduce the amount of
+                    // emitted errors here.
+                    (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
+                    (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => {
+                        a_param == b_param
+                    }
+                    (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val,
+                    // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]`
+                    // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This
+                    // means that we only allow inference variables if they are equal.
+                    (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val,
+                    // We expand generic anonymous constants at the start of this function, so this
+                    // branch should only be taking when dealing with associated constants, at
+                    // which point directly comparing them seems like the desired behavior.
+                    //
+                    // FIXME(generic_const_exprs): This isn't actually the case.
+                    // We also take this branch for concrete anonymous constants and
+                    // expand generic anonymous constants with concrete substs.
+                    (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => {
+                        a_uv == b_uv
+                    }
+                    // FIXME(generic_const_exprs): We may want to either actually try
+                    // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
+                    // this, for now we just return false here.
+                    _ => false,
                 }
-                // FIXME(generic_const_exprs): We may want to either actually try
-                // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like
-                // this, for now we just return false here.
-                _ => false,
             }
+            (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
+                self.try_unify(a.subtree(al), b.subtree(bl))
+                    && self.try_unify(a.subtree(ar), b.subtree(br))
+            }
+            (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
+                self.try_unify(a.subtree(av), b.subtree(bv))
+            }
+            (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
+                if a_args.len() == b_args.len() =>
+            {
+                self.try_unify(a.subtree(a_f), b.subtree(b_f))
+                    && iter::zip(a_args, b_args)
+                        .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn)))
+            }
+            (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
+                if (a_ty == b_ty) && (a_kind == b_kind) =>
+            {
+                self.try_unify(a.subtree(a_operand), b.subtree(b_operand))
+            }
+            // use this over `_ => false` to make adding variants to `Node` less error prone
+            (Node::Cast(..), _)
+            | (Node::FunctionCall(..), _)
+            | (Node::UnaryOp(..), _)
+            | (Node::Binop(..), _)
+            | (Node::Leaf(..), _) => false,
         }
-        (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => {
-            try_unify(tcx, a.subtree(al), b.subtree(bl))
-                && try_unify(tcx, a.subtree(ar), b.subtree(br))
-        }
-        (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => {
-            try_unify(tcx, a.subtree(av), b.subtree(bv))
-        }
-        (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args))
-            if a_args.len() == b_args.len() =>
-        {
-            try_unify(tcx, a.subtree(a_f), b.subtree(b_f))
-                && iter::zip(a_args, b_args)
-                    .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn)))
-        }
-        (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty))
-            if (a_ty == b_ty) && (a_kind == b_kind) =>
-        {
-            try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand))
-        }
-        // use this over `_ => false` to make adding variants to `Node` less error prone
-        (Node::Cast(..), _)
-        | (Node::FunctionCall(..), _)
-        | (Node::UnaryOp(..), _)
-        | (Node::Binop(..), _)
-        | (Node::Leaf(..), _) => false,
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 9ac8dc59a1d..1b862834467 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -580,7 +580,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> {
                         if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
                             (c1.val(), c2.val())
                         {
-                            if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
+                            if infcx.try_unify_abstract_consts(
+                                a.shrink(),
+                                b.shrink(),
+                                obligation.param_env,
+                            ) {
                                 return ProcessResult::Changed(vec![]);
                             }
                         }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 3a6ca9b7624..88750f272c8 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -862,7 +862,10 @@ pub fn provide(providers: &mut ty::query::Providers) {
                 ty::WithOptConstParam { did, const_param_did: Some(param_did) },
             )
         },
-        try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts,
+        try_unify_abstract_consts: |tcx, param_env_and| {
+            let (param_env, (a, b)) = param_env_and.into_parts();
+            const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env)
+        },
         ..*providers
     };
 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index b7c2f0f4832..6d232d86d8a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -639,7 +639,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
                             (c1.val(), c2.val())
                         {
-                            if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
+                            if self.infcx.try_unify_abstract_consts(
+                                a.shrink(),
+                                b.shrink(),
+                                obligation.param_env,
+                            ) {
                                 return Ok(EvaluatedToOk);
                             }
                         }
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs
index d38777bea59..4ab94f39357 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_typeck/src/check/dropck.rs
@@ -243,7 +243,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
                 (
                     ty::PredicateKind::ConstEvaluatable(a),
                     ty::PredicateKind::ConstEvaluatable(b),
-                ) => tcx.try_unify_abstract_consts((a, b)),
+                ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))),
                 (
                     ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
                     ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),
diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs
new file mode 100644
index 00000000000..c59d62e576d
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs
@@ -0,0 +1,26 @@
+// build-pass
+
+#![feature(generic_const_exprs)]
+//~^ WARNING the feature `generic_const_exprs` is incomplete
+
+trait Generic {
+    const ASSOC: usize;
+}
+
+impl Generic for u8 {
+    const ASSOC: usize = 17;
+}
+impl Generic for u16 {
+    const ASSOC: usize = 13;
+}
+
+
+fn uses_assoc_type<T: Generic, const N: usize>() -> [u8; N + T::ASSOC] {
+    [0; N + T::ASSOC]
+}
+
+fn only_generic_n<const N: usize>() -> [u8; N + 13] {
+    uses_assoc_type::<u16, N>()
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr
new file mode 100644
index 00000000000..b5719b3fe1d
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr
@@ -0,0 +1,11 @@
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/eval-try-unify.rs:3:12
+   |
+LL | #![feature(generic_const_exprs)]
+   |            ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs
index 68536348d38..71c164ab0a5 100644
--- a/src/test/ui/const-generics/issues/issue-83765.rs
+++ b/src/test/ui/const-generics/issues/issue-83765.rs
@@ -2,114 +2,115 @@
 #![allow(incomplete_features)]
 
 trait TensorDimension {
-    const DIM : usize;
-    const ISSCALAR : bool = Self::DIM == 0;
-    fn is_scalar(&self) -> bool {Self::ISSCALAR}
+    const DIM: usize;
+    //~^ ERROR cycle detected when resolving instance
+    // FIXME Given the current state of the compiler its expected that we cycle here,
+    // but the cycle is still wrong.
+    const ISSCALAR: bool = Self::DIM == 0;
+    fn is_scalar(&self) -> bool {
+        Self::ISSCALAR
+    }
 }
 
-trait TensorSize : TensorDimension {
-    fn size(&self) -> [usize;Self::DIM];
-    fn inbounds(&self,index : [usize;Self::DIM]) -> bool {
-        index.iter().zip(self.size().iter()).all(|(i,s)| i < s)
+trait TensorSize: TensorDimension {
+    fn size(&self) -> [usize; Self::DIM];
+    fn inbounds(&self, index: [usize; Self::DIM]) -> bool {
+        index.iter().zip(self.size().iter()).all(|(i, s)| i < s)
     }
 }
 
-
 trait Broadcastable: TensorSize + Sized {
     type Element;
-    fn bget(&self, index:[usize;Self::DIM]) -> Option<Self::Element>;
-    fn lazy_updim<const NEWDIM : usize>(&self, size : [usize;NEWDIM] ) ->
-       LazyUpdim<Self,{Self::DIM},NEWDIM>
-    {
-        assert!(NEWDIM >= Self::DIM,
-            "Updimmed tensor cannot have fewer indices than the initial one.");
-        LazyUpdim {size,reference:&self}
+    fn bget(&self, index: [usize; Self::DIM]) -> Option<Self::Element>;
+    fn lazy_updim<const NEWDIM: usize>(
+        &self,
+        size: [usize; NEWDIM],
+    ) -> LazyUpdim<Self, { Self::DIM }, NEWDIM> {
+        assert!(
+            NEWDIM >= Self::DIM,
+            "Updimmed tensor cannot have fewer indices than the initial one."
+        );
+        LazyUpdim { size, reference: &self }
     }
-    fn bmap<T,F :Fn(Self::Element) -> T>(&self,foo : F) -> BMap<T,Self,F,{Self::DIM}>{
-        BMap {reference:self,closure : foo}
+    fn bmap<T, F: Fn(Self::Element) -> T>(&self, foo: F) -> BMap<T, Self, F, { Self::DIM }> {
+        BMap { reference: self, closure: foo }
     }
 }
 
-
-struct LazyUpdim<'a,T : Broadcastable,const OLDDIM : usize, const DIM : usize> {
-    size : [usize;DIM],
-    reference : &'a T
+struct LazyUpdim<'a, T: Broadcastable, const OLDDIM: usize, const DIM: usize> {
+    size: [usize; DIM],
+    reference: &'a T,
 }
 
-impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T,{T::DIM},DIM> {
-    const DIM : usize = DIM;
+impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T, { T::DIM }, DIM> {
+    const DIM: usize = DIM;
 }
 
-impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> {
-    fn size(&self) -> [usize;DIM] {self.size}
-    //~^ ERROR method not compatible with trait
+impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> {
+    fn size(&self) -> [usize; DIM] {
+        self.size
+    }
 }
 
-impl<'a,T : Broadcastable,const DIM : usize>  Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM>
-{
+impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> {
     type Element = T::Element;
-    fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-      //~^ ERROR method not compatible with trait
+    fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
         assert!(DIM >= T::DIM);
-        if !self.inbounds(index) {return None}
-        //~^ ERROR unconstrained generic constant
-        //~| ERROR mismatched types
+        if !self.inbounds(index) {
+            return None;
+        }
         let size = self.size();
-        //~^ ERROR unconstrained generic constant
-        let newindex : [usize;T::DIM] = Default::default();
-        //~^ ERROR the trait bound `[usize; _]: Default` is not satisfied
+        let newindex: [usize; T::DIM] = Default::default();
         self.reference.bget(newindex)
     }
 }
 
-struct BMap<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R  , const DIM: usize> {
-    reference : &'a T,
-    closure : F
+struct BMap<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> {
+    reference: &'a T,
+    closure: F,
 }
 
-impl<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R,
-     const DIM: usize> TensorDimension for BMap<'a,R,T,F,DIM> {
-
-    const DIM : usize = DIM;
+impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorDimension
+    for BMap<'a, R, T, F, DIM>
+{
+    const DIM: usize = DIM;
 }
-impl<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R  ,
-      const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> {
-
-    fn size(&self) -> [usize;DIM] {self.reference.size()}
-    //~^ ERROR unconstrained generic constant
-    //~| ERROR mismatched types
-    //~| ERROR method not compatible with trait
+impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSize
+    for BMap<'a, R, T, F, DIM>
+{
+    fn size(&self) -> [usize; DIM] {
+        self.reference.size()
+    }
 }
 
-impl<'a,R, T : Broadcastable, F :  Fn(T::Element) -> R  ,
-  const DIM: usize> Broadcastable for BMap<'a,R,T,F,DIM> {
-
+impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcastable
+    for BMap<'a, R, T, F, DIM>
+{
     type Element = R;
-    fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-      //~^ ERROR method not compatible with trait
+    fn bget(&self, index: [usize; DIM]) -> Option<Self::Element> {
         self.reference.bget(index).map(&self.closure)
-        //~^ ERROR unconstrained generic constant
-        //~| ERROR mismatched types
     }
 }
 
 impl<T> TensorDimension for Vec<T> {
-    const DIM : usize = 1;
+    const DIM: usize = 1;
 }
 impl<T> TensorSize for Vec<T> {
-    fn size(&self) -> [usize;1] {[self.len()]}
+    fn size(&self) -> [usize; 1] {
+        [self.len()]
+    }
 }
 impl<T: Clone> Broadcastable for Vec<T> {
     type Element = T;
-    fn bget(& self,index : [usize;1]) -> Option<T> {
+    fn bget(&self, index: [usize; 1]) -> Option<T> {
         self.get(index[0]).cloned()
     }
 }
 
 fn main() {
-    let v = vec![1,2,3];
-    let bv = v.lazy_updim([3,4]);
-    let bbv = bv.bmap(|x| x*x);
+    let v = vec![1, 2, 3];
+    let bv = v.lazy_updim([3, 4]);
+    let bbv = bv.bmap(|x| x * x);
 
-    println!("The size of v is {:?}",bbv.bget([0,2]).expect("Out of bounds."));
+    println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds."));
 }
diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr
index a49f850717f..8705a39fa4b 100644
--- a/src/test/ui/const-generics/issues/issue-83765.stderr
+++ b/src/test/ui/const-generics/issues/issue-83765.stderr
@@ -1,130 +1,17 @@
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:44:5
+error[E0391]: cycle detected when resolving instance `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM`
+  --> $DIR/issue-83765.rs:5:5
    |
-LL |     fn size(&self) -> [usize;DIM] {self.size}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
+LL |     const DIM: usize;
+   |     ^^^^^^^^^^^^^^^^^
    |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:51:5
-   |
-LL |     fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:78:5
-   |
-LL |     fn size(&self) -> [usize;DIM] {self.reference.size()}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error[E0308]: method not compatible with trait
-  --> $DIR/issue-83765.rs:88:5
-   |
-LL |     fn bget(&self,index:[usize;DIM]) -> Option<Self::Element> {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:54:18
-   |
-LL |         if !self.inbounds(index) {return None}
-   |                  ^^^^^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `TensorSize::inbounds`
-  --> $DIR/issue-83765.rs:12:38
-   |
-LL |     fn inbounds(&self,index : [usize;Self::DIM]) -> bool {
-   |                                      ^^^^^^^^^ required by this bound in `TensorSize::inbounds`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-83765.rs:54:27
-   |
-LL |         if !self.inbounds(index) {return None}
-   |                           ^^^^^ expected `Self::DIM`, found `DIM`
-   |
-   = note: expected type `Self::DIM`
-              found type `DIM`
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:57:25
-   |
-LL |         let size = self.size();
-   |                         ^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `TensorSize::size`
-  --> $DIR/issue-83765.rs:11:30
-   |
-LL |     fn size(&self) -> [usize;Self::DIM];
-   |                              ^^^^^^^^^ required by this bound in `TensorSize::size`
-
-error[E0277]: the trait bound `[usize; _]: Default` is not satisfied
-  --> $DIR/issue-83765.rs:59:41
-   |
-LL |         let newindex : [usize;T::DIM] = Default::default();
-   |                                         ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]`
-   |
-help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
-   |
-LL | impl<'a,T : Broadcastable,const DIM : usize>  Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> where [usize; _]: Default
-   |                                                                                              +++++++++++++++++++++++++
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:78:51
-   |
-LL |     fn size(&self) -> [usize;DIM] {self.reference.size()}
-   |                                                   ^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `TensorSize::size`
-  --> $DIR/issue-83765.rs:11:30
-   |
-LL |     fn size(&self) -> [usize;Self::DIM];
-   |                              ^^^^^^^^^ required by this bound in `TensorSize::size`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-83765.rs:78:36
-   |
-LL |     fn size(&self) -> [usize;DIM] {self.reference.size()}
-   |                                    ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM`
-   |
-   = note: expected type `DIM`
-              found type `Self::DIM`
-
-error: unconstrained generic constant
-  --> $DIR/issue-83765.rs:90:24
-   |
-LL |         self.reference.bget(index).map(&self.closure)
-   |                        ^^^^
-   |
-   = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:`
-note: required by a bound in `Broadcastable::bget`
-  --> $DIR/issue-83765.rs:20:33
-   |
-LL |     fn bget(&self, index:[usize;Self::DIM]) -> Option<Self::Element>;
-   |                                 ^^^^^^^^^ required by this bound in `Broadcastable::bget`
-
-error[E0308]: mismatched types
-  --> $DIR/issue-83765.rs:90:29
-   |
-LL |         self.reference.bget(index).map(&self.closure)
-   |                             ^^^^^ expected `Self::DIM`, found `DIM`
+note: ...which requires checking if `TensorDimension` fulfills its obligations...
+  --> $DIR/issue-83765.rs:4:1
    |
-   = note: expected type `Self::DIM`
-              found type `DIM`
+LL | trait TensorDimension {
+   | ^^^^^^^^^^^^^^^^^^^^^
+   = note: ...which again requires resolving instance `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM`, completing the cycle
+   = note: cycle used when normalizing `<LazyUpdim<T, { T::DIM }, DIM> as TensorDimension>::DIM`
 
-error: aborting due to 12 previous errors
+error: aborting due to previous error
 
-Some errors have detailed explanations: E0277, E0308.
-For more information about an error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0391`.