Skip to content

Commit e29821f

Browse files
committed
Auto merge of #111623 - BoxyUwU:move_eval_hack, r=compiler-errors
move `super_relate_consts` hack to `normalize_param_env_or_error` `super_relate_consts` has as hack in it to work around the fact that `normalize_param_env_or_error` is broken. When relating two constants we attempt to evaluate them (aka normalize them). This is not an issue in any way specific to const generics, type aliases also have the same issue as demonstrated in [this code](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=84b6d3956a2c852a04b60782476b56c9). Since the hack in `super_relate_consts` only exists to make `normalize_param_env_or_error` emit less errors move it to `normalize_param_env_or_error`. This makes `super_relate_consts` act more like the normal plain structural equality its supposed to and should help ensure that the hack doesnt accidentally affect other situations. r? `@compiler-errors`
2 parents 617d3d6 + 21cf9ea commit e29821f

File tree

3 files changed

+81
-16
lines changed

3 files changed

+81
-16
lines changed

compiler/rustc_middle/src/ty/relate.rs

-11
Original file line numberDiff line numberDiff line change
@@ -589,17 +589,6 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
589589
debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
590590
let tcx = relation.tcx();
591591

592-
// HACK(const_generics): We still need to eagerly evaluate consts when
593-
// relating them because during `normalize_param_env_or_error`,
594-
// we may relate an evaluated constant in a obligation against
595-
// an unnormalized (i.e. unevaluated) const in the param-env.
596-
// FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
597-
// these `eval` calls can be removed.
598-
if !tcx.features().generic_const_exprs {
599-
a = a.eval(tcx, relation.param_env());
600-
b = b.eval(tcx, relation.param_env());
601-
}
602-
603592
if tcx.features().generic_const_exprs {
604593
a = tcx.expand_abstract_consts(a);
605594
b = tcx.expand_abstract_consts(b);

compiler/rustc_trait_selection/src/traits/mod.rs

+57-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use rustc_errors::ErrorGuaranteed;
3232
use rustc_middle::query::Providers;
3333
use rustc_middle::ty::fold::TypeFoldable;
3434
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
35-
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
35+
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFolder, TypeSuperVisitable};
3636
use rustc_middle::ty::{InternalSubsts, SubstsRef};
3737
use rustc_span::def_id::DefId;
3838
use rustc_span::Span;
@@ -272,8 +272,62 @@ pub fn normalize_param_env_or_error<'tcx>(
272272
// parameter environments once for every fn as it goes,
273273
// and errors will get reported then; so outside of type inference we
274274
// can be sure that no errors should occur.
275-
let mut predicates: Vec<_> =
276-
util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
275+
let mut predicates: Vec<_> = util::elaborate(
276+
tcx,
277+
unnormalized_env.caller_bounds().into_iter().map(|predicate| {
278+
if tcx.features().generic_const_exprs {
279+
return predicate;
280+
}
281+
282+
struct ConstNormalizer<'tcx>(TyCtxt<'tcx>);
283+
284+
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ConstNormalizer<'tcx> {
285+
fn interner(&self) -> TyCtxt<'tcx> {
286+
self.0
287+
}
288+
289+
fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
290+
// While it is pretty sus to be evaluating things with an empty param env, it
291+
// should actually be okay since without `feature(generic_const_exprs)` the only
292+
// const arguments that have a non-empty param env are array repeat counts. These
293+
// do not appear in the type system though.
294+
c.eval(self.0, ty::ParamEnv::empty())
295+
}
296+
}
297+
298+
// This whole normalization step is a hack to work around the fact that
299+
// `normalize_param_env_or_error` is fundamentally broken from using an
300+
// unnormalized param env with a trait solver that expects the param env
301+
// to be normalized.
302+
//
303+
// When normalizing the param env we can end up evaluating obligations
304+
// that have been normalized but can only be proven via a where clause
305+
// which is still in its unnormalized form. example:
306+
//
307+
// Attempting to prove `T: Trait<<u8 as Identity>::Assoc>` in a param env
308+
// with a `T: Trait<<u8 as Identity>::Assoc>` where clause will fail because
309+
// we first normalize obligations before proving them so we end up proving
310+
// `T: Trait<u8>`. Since lazy normalization is not implemented equating `u8`
311+
// with `<u8 as Identity>::Assoc` fails outright so we incorrectly believe that
312+
// we cannot prove `T: Trait<u8>`.
313+
//
314+
// The same thing is true for const generics- attempting to prove
315+
// `T: Trait<ConstKind::Unevaluated(...)>` with the same thing as a where clauses
316+
// will fail. After normalization we may be attempting to prove `T: Trait<4>` with
317+
// the unnormalized where clause `T: Trait<ConstKind::Unevaluated(...)>`. In order
318+
// for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)`
319+
// but as we do not have lazy norm implemented, equating the two consts fails outright.
320+
//
321+
// Ideally we would not normalize consts here at all but it is required for backwards
322+
// compatibility. Eventually when lazy norm is implemented this can just be removed.
323+
// We do not normalize types here as there is no backwards compatibility requirement
324+
// for us to do so.
325+
//
326+
// FIXME(-Ztrait-solver=next): remove this hack since we have deferred projection equality
327+
predicate.fold_with(&mut ConstNormalizer(tcx))
328+
}),
329+
)
330+
.collect();
277331

278332
debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
279333

Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11
// compile-flags: -Ztrait-solver=next
22
// check-pass
33

4-
#[derive(Default)]
54
struct Foo {
65
x: i32,
76
}
87

8+
impl MyDefault for Foo {
9+
fn my_default() -> Self {
10+
Self {
11+
x: 0,
12+
}
13+
}
14+
}
15+
16+
trait MyDefault {
17+
fn my_default() -> Self;
18+
}
19+
20+
impl MyDefault for [Foo; 0] {
21+
fn my_default() -> Self {
22+
[]
23+
}
24+
}
25+
impl MyDefault for [Foo; 1] {
26+
fn my_default() -> Self {
27+
[Foo::my_default(); 1]
28+
}
29+
}
30+
931
fn main() {
10-
let mut xs = <[Foo; 1]>::default();
32+
let mut xs = <[Foo; 1]>::my_default();
1133
xs[0].x = 1;
1234
(&mut xs[0]).x = 2;
1335
}

0 commit comments

Comments
 (0)