Skip to content

Commit efc52dd

Browse files
committed
Auto merge of rust-lang#137972 - BoxyUwU:ty_const_wf_before_eval, r=<try>
Ensure constants are WF before calling into CTFE Fixes rust-lang#127643 Fixes rust-lang#131046 Fixes rust-lang#131406 Fixes rust-lang#133066 I'll write a PR desc for this tommorow r? `@ghost`
2 parents fd17dea + d2152f4 commit efc52dd

23 files changed

+560
-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

+156-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,141 @@ 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+
673+
if !tcx.features().generic_const_exprs() {
674+
let input = infcx
675+
.canonicalize_query(param_env.and(uv), &mut OriginalQueryValues::default());
676+
if !tcx.check_constant_safe_to_evaluate(input) {
677+
let e = tcx.dcx().delayed_bug(
678+
"Attempted to evaluate illformed constant but no error emitted",
679+
);
680+
return Err(EvaluateConstErr::EvaluationFailure(e));
681+
}
682+
}
683+
617684
use rustc_middle::mir::interpret::ErrorHandled;
618685
match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
619686
Ok(Ok(val)) => Ok(ty::Const::new_value(
@@ -697,7 +764,51 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
697764
args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 })
698765
}
699766

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

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)