Skip to content

Commit ef54806

Browse files
committed
Properly defer evaluation of consts with infers/params
1 parent 4c5bbbb commit ef54806

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
@@ -773,6 +773,10 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
773773
}
774774

775775
impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_feature::Features {
776+
fn min_generic_const_args(self) -> bool {
777+
self.min_generic_const_args()
778+
}
779+
776780
fn generic_const_exprs(self) -> bool {
777781
self.generic_const_exprs()
778782
}

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

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

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

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

compiler/rustc_type_ir/src/inherent.rs

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

552552
pub trait Features<I: Interner>: Copy {
553+
fn min_generic_const_args(self) -> bool;
554+
553555
fn generic_const_exprs(self) -> bool;
554556

555557
fn coroutine_clone(self) -> bool;

0 commit comments

Comments
 (0)