Skip to content

Commit f770b29

Browse files
committed
Ensure constants are WF before calling into CTFE
1 parent 81d8edc commit f770b29

22 files changed

+544
-169
lines changed

compiler/rustc_middle/src/query/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -2374,6 +2374,18 @@ rustc_queries! {
23742374
}
23752375
}
23762376

2377+
query check_constant_safe_to_evaluate(
2378+
key: ty::CanonicalQueryInput<
2379+
TyCtxt<'tcx>,
2380+
ty::ParamEnvAnd<'tcx, ty::UnevaluatedConst<'tcx>>,
2381+
>
2382+
) -> bool {
2383+
desc { |tcx|
2384+
"checking constant `{}` is safe to evaluate",
2385+
tcx.def_path_str(key.canonical.value.into_parts().1.def)
2386+
}
2387+
}
2388+
23772389
query method_autoderef_steps(
23782390
goal: CanonicalTyGoal<'tcx>
23792391
) -> MethodAutoderefStepsResult<'tcx> {

compiler/rustc_trait_selection/src/traits/mod.rs

+145-44
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use std::ops::ControlFlow;
2828

2929
use rustc_errors::ErrorGuaranteed;
3030
use rustc_hir::def::DefKind;
31+
use rustc_infer::infer::canonical::OriginalQueryValues;
3132
pub use rustc_infer::traits::*;
3233
use rustc_middle::query::Providers;
3334
use rustc_middle::span_bug;
@@ -545,75 +546,130 @@ pub fn try_evaluate_const<'tcx>(
545546
| ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers),
546547
ty::ConstKind::Unevaluated(uv) => {
547548
// Postpone evaluation of constants that depend on generic parameters or
548-
// inference variables.
549+
// inference variables. Also ensure that constants are wf before passing
550+
// them onto CTFE.
549551
//
550-
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
552+
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
551553
// to be revealing opaque types here as borrowcheck has not run yet. However,
552554
// CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during
553555
// typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
554556
// As a result we always use a revealed env when resolving the instance to evaluate.
555557
//
556558
// FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
557559
// instead of having this logic here
558-
let (args, typing_env) = if tcx.features().generic_const_exprs()
559-
&& uv.has_non_region_infer()
560-
{
561-
// `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
562-
// inference variables and generic parameters to show up in `ty::Const` even though the anon const
563-
// does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
564-
match tcx.thir_abstract_const(uv.def) {
565-
Ok(Some(ct)) => {
566-
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args));
567-
if let Err(e) = ct.error_reported() {
568-
return Err(EvaluateConstErr::EvaluationFailure(e));
569-
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
570-
// If the anon const *does* actually use generic parameters or inference variables from
571-
// the generic arguments provided for it, then we should *not* attempt to evaluate it.
572-
return Err(EvaluateConstErr::HasGenericsOrInfers);
573-
} else {
574-
let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args);
575-
let typing_env = infcx
576-
.typing_env(tcx.erase_regions(param_env))
577-
.with_post_analysis_normalized(tcx);
560+
let (args, typing_env) = if tcx.features().generic_const_exprs() {
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+
// We don't bother trying to ensure GCE constants are WF before passing them to CTFE as it would cause
566+
// query cycles on almost every single call to this function.
567+
568+
if uv.has_non_region_infer() || uv.has_non_region_param() {
569+
// `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
570+
// inference variables and generic parameters to show up in `ty::Const` even though the anon const
571+
// does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
572+
match tcx.thir_abstract_const(uv.def) {
573+
Ok(Some(ct)) => {
574+
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args));
575+
if let Err(e) = ct.error_reported() {
576+
return Err(EvaluateConstErr::EvaluationFailure(e));
577+
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
578+
// If the anon const *does* actually use generic parameters or inference variables from
579+
// the generic arguments provided for it, then we should *not* attempt to evaluate it.
580+
return Err(EvaluateConstErr::HasGenericsOrInfers);
581+
} else {
582+
let args =
583+
replace_param_and_infer_args_with_placeholder(tcx, uv.args);
584+
let typing_env = infcx
585+
.typing_env(tcx.erase_regions(param_env))
586+
.with_post_analysis_normalized(tcx);
587+
(args, typing_env)
588+
}
589+
}
590+
Err(_) | Ok(None) => {
591+
let args = GenericArgs::identity_for_item(tcx, uv.def);
592+
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
578593
(args, typing_env)
579594
}
580595
}
581-
Err(_) | Ok(None) => {
582-
let args = GenericArgs::identity_for_item(tcx, uv.def);
583-
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
584-
(args, typing_env)
585-
}
596+
} else {
597+
let typing_env = infcx
598+
.typing_env(tcx.erase_regions(param_env))
599+
.with_post_analysis_normalized(tcx);
600+
(uv.args, typing_env)
586601
}
587-
} else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() {
602+
} else if !tcx.features().min_generic_const_args()
603+
&& !tcx.features().associated_const_equality()
604+
// We check for anon consts so that when `associated_const_equality` bounds are
605+
// lowered on stable we still handle them correctly to avoid ICEs in CTFE.
606+
&& tcx.def_kind(uv.def) == DefKind::AnonConst
607+
{
588608
// FIXME: remove this when `const_evaluatable_unchecked` is a hard error.
589609
//
590-
// Diagnostics will sometimes replace the identity args of anon consts in
591-
// array repeat expr counts with inference variables so we have to handle this
592-
// even though it is not something we should ever actually encounter.
593-
//
594-
// Array repeat expr counts are allowed to syntactically use generic parameters
595-
// but must not actually depend on them in order to evalaute successfully. This means
596-
// that it is actually fine to evalaute them in their own environment rather than with
597-
// the actually provided generic arguments.
598-
tcx.dcx().delayed_bug(
599-
"Encountered anon const with inference variable args but no error reported",
600-
);
610+
// Under `min_const_generics` the only place we can encounter generics in type system
611+
// consts is for the `const_evaluatable_unchecked` future compat lint. We don't care
612+
// to handle them in important ways (e.g. deferring evaluation) so we handle it separately.
613+
614+
if uv.has_non_region_infer() {
615+
// Diagnostics will sometimes replace the identity args of anon consts in
616+
// array repeat expr counts with inference variables so we have to handle this
617+
// even though it is not something we should ever actually encounter.
618+
//
619+
// Array repeat expr counts are allowed to syntactically use generic parameters
620+
// but must not actually depend on them in order to evalaute successfully. This means
621+
// that it is actually fine to evalaute them in their own environment rather than with
622+
// the actually provided generic arguments.
623+
tcx.dcx().delayed_bug(
624+
"Encountered anon const with inference variable args but no error reported",
625+
);
626+
}
601627

628+
// Generic arguments to anon consts in the type system are never meaningful under mcg,
629+
// there either are no arguments or its a repeat count and the arguments must not be
630+
// depended on for evaluation.
602631
let args = GenericArgs::identity_for_item(tcx, uv.def);
603632
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
604633
(args, typing_env)
605634
} else {
606-
// FIXME: This codepath is reachable under `associated_const_equality` and in the
607-
// future will be reachable by `min_generic_const_args`. We should handle inference
608-
// variables and generic parameters properly instead of doing nothing.
635+
// We are only dealing with "truly" generic/uninferred constants here:
636+
// - `generic_const_exprs` has been handled separately in the first if
637+
// - `min_const_generics` repeat expr count hacks have been handled in the previous if
638+
//
639+
// So we are free to simply defer evaluation here. This does assume that `uv.args` has
640+
// already been normalized.
641+
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
642+
return Err(EvaluateConstErr::HasGenericsOrInfers);
643+
}
644+
645+
// If we are dealing with a fully monomorphic constant then we should ensure that
646+
// it is well formed as otherwise CTFE will ICE. For the same reasons as with
647+
// deferring evaluation of generic/uninferred constants, we do not have to worry
648+
// about `generic_const_expr`
649+
if tcx.def_kind(uv.def) == DefKind::AnonConst
650+
&& tcx.predicates_of(uv.def).predicates.is_empty()
651+
{
652+
// ... skip doing any work
653+
} else {
654+
let input = infcx
655+
.canonicalize_query(param_env.and(uv), &mut OriginalQueryValues::default());
656+
if !tcx.check_constant_safe_to_evaluate(input) {
657+
let e = tcx.dcx().delayed_bug(
658+
"Attempted to evaluate illformed constant but no error emitted",
659+
);
660+
return Err(EvaluateConstErr::EvaluationFailure(e));
661+
}
662+
}
663+
609664
let typing_env = infcx
610665
.typing_env(tcx.erase_regions(param_env))
611666
.with_post_analysis_normalized(tcx);
612667
(uv.args, typing_env)
613668
};
614-
let uv = ty::UnevaluatedConst::new(uv.def, args);
615669

670+
let uv = ty::UnevaluatedConst::new(uv.def, args);
616671
let erased_uv = tcx.erase_regions(uv);
672+
617673
use rustc_middle::mir::interpret::ErrorHandled;
618674
match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
619675
Ok(Ok(val)) => Ok(ty::Const::new_value(
@@ -697,7 +753,51 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
697753
args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 })
698754
}
699755

700-
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
756+
#[instrument(level = "debug", skip(tcx), ret)]
757+
fn check_constant_safe_to_evaluate<'tcx>(
758+
tcx: TyCtxt<'tcx>,
759+
input: ty::CanonicalQueryInput<TyCtxt<'tcx>, ty::ParamEnvAnd<'tcx, ty::UnevaluatedConst<'tcx>>>,
760+
) -> bool {
761+
let (infcx, param_env_and, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &input);
762+
let (param_env, uv) = param_env_and.into_parts();
763+
764+
// We can't just register a wf goal for the constant as that would require evaluating the constant
765+
// which would result in a query cycle.
766+
let mut predicates = tcx.predicates_of(uv.def).instantiate(tcx, uv.args).predicates;
767+
768+
// Specifically check trait fulfillment to avoid an error when trying to resolve
769+
// associated items.
770+
if let Some(trait_def_id) = tcx.trait_of_item(uv.def) {
771+
let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, uv.args);
772+
predicates.push(trait_ref.upcast(tcx));
773+
}
774+
775+
let ocx = ObligationCtxt::new(&infcx);
776+
let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates);
777+
for predicate in predicates {
778+
let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate);
779+
ocx.register_obligation(obligation);
780+
}
781+
let errors = ocx.select_all_or_error();
782+
783+
if !errors.is_empty() {
784+
return false;
785+
}
786+
787+
// Leak check for any higher-ranked trait mismatches.
788+
// We only need to do this in the old solver, since the new solver already
789+
// leak-checks.
790+
if !infcx.next_trait_solver() && infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() {
791+
return false;
792+
}
793+
794+
// We don't check non-higher-ranked region constraints, but we don't need to as region
795+
// constraints cant affect coherence and therefore result in overlapping impls in codegen/ctfe
796+
// so it shouldn't matter to speculatively evaluate constants with failing region constraints.
797+
true
798+
}
799+
800+
/// Normalizes the predicates and checks whether they hold in a given empty. If this
701801
/// returns true, then either normalize encountered an error or one of the predicates did not
702802
/// hold. Used when creating vtables to check for unsatisfiable methods. This should not be
703803
/// used during analysis.
@@ -840,6 +940,7 @@ pub fn provide(providers: &mut Providers) {
840940
specialization_enabled_in: specialize::specialization_enabled_in,
841941
instantiate_and_check_impossible_predicates,
842942
is_impossible_associated_item,
943+
check_constant_safe_to_evaluate,
843944
..*providers
844945
};
845946
}

tests/crashes/127643.rs

-18
This file was deleted.

tests/crashes/131046.rs

-15
This file was deleted.

tests/crashes/131406.rs

-12
This file was deleted.

tests/crashes/133066.rs

-12
This file was deleted.

tests/crashes/133199.rs

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![feature(associated_const_equality)]
2+
3+
trait Owner<K> {
4+
const C: K;
5+
}
6+
impl<K: ConstDefault> Owner<K> for () {
7+
const C: K = K::DEFAULT;
8+
}
9+
10+
trait ConstDefault {
11+
const DEFAULT: Self;
12+
}
13+
14+
fn user() -> impl Owner<dyn Sized, C = 0> {}
15+
//~^ ERROR: the trait bound `(dyn Sized + 'static): ConstDefault` is not satisfied
16+
//~| ERROR: the size for values of type `(dyn Sized + 'static)` cannot be known at compilation time
17+
//~| ERROR: the trait `Sized` is not dyn compatible
18+
//~| ERROR: mismatched types
19+
20+
fn main() {}

0 commit comments

Comments
 (0)