diff options
| author | Ralf Jung <post@ralfj.de> | 2024-03-14 07:28:25 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2024-04-23 22:52:44 +0200 |
| commit | bf021ea6258bcd886fae523e8448f3697dd041fe (patch) | |
| tree | 56a2db07f08c28f7614aef7b204a4114451172ab | |
| parent | b2b617a88e477ca6511790cd58c9409e23eac1f5 (diff) | |
| download | rust-bf021ea6258bcd886fae523e8448f3697dd041fe.tar.gz rust-bf021ea6258bcd886fae523e8448f3697dd041fe.zip | |
interpret: sanity-check that required_consts captures all consts that can fail
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/dummy_machine.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/const_eval/machine.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/eval_context.rs | 24 | ||||
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/machine.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/inline.rs | 19 | ||||
| -rw-r--r-- | compiler/rustc_mir_transform/src/required_consts.rs | 22 | ||||
| -rw-r--r-- | src/tools/miri/src/machine.rs | 6 |
7 files changed, 62 insertions, 34 deletions
diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index ba2e2a1e353..de8de021b09 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -47,6 +47,12 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine { const PANIC_ON_ALLOC_FAIL: bool = true; #[inline(always)] + fn all_required_consts_are_checked(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + // We want to just eval random consts in the program, so `eval_mir_const` can fail. + false + } + + #[inline(always)] fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { false // no reason to enforce alignment } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index dd835279df3..2d06460f353 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -375,6 +375,20 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error + #[inline] + fn all_required_consts_are_checked(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + // Generally we expect required_consts to be properly filled, except for promoteds where + // storing these consts shows up negatively in benchmarks. A promoted can only be relevant + // if its parent MIR is relevant, and the consts in the promoted will be in the parent's + // `required_consts`, so we are still sure to catch any const-eval bugs, just a bit less + // directly. + if ecx.frame_idx() == 0 && ecx.frame().body.source.promoted.is_some() { + false + } else { + true + } + } + #[inline(always)] fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { matches!(ecx.machine.check_alignment, CheckAlignment::Error) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 62d169db628..8f7c2580ad4 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -822,15 +822,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.stack_mut().push(frame); // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). - if M::POST_MONO_CHECKS { - for &const_ in &body.required_consts { - let c = self - .instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; - c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| { - err.emit_note(*self.tcx); - err - })?; - } + for &const_ in &body.required_consts { + let c = + self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?; + c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| { + err.emit_note(*self.tcx); + err + })?; } // done @@ -1181,8 +1179,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| { let const_val = val.eval(*ecx.tcx, ecx.param_env, span).map_err(|err| { - // FIXME: somehow this is reachable even when POST_MONO_CHECKS is on. - // Are we not always populating `required_consts`? + if M::all_required_consts_are_checked(self) + && !matches!(err, ErrorHandled::TooGeneric(..)) + { + // Looks like the const is not captued by `required_consts`, that's bad. + bug!("interpret const eval failure of {val:?} which is not in required_consts"); + } err.emit_note(*ecx.tcx); err })?; diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 7617cb57b3c..e8ecbe089c3 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -140,8 +140,9 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { /// Should the machine panic on allocation failures? const PANIC_ON_ALLOC_FAIL: bool; - /// Should post-monomorphization checks be run when a stack frame is pushed? - const POST_MONO_CHECKS: bool = true; + /// Determines whether `eval_mir_constant` can never fail because all required consts have + /// already been checked before. + fn all_required_consts_are_checked(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; /// Whether memory accesses should be alignment-checked. fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 7dcff458ced..b21bbc98f73 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -720,18 +720,8 @@ impl<'tcx> Inliner<'tcx> { kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, }); - // Copy only unevaluated constants from the callee_body into the caller_body. - // Although we are only pushing `ConstKind::Unevaluated` consts to - // `required_consts`, here we may not only have `ConstKind::Unevaluated` - // because we are calling `instantiate_and_normalize_erasing_regions`. - caller_body.required_consts.extend(callee_body.required_consts.iter().copied().filter( - |&ct| match ct.const_ { - Const::Ty(_) => { - bug!("should never encounter ty::UnevaluatedConst in `required_consts`") - } - Const::Val(..) | Const::Unevaluated(..) => true, - }, - )); + // Copy required constants from the callee_body into the caller_body. + caller_body.required_consts.extend(callee_body.required_consts); // Now that we incorporated the callee's `required_consts`, we can remove the callee from // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does // some extra work here to save the monomorphization collector work later. It helps a lot, @@ -747,8 +737,9 @@ impl<'tcx> Inliner<'tcx> { caller_body.mentioned_items.remove(idx); caller_body.mentioned_items.extend(callee_body.mentioned_items); } else { - // If we can't find the callee, there's no point in adding its items. - // Probably it already got removed by being inlined elsewhere in the same function. + // If we can't find the callee, there's no point in adding its items. Probably it + // already got removed by being inlined elsewhere in the same function, so we already + // took its items. } } diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs index abde6a47e83..f0c63a6648c 100644 --- a/compiler/rustc_mir_transform/src/required_consts.rs +++ b/compiler/rustc_mir_transform/src/required_consts.rs @@ -1,6 +1,6 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{Const, ConstOperand, Location}; -use rustc_middle::ty::ConstKind; +use rustc_middle::ty; pub struct RequiredConstsVisitor<'a, 'tcx> { required_consts: &'a mut Vec<ConstOperand<'tcx>>, @@ -14,14 +14,22 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> { fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) { - let const_ = constant.const_; - match const_ { + // Only unevaluated consts have to be added to `required_consts` as only those can possibly + // still have latent const-eval errors. + let is_required = match constant.const_ { Const::Ty(c) => match c.kind() { - ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {} - _ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c), + ty::ConstKind::Value(_) => false, // already a value, cannot error + ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => true, // these are errors or could be replaced by errors + _ => bug!( + "only ConstKind::Param/Value/Error should be encountered here, got {:#?}", + c + ), }, - Const::Unevaluated(..) => self.required_consts.push(*constant), - Const::Val(..) => {} + Const::Unevaluated(..) => true, + Const::Val(..) => false, // already a value, cannot error + }; + if is_required { + self.required_consts.push(*constant); } } } diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index cbe70cbffee..045df6111ff 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -873,6 +873,12 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { const PANIC_ON_ALLOC_FAIL: bool = false; #[inline(always)] + fn all_required_consts_are_checked(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + // We want to catch any cases of missing required_consts + true + } + + #[inline(always)] fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool { ecx.machine.check_alignment != AlignmentCheck::None } |
