about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs60
2 files changed, 57 insertions, 14 deletions
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 3bbe6a23b66..8bcae3d9ab7 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -589,17 +589,6 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
     debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
     let tcx = relation.tcx();
 
-    // HACK(const_generics): We still need to eagerly evaluate consts when
-    // relating them because during `normalize_param_env_or_error`,
-    // we may relate an evaluated constant in a obligation against
-    // an unnormalized (i.e. unevaluated) const in the param-env.
-    // FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
-    // these `eval` calls can be removed.
-    if !tcx.features().generic_const_exprs {
-        a = a.eval(tcx, relation.param_env());
-        b = b.eval(tcx, relation.param_env());
-    }
-
     if tcx.features().generic_const_exprs {
         a = tcx.expand_abstract_consts(a);
         b = tcx.expand_abstract_consts(b);
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 4b38687eebe..f7389bda159 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -32,7 +32,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFolder, TypeSuperVisitable};
 use rustc_middle::ty::{InternalSubsts, SubstsRef};
 use rustc_span::def_id::DefId;
 use rustc_span::Span;
@@ -272,8 +272,62 @@ pub fn normalize_param_env_or_error<'tcx>(
     // parameter environments once for every fn as it goes,
     // and errors will get reported then; so outside of type inference we
     // can be sure that no errors should occur.
-    let mut predicates: Vec<_> =
-        util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
+    let mut predicates: Vec<_> = util::elaborate(
+        tcx,
+        unnormalized_env.caller_bounds().into_iter().map(|predicate| {
+            if tcx.features().generic_const_exprs {
+                return predicate;
+            }
+
+            struct ConstNormalizer<'tcx>(TyCtxt<'tcx>);
+
+            impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ConstNormalizer<'tcx> {
+                fn interner(&self) -> TyCtxt<'tcx> {
+                    self.0
+                }
+
+                fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+                    // While it is pretty sus to be evaluating things with an empty param env, it
+                    // should actually be okay since without `feature(generic_const_exprs)` the only
+                    // const arguments that have a non-empty param env are array repeat counts. These
+                    // do not appear in the type system though.
+                    c.eval(self.0, ty::ParamEnv::empty())
+                }
+            }
+
+            // This whole normalization step is a hack to work around the fact that
+            // `normalize_param_env_or_error` is fundamentally broken from using an
+            // unnormalized param env with a trait solver that expects the param env
+            // to be normalized.
+            //
+            // When normalizing the param env we can end up evaluating obligations
+            // that have been normalized but can only be proven via a where clause
+            // which is still in its unnormalized form. example:
+            //
+            // Attempting to prove `T: Trait<<u8 as Identity>::Assoc>` in a param env
+            // with a `T: Trait<<u8 as Identity>::Assoc>` where clause will fail because
+            // we first normalize obligations before proving them so we end up proving
+            // `T: Trait<u8>`. Since lazy normalization is not implemented equating `u8`
+            // with `<u8 as Identity>::Assoc` fails outright so we incorrectly believe that
+            // we cannot prove `T: Trait<u8>`.
+            //
+            // The same thing is true for const generics- attempting to prove
+            // `T: Trait<ConstKind::Unevaluated(...)>` with the same thing as a where clauses
+            // will fail. After normalization we may be attempting to prove `T: Trait<4>` with
+            // the unnormalized where clause `T: Trait<ConstKind::Unevaluated(...)>`. In order
+            // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)`
+            // but as we do not have lazy norm implemented, equating the two consts fails outright.
+            //
+            // Ideally we would not normalize consts here at all but it is required for backwards
+            // compatibility. Eventually when lazy norm is implemented this can just be removed.
+            // We do not normalize types here as there is no backwards compatibility requirement
+            // for us to do so.
+            //
+            // FIXME(-Ztrait-solver=next): remove this hack since we have deferred projection equality
+            predicate.fold_with(&mut ConstNormalizer(tcx))
+        }),
+    )
+    .collect();
 
     debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);