Skip to content

Commit 8a1214a

Browse files
committed
interpret: sanity-check that required_consts captures all consts that can fail
1 parent aa51aec commit 8a1214a

File tree

7 files changed

+59
-31
lines changed

7 files changed

+59
-31
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

+14
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,20 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
375375

376376
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
377377

378+
#[inline]
379+
fn all_required_consts_are_checked(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
380+
// Generally we expect required_consts to be properly filled, except for promoteds where
381+
// storing these consts shows up negatively in benchmarks. A promoted can only be relevant
382+
// if its parent MIR is relevant, and the consts in the promoted will be in the parent's
383+
// `required_consts`, so we are still sure to catch any const-eval bugs, just a bit less
384+
// directly.
385+
if ecx.frame_idx() == 0 && ecx.frame().body.source.promoted.is_some() {
386+
false
387+
} else {
388+
true
389+
}
390+
}
391+
378392
#[inline(always)]
379393
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
380394
matches!(ecx.machine.check_alignment, CheckAlignment::Error)

compiler/rustc_const_eval/src/interpret/eval_context.rs

+13-11
Original file line numberDiff line numberDiff line change
@@ -792,15 +792,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
792792
self.stack_mut().push(frame);
793793

794794
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
795-
if M::POST_MONO_CHECKS {
796-
for &const_ in &body.required_consts {
797-
let c = self
798-
.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
799-
c.eval(*self.tcx, self.param_env, Some(const_.span)).map_err(|err| {
800-
err.emit_note(*self.tcx);
801-
err
802-
})?;
803-
}
795+
for &const_ in &body.required_consts {
796+
let c =
797+
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
798+
c.eval(*self.tcx, self.param_env, Some(const_.span)).map_err(|err| {
799+
err.emit_note(*self.tcx);
800+
err
801+
})?;
804802
}
805803

806804
// done
@@ -1149,8 +1147,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
11491147
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
11501148
M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
11511149
let const_val = val.eval(*ecx.tcx, ecx.param_env, span).map_err(|err| {
1152-
// FIXME: somehow this is reachable even when POST_MONO_CHECKS is on.
1153-
// Are we not always populating `required_consts`?
1150+
if M::all_required_consts_are_checked(self)
1151+
&& !matches!(err, ErrorHandled::TooGeneric(..))
1152+
{
1153+
// Looks like the const is not captued by `required_consts`, that's bad.
1154+
bug!("interpret const eval failure of {val:?} which is not in required_consts");
1155+
}
11541156
err.emit_note(*ecx.tcx);
11551157
err
11561158
})?;

compiler/rustc_const_eval/src/interpret/machine.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
140140
/// Should the machine panic on allocation failures?
141141
const PANIC_ON_ALLOC_FAIL: bool;
142142

143-
/// Should post-monomorphization checks be run when a stack frame is pushed?
144-
const POST_MONO_CHECKS: bool = true;
143+
/// Determines whether `eval_mir_constant` can never fail because all required consts have
144+
/// already been checked before.
145+
fn all_required_consts_are_checked(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
145146

146147
/// Whether memory accesses should be alignment-checked.
147148
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

+6
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,12 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
900900
type MemoryKind = !;
901901
const PANIC_ON_ALLOC_FAIL: bool = true;
902902

903+
#[inline(always)]
904+
fn all_required_consts_are_checked(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
905+
// We want to just eval random consts in the program, so `eval_mir_const` can fail.
906+
false
907+
}
908+
903909
#[inline(always)]
904910
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
905911
false // no reason to enforce alignment

compiler/rustc_mir_transform/src/inline.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -706,17 +706,7 @@ impl<'tcx> Inliner<'tcx> {
706706
});
707707

708708
// Copy only unevaluated constants from the callee_body into the caller_body.
709-
// Although we are only pushing `ConstKind::Unevaluated` consts to
710-
// `required_consts`, here we may not only have `ConstKind::Unevaluated`
711-
// because we are calling `instantiate_and_normalize_erasing_regions`.
712-
caller_body.required_consts.extend(callee_body.required_consts.iter().copied().filter(
713-
|&ct| match ct.const_ {
714-
Const::Ty(_) => {
715-
bug!("should never encounter ty::UnevaluatedConst in `required_consts`")
716-
}
717-
Const::Val(..) | Const::Unevaluated(..) => true,
718-
},
719-
));
709+
caller_body.required_consts.extend(callee_body.required_consts);
720710
}
721711

722712
fn make_call_args(
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_middle::mir::visit::Visitor;
22
use rustc_middle::mir::{Const, ConstOperand, Location};
3-
use rustc_middle::ty::ConstKind;
3+
use rustc_middle::ty;
44

55
pub struct RequiredConstsVisitor<'a, 'tcx> {
66
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
@@ -14,14 +14,23 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
1414

1515
impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
1616
fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
17-
let const_ = constant.const_;
18-
match const_ {
17+
// Only unevalated consts have to be added to `required_consts` as only those can possibly
18+
// still have latent const-eval errors.
19+
let is_required = match constant.const_ {
1920
Const::Ty(c) => match c.kind() {
20-
ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {}
21-
_ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
21+
ty::ConstKind::Error(_) => true, // already an error, so clearly this can error :)
22+
ty::ConstKind::Value(_) => false, // already a value, cannot error
23+
ty::ConstKind::Param(_) => false, // if this errors it should fail further up the call chain
24+
_ => bug!(
25+
"only ConstKind::Param/Value/Error should be encountered here, got {:#?}",
26+
c
27+
),
2228
},
23-
Const::Unevaluated(..) => self.required_consts.push(*constant),
24-
Const::Val(..) => {}
29+
Const::Unevaluated(..) => true,
30+
Const::Val(..) => false, // already a value, cannot error
31+
};
32+
if is_required {
33+
self.required_consts.push(*constant);
2534
}
2635
}
2736
}

src/tools/miri/src/machine.rs

+6
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,12 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
872872

873873
const PANIC_ON_ALLOC_FAIL: bool = false;
874874

875+
#[inline(always)]
876+
fn all_required_consts_are_checked(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
877+
// We want to catch any cases of missing required_consts
878+
true
879+
}
880+
875881
#[inline(always)]
876882
fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
877883
ecx.machine.check_alignment != AlignmentCheck::None

0 commit comments

Comments
 (0)