Skip to content

Commit 67cb628

Browse files
committed
Ensure constants are WF before calling into CTFE
1 parent 0ad5959 commit 67cb628

31 files changed

+653
-186
lines changed

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 133 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use rustc_middle::ty::{
3838
self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperFoldable,
3939
TypeSuperVisitable, TypingMode, Upcast,
4040
};
41+
use rustc_session::lint;
4142
use rustc_span::def_id::DefId;
4243
use rustc_span::{DUMMY_SP, Span};
4344
use tracing::{debug, instrument};
@@ -545,75 +546,163 @@ 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. These needs to
612+
// be handled very specially, and the way we handle them is incompatible with the ways
613+
// that we must handle generic const args in the general case.
614+
615+
if uv.has_non_region_infer() {
616+
// Diagnostics will sometimes replace the identity args of anon consts in
617+
// array repeat expr counts with inference variables so we have to handle this
618+
// even though it is not something we should ever actually encounter.
619+
//
620+
// Array repeat expr counts are allowed to syntactically use generic parameters
621+
// but must not actually depend on them in order to evalaute successfully. This means
622+
// that it is actually fine to evalaute them in their own environment rather than with
623+
// the actually provided generic arguments.
624+
tcx.dcx().delayed_bug(
625+
"Encountered anon const with inference variable args but no error reported",
626+
);
627+
}
601628

629+
// Generic arguments to anon consts in the type system are never meaningful under mcg,
630+
// there either are no arguments or its a repeat count and the arguments must not be
631+
// depended on for evaluation.
602632
let args = GenericArgs::identity_for_item(tcx, uv.def);
603633
let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def);
634+
635+
// Instead of erroring when encountering a constant with impossible preds we just FCW, as
636+
// it would be a breaking change.
637+
if tcx
638+
.instantiate_and_check_impossible_predicates((uv.def, tcx.erase_regions(args)))
639+
{
640+
if let Some(local_def) = uv.def.as_local() {
641+
tcx.node_span_lint(
642+
lint::builtin::CONST_EVALUATABLE_UNCHECKED,
643+
tcx.local_def_id_to_hir_id(local_def),
644+
tcx.def_span(uv.def),
645+
|lint| {
646+
lint.primary_message(
647+
"cannot use constants which depend on trivially-false where clauses",
648+
);
649+
},
650+
)
651+
} else {
652+
// If the anon const is not a local def then it's probably a GCE const from some
653+
// upstream crate. We don't need to lint on that. It also shouldn't be possible
654+
// for an upstream crate to put a repeat expr count anon const into a signature
655+
// and have it *only* evaluated in a downstream crate.
656+
}
657+
};
658+
604659
(args, typing_env)
605660
} 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.
661+
// We are only dealing with "truly" generic/uninferred constants here:
662+
// - `generic_const_exprs` has been handled separately in the first if
663+
// - `min_const_generics` repeat expr count hacks have been handled in the previous if
664+
//
665+
// So we are free to simply defer evaluation here. This does assume that `uv.args` has
666+
// already been normalized.
667+
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
668+
return Err(EvaluateConstErr::HasGenericsOrInfers);
669+
}
670+
671+
// If we are dealing with a fully monomorphic constant then we should ensure that
672+
// it is well formed as otherwise CTFE will ICE. For the same reasons as with
673+
// deferring evaluation of generic/uninferred constants, we do not have to worry
674+
// about `generic_const_expr`
675+
//
676+
// This check is done in an empty environment which is a little weird, however, mir
677+
// bodies with impossible predicates (in an empty environment) are sometimes built as
678+
// only an `unreachable` terminator which makes evaluating them incorrect even if the
679+
// impossible pred is satsifiable in this environment.
680+
if tcx.instantiate_and_check_impossible_predicates((
681+
uv.def,
682+
tcx.erase_regions(uv.args),
683+
)) {
684+
// We treat these consts as rigid instead of an error or delaying a bug as we may
685+
// be checking a constant with a trivialy-false where clause that is satisfied from
686+
// a trivially-false clause in the environment.
687+
//
688+
// Delaying a bug would ICE the compiler as we may be in an environment where the
689+
// impossible pred actually holds.
690+
//
691+
// Emitting an error would be wrong as we may be normalizing inside of a probe where
692+
// an inference variable was inferred to a concrete value resulting in an impossible
693+
// predicate.
694+
return Ok(ct);
695+
}
696+
609697
let typing_env = infcx
610698
.typing_env(tcx.erase_regions(param_env))
611699
.with_post_analysis_normalized(tcx);
612700
(uv.args, typing_env)
613701
};
614-
let uv = ty::UnevaluatedConst::new(uv.def, args);
615702

703+
let uv = ty::UnevaluatedConst::new(uv.def, args);
616704
let erased_uv = tcx.erase_regions(uv);
705+
617706
use rustc_middle::mir::interpret::ErrorHandled;
618707
match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, DUMMY_SP) {
619708
Ok(Ok(val)) => Ok(ty::Const::new_value(
@@ -697,7 +786,7 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
697786
args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 })
698787
}
699788

700-
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
789+
/// Normalizes the predicates and checks whether they hold in a given empty. If this
701790
/// returns true, then either normalize encountered an error or one of the predicates did not
702791
/// hold. Used when creating vtables to check for unsatisfiable methods. This should not be
703792
/// used during analysis.

tests/crashes/127643.rs

Lines changed: 0 additions & 18 deletions
This file was deleted.

tests/crashes/131046.rs

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/crashes/131406.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/crashes/133066.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/crashes/133199.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
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() {}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
error[E0277]: the trait bound `(dyn Sized + 'static): ConstDefault` is not satisfied
2+
--> $DIR/wf_before_evaluate-2.rs:14:14
3+
|
4+
LL | fn user() -> impl Owner<dyn Sized, C = 0> {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `ConstDefault` is not implemented for `(dyn Sized + 'static)`
6+
|
7+
help: this trait has no implementations, consider adding one
8+
--> $DIR/wf_before_evaluate-2.rs:10:1
9+
|
10+
LL | trait ConstDefault {
11+
| ^^^^^^^^^^^^^^^^^^
12+
note: required for `()` to implement `Owner<(dyn Sized + 'static)>`
13+
--> $DIR/wf_before_evaluate-2.rs:6:23
14+
|
15+
LL | impl<K: ConstDefault> Owner<K> for () {
16+
| ------------ ^^^^^^^^ ^^
17+
| |
18+
| unsatisfied trait bound introduced here
19+
20+
error[E0277]: the size for values of type `(dyn Sized + 'static)` cannot be known at compilation time
21+
--> $DIR/wf_before_evaluate-2.rs:14:14
22+
|
23+
LL | fn user() -> impl Owner<dyn Sized, C = 0> {}
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
25+
|
26+
= help: the trait `Sized` is not implemented for `(dyn Sized + 'static)`
27+
= help: the trait `Owner<K>` is implemented for `()`
28+
note: required for `()` to implement `Owner<(dyn Sized + 'static)>`
29+
--> $DIR/wf_before_evaluate-2.rs:6:23
30+
|
31+
LL | impl<K: ConstDefault> Owner<K> for () {
32+
| - ^^^^^^^^ ^^
33+
| |
34+
| unsatisfied trait bound introduced here
35+
36+
error[E0038]: the trait `Sized` is not dyn compatible
37+
--> $DIR/wf_before_evaluate-2.rs:14:40
38+
|
39+
LL | fn user() -> impl Owner<dyn Sized, C = 0> {}
40+
| ^ `Sized` is not dyn compatible
41+
|
42+
= note: the trait is not dyn compatible because it requires `Self: Sized`
43+
= note: for a trait to be dyn compatible it needs to allow building a vtable
44+
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
45+
46+
error[E0308]: mismatched types
47+
--> $DIR/wf_before_evaluate-2.rs:14:40
48+
|
49+
LL | fn user() -> impl Owner<dyn Sized, C = 0> {}
50+
| ^ expected `dyn Sized`, found integer
51+
|
52+
= note: expected trait object `(dyn Sized + 'static)`
53+
found type `{integer}`
54+
55+
error: aborting due to 4 previous errors
56+
57+
Some errors have detailed explanations: E0038, E0277, E0308.
58+
For more information about an error, try `rustc --explain E0038`.

0 commit comments

Comments
 (0)