Skip to content

Commit db96f90

Browse files
committed
interpret: double-check that required_consts captures all consts that can fail
1 parent aa51aec commit db96f90

File tree

10 files changed

+73
-46
lines changed

10 files changed

+73
-46
lines changed

compiler/rustc_const_eval/src/interpret/eval_context.rs

+17-17
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
@@ -1141,18 +1139,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
11411139
self.raw_const_to_mplace(val)
11421140
}
11431141

1144-
pub fn eval_mir_constant(
1142+
/// Evaluate a constant from the current frame.
1143+
/// This function relies on the fact that when the frame got pushed, all `required_consts` have been checked.
1144+
pub(super) fn eval_frame_mir_constant(
11451145
&self,
1146-
val: &mir::Const<'tcx>,
1146+
const_: &mir::Const<'tcx>,
11471147
span: Option<Span>,
11481148
layout: Option<TyAndLayout<'tcx>>,
11491149
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
1150-
M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
1151-
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`?
1154-
err.emit_note(*ecx.tcx);
1155-
err
1150+
M::eval_mir_constant(self, *const_, span, layout, |ecx, val, span, layout| {
1151+
let const_val = val.eval(*ecx.tcx, ecx.param_env, span).map_err(|err| match err {
1152+
ErrorHandled::Reported(..) => {
1153+
bug!("interpret const eval failure of {val:?} which is not in required_consts")
1154+
}
1155+
ErrorHandled::TooGeneric(_) => err,
11561156
})?;
11571157
ecx.const_val_to_op(const_val, val.ty(), layout)
11581158
})

compiler/rustc_const_eval/src/interpret/machine.rs

-3
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,6 @@ 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;
145-
146143
/// Whether memory accesses should be alignment-checked.
147144
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
148145

compiler/rustc_const_eval/src/interpret/operand.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -740,14 +740,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
740740
// * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
741741
// checked yet.
742742
// * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
743-
self.eval_mir_constant(&c, Some(constant.span), layout)?
743+
self.eval_frame_mir_constant(&c, Some(constant.span), layout)?
744744
}
745745
};
746746
trace!("{:?}: {:?}", mir_op, op);
747747
Ok(op)
748748
}
749749

750-
pub(crate) fn const_val_to_op(
750+
pub fn const_val_to_op(
751751
&self,
752752
val_val: mir::ConstValue<'tcx>,
753753
ty: Ty<'tcx>,

compiler/rustc_middle/src/mir/consts.rs

+16
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,22 @@ impl<'tcx> Const<'tcx> {
230230
))
231231
}
232232

233+
#[inline]
234+
pub fn is_required_const(&self) -> bool {
235+
// Only unevalated consts must be added to `required_consts` as only those can possibly
236+
// still have latent const-eval errors.
237+
match self {
238+
Const::Ty(c) => match c.kind() {
239+
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_) => {
240+
false
241+
}
242+
_ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
243+
},
244+
Const::Unevaluated(..) => true,
245+
Const::Val(..) => false,
246+
}
247+
}
248+
233249
#[inline(always)]
234250
pub fn ty(&self) -> Ty<'tcx> {
235251
match self {

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ use rustc_data_structures::fx::FxHashMap;
99
use rustc_hir::def::DefKind;
1010
use rustc_middle::mir::interpret::{AllocId, ConstAllocation, InterpResult, Scalar};
1111
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
12-
use rustc_middle::mir::*;
12+
use rustc_middle::mir::{self, *};
1313
use rustc_middle::query::TyCtxtAt;
14-
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
14+
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf, TyAndLayout};
1515
use rustc_middle::ty::{self, Ty, TyCtxt};
1616
use rustc_mir_dataflow::value_analysis::{
1717
Map, PlaceIndex, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
1818
};
1919
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, Results, ResultsVisitor};
2020
use rustc_span::def_id::DefId;
21-
use rustc_span::DUMMY_SP;
21+
use rustc_span::{Span, DUMMY_SP};
2222
use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx, FIRST_VARIANT};
2323

2424
/// Macro for machine-specific `InterpError` without allocation.
@@ -393,7 +393,9 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
393393
}
394394
}
395395
Operand::Constant(box constant) => {
396-
if let Ok(constant) = self.ecx.eval_mir_constant(&constant.const_, None, None) {
396+
if let Ok(constant) =
397+
DummyMachine::eval_mir_constant(&self.ecx, &constant.const_, None)
398+
{
397399
self.assign_constant(state, place, constant, &[]);
398400
}
399401
}
@@ -889,6 +891,23 @@ impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
889891

890892
pub(crate) struct DummyMachine;
891893

894+
impl DummyMachine {
895+
/// Evaluate a MIR constant that doesn't have to be already pre-checked via `required_consts`.
896+
pub fn eval_mir_constant<'tcx>(
897+
ecx: &InterpCx<'_, 'tcx, Self>,
898+
const_: &mir::Const<'tcx>,
899+
span: Option<Span>,
900+
) -> InterpResult<'tcx, OpTy<'tcx>> {
901+
let const_val = const_.eval(*ecx.tcx, ecx.param_env(), span).map_err(|err| {
902+
// This *may* be the only time we're actually evaluating this const, so show a note at
903+
// the place it is used.
904+
err.emit_note(*ecx.tcx);
905+
err
906+
})?;
907+
ecx.const_val_to_op(const_val, const_.ty(), None)
908+
}
909+
}
910+
892911
impl HasStaticRootDefId for DummyMachine {
893912
fn static_def_id(&self) -> Option<rustc_hir::def_id::LocalDefId> {
894913
None

compiler/rustc_mir_transform/src/gvn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
367367
Repeat(..) => return None,
368368

369369
Constant { ref value, disambiguator: _ } => {
370-
self.ecx.eval_mir_constant(value, None, None).ok()?
370+
DummyMachine::eval_mir_constant(&self.ecx, value, None).ok()?
371371
}
372372
Aggregate(kind, variant, ref fields) => {
373373
let fields = fields

compiler/rustc_mir_transform/src/jump_threading.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
416416
match rhs {
417417
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
418418
Operand::Constant(constant) => {
419-
let constant = self.ecx.eval_mir_constant(&constant.const_, None, None).ok()?;
419+
let constant =
420+
DummyMachine::eval_mir_constant(&self.ecx, &constant.const_, None).ok()?;
420421
self.process_constant(bb, lhs, constant, state);
421422
}
422423
// Transfer the conditions on the copied rhs.

compiler/rustc_mir_transform/src/known_panics_lint.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
259259
// that the `RevealAll` pass has happened and that the body's consts
260260
// are normalized, so any call to resolve before that needs to be
261261
// manually normalized.
262-
let val = self.tcx.try_normalize_erasing_regions(self.param_env, c.const_).ok()?;
262+
let const_ = self.tcx.try_normalize_erasing_regions(self.param_env, c.const_).ok()?;
263263

264-
self.use_ecx(|this| this.ecx.eval_mir_constant(&val, Some(c.span), None))?
264+
self.use_ecx(|this| DummyMachine::eval_mir_constant(&this.ecx, &const_, Some(c.span)))?
265265
.as_mplace_or_imm()
266266
.right()
267267
}

compiler/rustc_mir_transform/src/promote_consts.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
934934
self.assign(RETURN_PLACE, rvalue, span);
935935

936936
// Now that we did promotion, we know whether we'll want to add this to `required_consts`.
937-
if self.add_to_required {
937+
if self.add_to_required && promoted_op.const_.is_required_const() {
938938
self.source.required_consts.push(promoted_op);
939939
}
940940

@@ -953,6 +953,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
953953
*local = self.promote_temp(*local);
954954
}
955955
}
956+
957+
fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) {
958+
self.promoted.required_consts.push(*constant);
959+
960+
// Skipping `super_constant` as the visitor is otherwise only looking for locals.
961+
}
956962
}
957963

958964
fn promote_candidates<'tcx>(
@@ -997,11 +1003,6 @@ fn promote_candidates<'tcx>(
9971003
None,
9981004
body.tainted_by_errors,
9991005
);
1000-
// We keep `required_consts` of the new MIR body empty. All consts mentioned here have
1001-
// already been added to the parent MIR's `required_consts` (that is computed before
1002-
// promotion), and no matter where this promoted const ends up, our parent MIR must be
1003-
// somewhere in the reachable dependency chain so we can rely on its required consts being
1004-
// evaluated.
10051006
promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial);
10061007

10071008
let promoter = Promoter {
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use rustc_middle::mir::visit::Visitor;
2-
use rustc_middle::mir::{Const, ConstOperand, Location};
3-
use rustc_middle::ty::ConstKind;
2+
use rustc_middle::mir::{ConstOperand, Location};
43

54
pub struct RequiredConstsVisitor<'a, 'tcx> {
65
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
@@ -14,14 +13,8 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
1413

1514
impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
1615
fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
17-
let const_ = constant.const_;
18-
match const_ {
19-
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),
22-
},
23-
Const::Unevaluated(..) => self.required_consts.push(*constant),
24-
Const::Val(..) => {}
16+
if constant.const_.is_required_const() {
17+
self.required_consts.push(*constant);
2518
}
2619
}
2720
}

0 commit comments

Comments
 (0)