Skip to content

Commit 0d0ef99

Browse files
committed
Properly defer evaluation of consts with infers/params
1 parent 2fe5143 commit 0d0ef99

File tree

5 files changed

+95
-48
lines changed

5 files changed

+95
-48
lines changed

compiler/rustc_middle/src/ty/context.rs

+4
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,10 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
761761
}
762762

763763
impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_feature::Features {
764+
fn min_generic_const_args(self) -> bool {
765+
self.min_generic_const_args()
766+
}
767+
764768
fn generic_const_exprs(self) -> bool {
765769
self.generic_const_exprs()
766770
}

compiler/rustc_next_trait_solver/src/solve/mod.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,26 @@ where
143143
) -> QueryResult<I> {
144144
match ct.kind() {
145145
ty::ConstKind::Unevaluated(uv) => {
146+
// `ConstEvaluatable` goals don't really need to exist under `mgca` as we can assume all
147+
// generic const args can be sucessfully evaluated as they have been checked at def site.
148+
//
149+
// The only reason we keep this around is so that wf checking of signatures is guaranteed
150+
// to wind up normalizing constants emitting errors if they are ill formed. The equivalent
151+
// check does not exist for types and results in diverging aliases not being normalized during
152+
// wfck sometimes.
153+
//
154+
// Regardless, the point being that the behaviour of this goal doesn't really matter so we just
155+
// always return `Ok` and evaluate for the CTFE side effect of emitting an error.
156+
if self.cx().features().min_generic_const_args() {
157+
let _ = self.evaluate_const(param_env, uv);
158+
return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
159+
}
160+
146161
// We never return `NoSolution` here as `evaluate_const` emits an
147162
// error itself when failing to evaluate, so emitting an additional fulfillment
148163
// error in that case is unnecessary noise. This may change in the future once
149164
// evaluation failures are allowed to impact selection, e.g. generic const
150165
// expressions in impl headers or `where`-clauses.
151-
152-
// FIXME(generic_const_exprs): Implement handling for generic
153-
// const expressions here.
154166
if let Some(_normalized) = self.evaluate_const(param_env, uv) {
155167
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
156168
} else {

compiler/rustc_trait_selection/src/traits/const_evaluatable.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,16 @@ pub fn is_const_evaluatable<'tcx>(
8686
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
8787
}
8888
} else if tcx.features().min_generic_const_args() {
89-
// This is a sanity check to make sure that non-generics consts are checked to
90-
// be evaluatable in case they aren't cchecked elsewhere. This will NOT error
91-
// if the const uses generics, as desired.
89+
// `ConstEvaluatable` goals don't really need to exist under `mgca` as we can assume all
90+
// generic const args can be sucessfully evaluated as they have been checked at def site.
91+
//
92+
// The only reason we keep this around is so that wf checking of signatures is guaranteed
93+
// to wind up normalizing constants emitting errors if they are ill formed. The equivalent
94+
// check does not exist for types and results in diverging aliases not being normalized during
95+
// wfck sometimes.
96+
//
97+
// Regardless, the point being that the behaviour of this goal doesn't really matter so we just
98+
// always return `Ok` and evaluate for the CTFE side effect of emitting an error.
9299
crate::traits::evaluate_const(infcx, unexpanded_ct, param_env);
93100
Ok(())
94101
} else {

compiler/rustc_trait_selection/src/traits/mod.rs

+64-42
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ pub fn try_evaluate_const<'tcx>(
547547
// Postpone evaluation of constants that depend on generic parameters or
548548
// inference variables.
549549
//
550-
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
550+
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
551551
// to be revealing opaque types here as borrowcheck has not run yet. However,
552552
// CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during
553553
// typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
@@ -557,68 +557,90 @@ pub fn try_evaluate_const<'tcx>(
557557
// instead of having this logic here
558558
let (args, typing_env) = if tcx.def_kind(uv.def) == DefKind::AnonConst
559559
&& let ty::AnonConstKind::GCEConst = tcx.anon_const_kind(uv.def)
560-
&& uv.has_non_region_infer()
561560
{
562-
// `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
563-
// inference variables and generic parameters to show up in `ty::Const` even though the anon const
564-
// does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
565-
match tcx.thir_abstract_const(uv.def) {
566-
Ok(Some(ct)) => {
567-
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args));
568-
if let Err(e) = ct.error_reported() {
569-
return Err(EvaluateConstErr::EvaluationFailure(e));
570-
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
571-
// If the anon const *does* actually use generic parameters or inference variables from
572-
// the generic arguments provided for it, then we should *not* attempt to evaluate it.
573-
return Err(EvaluateConstErr::HasGenericsOrInfers);
574-
} else {
575-
let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args);
576-
let typing_env = infcx
577-
.typing_env(tcx.erase_regions(param_env))
578-
.with_post_analysis_normalized(tcx);
561+
// We handle `generic_const_exprs` separately as reasonable ways of handling constants in the type system
562+
// completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason
563+
// about if you have to consider gce whatsoever.
564+
565+
if uv.has_non_region_infer() || uv.has_non_region_param() {
566+
// `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
567+
// inference variables and generic parameters to show up in `ty::Const` even though the anon const
568+
// does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
569+
match tcx.thir_abstract_const(uv.def) {
570+
Ok(Some(ct)) => {
571+
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args));
572+
if let Err(e) = ct.error_reported() {
573+
return Err(EvaluateConstErr::EvaluationFailure(e));
574+
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
575+
// If the anon const *does* actually use generic parameters or inference variables from
576+
// the generic arguments provided for it, then we should *not* attempt to evaluate it.
577+
return Err(EvaluateConstErr::HasGenericsOrInfers);
578+
} else {
579+
let args =
580+
replace_param_and_infer_args_with_placeholder(tcx, uv.args);
581+
let typing_env = infcx
582+
.typing_env(tcx.erase_regions(param_env))
583+
.with_post_analysis_normalized(tcx);
584+
(args, typing_env)
585+
}
586+
}
587+
Err(_) | Ok(None) => {
588+
let args = GenericArgs::identity_for_item(tcx, uv.def);
589+
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
579590
(args, typing_env)
580591
}
581592
}
582-
Err(_) | Ok(None) => {
583-
let args = GenericArgs::identity_for_item(tcx, uv.def);
584-
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
585-
(args, typing_env)
586-
}
593+
} else {
594+
let typing_env = infcx
595+
.typing_env(tcx.erase_regions(param_env))
596+
.with_post_analysis_normalized(tcx);
597+
(uv.args, typing_env)
587598
}
588599
} else if tcx.def_kind(uv.def) == DefKind::AnonConst
589600
&& let ty::AnonConstKind::RepeatExprCount = tcx.anon_const_kind(uv.def)
590-
&& uv.has_non_region_infer()
591601
{
592-
// FIXME: remove this when `const_evaluatable_unchecked` is a hard error.
593-
//
594-
// Diagnostics will sometimes replace the identity args of anon consts in
595-
// array repeat expr counts with inference variables so we have to handle this
596-
// even though it is not something we should ever actually encounter.
597-
//
598-
// Array repeat expr counts are allowed to syntactically use generic parameters
599-
// but must not actually depend on them in order to evalaute successfully. This means
600-
// that it is actually fine to evalaute them in their own environment rather than with
601-
// the actually provided generic arguments.
602-
tcx.dcx().delayed_bug(
603-
"Encountered anon const with inference variable args but no error reported",
604-
);
602+
if uv.has_non_region_infer() {
603+
// Diagnostics will sometimes replace the identity args of anon consts in
604+
// array repeat expr counts with inference variables so we have to handle this
605+
// even though it is not something we should ever actually encounter.
606+
//
607+
// Array repeat expr counts are allowed to syntactically use generic parameters
608+
// but must not actually depend on them in order to evalaute successfully. This means
609+
// that it is actually fine to evalaute them in their own environment rather than with
610+
// the actually provided generic arguments.
611+
tcx.dcx().delayed_bug(
612+
"Encountered anon const with inference variable args but no error reported",
613+
);
614+
}
605615

616+
// The generic args of repeat expr counts under `min_const_generics` are not supposed to
617+
// affect evaluation of the constant as this would make it a "truly" generic const arg.
618+
// To prevent this we discard all the generic arguments and evalaute with identity args
619+
// and in its own environment instead of the current environment we are normalizing in.
606620
let args = GenericArgs::identity_for_item(tcx, uv.def);
607621
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
608622

609623
(args, typing_env)
610624
} else {
611-
// FIXME: This codepath is reachable under `associated_const_equality` and in the
612-
// future will be reachable by `min_generic_const_args`. We should handle inference
613-
// variables and generic parameters properly instead of doing nothing.
625+
// We are only dealing with "truly" generic/uninferred constants here:
626+
// - GCEConsts have been handled separately
627+
// - Repeat expr count back compat consts have also been handled separately
628+
// So we are free to simply defer evaluation here.
629+
//
630+
// FIXME: This assumes that `args` are normalized which is not necessarily true
631+
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
632+
return Err(EvaluateConstErr::HasGenericsOrInfers);
633+
}
634+
614635
let typing_env = infcx
615636
.typing_env(tcx.erase_regions(param_env))
616637
.with_post_analysis_normalized(tcx);
617638
(uv.args, typing_env)
618639
};
619-
let uv = ty::UnevaluatedConst::new(uv.def, args);
620640

641+
let uv = ty::UnevaluatedConst::new(uv.def, args);
621642
let erased_uv = tcx.erase_regions(uv);
643+
622644
use rustc_middle::mir::interpret::ErrorHandled;
623645
match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
624646
Ok(Ok(val)) => Ok(ty::Const::new_value(

compiler/rustc_type_ir/src/inherent.rs

+2
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
570570
}
571571

572572
pub trait Features<I: Interner>: Copy {
573+
fn min_generic_const_args(self) -> bool;
574+
573575
fn generic_const_exprs(self) -> bool;
574576

575577
fn coroutine_clone(self) -> bool;

0 commit comments

Comments
 (0)