Skip to content

Commit 5f88c8c

Browse files
committed
Ensure constants are WF before calling into CTFE
1 parent 78bb3a2 commit 5f88c8c

File tree

29 files changed

+537
-188
lines changed

29 files changed

+537
-188
lines changed

compiler/rustc_const_eval/src/check_consts/ops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
142142
|err, self_ty, trait_id| {
143143
// FIXME(const_trait_impl): Do we need any of this on the non-const codepath?
144144

145-
let trait_ref = TraitRef::from_method(tcx, trait_id, self.args);
145+
let trait_ref = TraitRef::from_assoc_args(tcx, trait_id, self.args);
146146

147147
match self_ty.kind() {
148148
Param(param_ty) => {

compiler/rustc_const_eval/src/interpret/call.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
741741
let tcx = *self.tcx;
742742

743743
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
744-
let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args);
744+
let virtual_trait_ref =
745+
ty::TraitRef::from_assoc_args(tcx, trait_def_id, virtual_instance.args);
745746
let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
746747
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
747748

compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ pub(crate) fn transform_instance<'tcx>(
343343
let upcast_ty = match tcx.trait_of_item(def_id) {
344344
Some(trait_id) => trait_object_ty(
345345
tcx,
346-
ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
346+
ty::Binder::dummy(ty::TraitRef::from_assoc_args(tcx, trait_id, instance.args)),
347347
),
348348
// drop_in_place won't have a defining trait, skip the upcast
349349
None => instance.args.type_at(0),
@@ -482,7 +482,7 @@ fn implemented_method<'tcx>(
482482
trait_method = trait_method_bound;
483483
method_id = instance.def_id();
484484
trait_id = tcx.trait_of_item(method_id)?;
485-
trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args));
485+
trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc_args(tcx, trait_id, instance.args));
486486
trait_id
487487
} else {
488488
return None;

compiler/rustc_trait_selection/src/traits/mod.rs

+107-45
Original file line numberDiff line numberDiff line change
@@ -545,75 +545,137 @@ pub fn try_evaluate_const<'tcx>(
545545
| ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers),
546546
ty::ConstKind::Unevaluated(uv) => {
547547
// Postpone evaluation of constants that depend on generic parameters or
548-
// inference variables.
548+
// inference variables. Also ensure that constants are wf before passing
549+
// them onto CTFE.
549550
//
550-
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
551+
// We use `TypingMode::PostAnalysis` here which is not *technically* correct
551552
// to be revealing opaque types here as borrowcheck has not run yet. However,
552553
// CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during
553554
// typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821).
554555
// As a result we always use a revealed env when resolving the instance to evaluate.
555556
//
556557
// FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself
557558
// 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);
559+
let (args, typing_env) = if tcx.features().generic_const_exprs() {
560+
// We handle `generic_const_exprs` separately as reasonable ways of handling constants in the type system
561+
// completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason
562+
// about if you have to consider gce whatsoever.
563+
//
564+
// We don't bother trying to ensure GCE constants are WF before passing them to CTFE as it would cause
565+
// query cycles on almost every single call to this function.
566+
567+
if uv.has_non_region_infer() || uv.has_non_region_param() {
568+
// `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause
569+
// inference variables and generic parameters to show up in `ty::Const` even though the anon const
570+
// does not actually make use of them. We handle this case specially and attempt to evaluate anyway.
571+
match tcx.thir_abstract_const(uv.def) {
572+
Ok(Some(ct)) => {
573+
let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args));
574+
if let Err(e) = ct.error_reported() {
575+
return Err(EvaluateConstErr::EvaluationFailure(e));
576+
} else if ct.has_non_region_infer() || ct.has_non_region_param() {
577+
// If the anon const *does* actually use generic parameters or inference variables from
578+
// the generic arguments provided for it, then we should *not* attempt to evaluate it.
579+
return Err(EvaluateConstErr::HasGenericsOrInfers);
580+
} else {
581+
let args =
582+
replace_param_and_infer_args_with_placeholder(tcx, uv.args);
583+
let typing_env = infcx
584+
.typing_env(tcx.erase_regions(param_env))
585+
.with_post_analysis_normalized(tcx);
586+
(args, typing_env)
587+
}
588+
}
589+
Err(_) | Ok(None) => {
590+
let args = GenericArgs::identity_for_item(tcx, uv.def);
591+
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
578592
(args, typing_env)
579593
}
580594
}
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-
}
595+
} else {
596+
let typing_env = infcx
597+
.typing_env(tcx.erase_regions(param_env))
598+
.with_post_analysis_normalized(tcx);
599+
(uv.args, typing_env)
586600
}
587-
} else if tcx.def_kind(uv.def) == DefKind::AnonConst && uv.has_non_region_infer() {
601+
} else if !tcx.features().min_generic_const_args()
602+
&& !tcx.features().associated_const_equality()
603+
// We check for anon consts so that when `associated_const_equality` bounds are
604+
// lowered on stable we still handle them correctly to avoid ICEs in CTFE.
605+
&& tcx.def_kind(uv.def) == DefKind::AnonConst
606+
{
588607
// FIXME: remove this when `const_evaluatable_unchecked` is a hard error.
589608
//
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-
);
609+
// Under `min_const_generics` the only place we can encounter generics in type system
610+
// consts is for the `const_evaluatable_unchecked` future compat lint. We don't care
611+
// to handle them in important ways (e.g. deferring evaluation) so we handle it separately.
612+
613+
if uv.has_non_region_infer() {
614+
// Diagnostics will sometimes replace the identity args of anon consts in
615+
// array repeat expr counts with inference variables so we have to handle this
616+
// even though it is not something we should ever actually encounter.
617+
//
618+
// Array repeat expr counts are allowed to syntactically use generic parameters
619+
// but must not actually depend on them in order to evalaute successfully. This means
620+
// that it is actually fine to evalaute them in their own environment rather than with
621+
// the actually provided generic arguments.
622+
tcx.dcx().delayed_bug(
623+
"Encountered anon const with inference variable args but no error reported",
624+
);
625+
}
601626

627+
// Generic arguments to anon consts in the type system are never meaningful under mcg,
628+
// there either are no arguments or its a repeat count and the arguments must not be
629+
// depended on for evaluation.
602630
let args = GenericArgs::identity_for_item(tcx, uv.def);
603631
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
604632
(args, typing_env)
605633
} 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.
634+
// We are only dealing with "truly" generic/uninferred constants here:
635+
// - `generic_const_exprs` has been handled separately in the first if
636+
// - `min_const_generics` repeat expr count hacks have been handled in the previous if
637+
//
638+
// So we are free to simply defer evaluation here. This does assume that `uv.args` has
639+
// already been normalized.
640+
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
641+
return Err(EvaluateConstErr::HasGenericsOrInfers);
642+
}
643+
644+
// If we are dealing with a fully monomorphic constant then we should ensure that
645+
// it is well formed as otherwise CTFE will ICE. For the same reasons as with
646+
// deferring evaluation of generic/uninferred constants, we do not have to worry
647+
// about `generic_const_expr`
648+
//
649+
// This check is done in an empty environment which is a little weird, however, mir
650+
// bodies with impossible predicates (in an empty environment) are sometimes built as
651+
// only an `unreachable` terminator which makes evaluating them incorrect even if the
652+
// impossible pred is satsifiable in this environment.
653+
if tcx.instantiate_and_check_impossible_predicates((
654+
uv.def,
655+
tcx.erase_regions(uv.args),
656+
)) {
657+
// We treat these consts as rigid instead of an error or delaying a bug as we may
658+
// be checking a constant with a trivialy-false where clause that is satisfied from
659+
// a trivially-false clause in the environment.
660+
//
661+
// Delaying a bug would ICE the compiler as we may be in an environment where the
662+
// impossible pred actually holds.
663+
//
664+
// Emitting an error would be wrong as we may be normalizing inside of a probe where
665+
// an inference variable was inferred to a concrete value resulting in an impossible
666+
// predicate.
667+
return Ok(ct);
668+
}
669+
609670
let typing_env = infcx
610671
.typing_env(tcx.erase_regions(param_env))
611672
.with_post_analysis_normalized(tcx);
612673
(uv.args, typing_env)
613674
};
614-
let uv = ty::UnevaluatedConst::new(uv.def, args);
615675

676+
let uv = ty::UnevaluatedConst::new(uv.def, args);
616677
let erased_uv = tcx.erase_regions(uv);
678+
617679
use rustc_middle::mir::interpret::ErrorHandled;
618680
match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
619681
Ok(Ok(val)) => Ok(ty::Const::new_value(
@@ -697,7 +759,7 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
697759
args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 })
698760
}
699761

700-
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
762+
/// Normalizes the predicates and checks whether they hold in a given empty. If this
701763
/// returns true, then either normalize encountered an error or one of the predicates did not
702764
/// hold. Used when creating vtables to check for unsatisfiable methods. This should not be
703765
/// used during analysis.
@@ -738,7 +800,7 @@ fn instantiate_and_check_impossible_predicates<'tcx>(
738800
// Specifically check trait fulfillment to avoid an error when trying to resolve
739801
// associated items.
740802
if let Some(trait_def_id) = tcx.trait_of_item(key.0) {
741-
let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1);
803+
let trait_ref = ty::TraitRef::from_assoc_args(tcx, trait_def_id, key.1);
742804
predicates.push(trait_ref.upcast(tcx));
743805
}
744806

compiler/rustc_ty_utils/src/instance.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ fn resolve_associated_item<'tcx>(
102102
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
103103
debug!(?trait_item_id, ?typing_env, ?trait_id, ?rcvr_args, "resolve_associated_item");
104104

105-
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_args);
105+
let trait_ref = ty::TraitRef::from_assoc_args(tcx, trait_id, rcvr_args);
106106

107107
let input = typing_env.as_query_input(trait_ref);
108108
let vtbl = match tcx.codegen_select_candidate(input) {
@@ -232,7 +232,7 @@ fn resolve_associated_item<'tcx>(
232232
Some(ty::Instance::new(leaf_def.item.def_id, args))
233233
}
234234
traits::ImplSource::Builtin(BuiltinImplSource::Object(_), _) => {
235-
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_args);
235+
let trait_ref = ty::TraitRef::from_assoc_args(tcx, trait_id, rcvr_args);
236236
if trait_ref.has_non_region_infer() || trait_ref.has_non_region_param() {
237237
// We only resolve totally substituted vtable entries.
238238
None

compiler/rustc_type_ir/src/predicate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl<I: Interner> TraitRef<I> {
7474
Self::new_from_args(interner, trait_def_id, args)
7575
}
7676

77-
pub fn from_method(interner: I, trait_id: I::DefId, args: I::GenericArgs) -> TraitRef<I> {
77+
pub fn from_assoc_args(interner: I, trait_id: I::DefId, args: I::GenericArgs) -> TraitRef<I> {
7878
let generics = interner.generics_of(trait_id);
7979
TraitRef::new(interner, trait_id, args.iter().take(generics.count()))
8080
}

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)