Delay ICE on evaluation fail

This commit is contained in:
Smitty 2021-06-30 15:38:31 -04:00
parent 12a8d106f6
commit 3e20129a18
2 changed files with 20 additions and 68 deletions

View File

@ -8,6 +8,7 @@ use std::ptr;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::sorted_map::SortedMap;
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, HasDataLayout, Size}; use rustc_target::abi::{Align, HasDataLayout, Size};
use super::{ use super::{
@ -15,6 +16,7 @@ use super::{
ResourceExhaustionInfo, Scalar, ScalarMaybeUninit, UndefinedBehaviorInfo, UninitBytesAccess, ResourceExhaustionInfo, Scalar, ScalarMaybeUninit, UndefinedBehaviorInfo, UninitBytesAccess,
UnsupportedOpInfo, UnsupportedOpInfo,
}; };
use crate::ty;
/// This type represents an Allocation in the Miri/CTFE core engine. /// This type represents an Allocation in the Miri/CTFE core engine.
/// ///
@ -132,6 +134,9 @@ impl<Tag> Allocation<Tag> {
// deterministic. However, we can be non-determinstic here because all uses of const // deterministic. However, we can be non-determinstic here because all uses of const
// evaluation (including ConstProp!) will make compilation fail (via hard error // evaluation (including ConstProp!) will make compilation fail (via hard error
// or ICE) upon encountering a `MemoryExhausted` error. // or ICE) upon encountering a `MemoryExhausted` error.
ty::tls::with(|tcx| {
tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpreation")
});
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
})?; })?;
bytes.resize(size.bytes_usize(), 0); bytes.resize(size.bytes_usize(), 0);

View File

@ -31,9 +31,9 @@ use rustc_trait_selection::traits;
use crate::const_eval::ConstEvalErr; use crate::const_eval::ConstEvalErr;
use crate::interpret::{ use crate::interpret::{
self, compile_time_machine, AllocId, Allocation, ConstValue, CtfeValidationMode, Frame, ImmTy, self, compile_time_machine, AllocId, Allocation, ConstValue, CtfeValidationMode, Frame, ImmTy,
Immediate, InterpCx, InterpError, InterpResult, LocalState, LocalValue, MemPlace, Memory, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy,
MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, ResourceExhaustionInfo, Scalar, Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup,
ScalarMaybeUninit, StackPopCleanup, StackPopUnwind, StackPopUnwind,
}; };
use crate::transform::MirPass; use crate::transform::MirPass;
@ -393,12 +393,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
.filter(|ret_layout| { .filter(|ret_layout| {
!ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) !ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
}) })
.and_then(|ret_layout| { .map(|ret_layout| {
let alloc = ecx.allocate(ret_layout, MemoryKind::Stack); ecx.allocate(ret_layout, MemoryKind::Stack)
Self::check_interpresult(tcx, &alloc); .expect("couldn't perform small allocation")
alloc.ok() .into()
}) });
.map(Into::into);
ecx.push_stack_frame( ecx.push_stack_frame(
Instance::new(def_id, substs), Instance::new(def_id, substs),
@ -421,27 +420,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
} }
} }
/// Some `InterpError`s could be ignored but must not be to ensure that queries are stable.
fn check_interpresult<T>(tcx: TyCtxt<'tcx>, error: &InterpResult<'tcx, T>) {
if let Err(e) = error {
if matches!(
e.kind(),
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
) {
// Memory errors can't be ignored since otherwise the amount of available
// memory influences the result of optimization and the build. The error
// doesn't need to be fatal since no code will actually be generated anyways.
tcx.sess.fatal("memory exhausted during optimization");
}
}
}
fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> { fn get_const(&self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
let op = match self.ecx.eval_place_to_op(place, None) { let op = match self.ecx.eval_place_to_op(place, None) {
Ok(op) => op, Ok(op) => op,
Err(e) => { Err(e) => {
trace!("get_const failed: {}", e); trace!("get_const failed: {}", e);
Self::check_interpresult::<()>(self.tcx, &Err(e));
return None; return None;
} }
}; };
@ -513,19 +496,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}, },
ConstantKind::Val(_, ty) => ty.needs_subst(), ConstantKind::Val(_, ty) => ty.needs_subst(),
}; };
// Memory errors can't be ignored since otherwise the amount of available if lint_only {
// memory influences the result of optimization and the build. The error
// doesn't need to be fatal since no code will actually be generated anyways.
// FIXME(#86255): use err.error.is_hard_err(), but beware of backwards
// compatibility and interactions with promoteds
if lint_only
&& !matches!(
err.error,
InterpError::ResourceExhaustion(
ResourceExhaustionInfo::MemoryExhausted,
),
)
{
// Out of backwards compatibility we cannot report hard errors in unused // Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters. // generic functions using associated constants of the generic parameters.
err.report_as_lint(tcx, "erroneous constant used", lint_root, Some(c.span)); err.report_as_lint(tcx, "erroneous constant used", lint_root, Some(c.span));
@ -543,12 +514,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
/// Returns the value, if any, of evaluating `place`. /// Returns the value, if any, of evaluating `place`.
fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> { fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
trace!("eval_place(place={:?})", place); trace!("eval_place(place={:?})", place);
let tcx = self.tcx; self.use_ecx(|this| this.ecx.eval_place_to_op(place, None))
self.use_ecx(|this| {
let val = this.ecx.eval_place_to_op(place, None);
Self::check_interpresult(tcx, &val);
val
})
} }
/// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant` /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
@ -609,17 +575,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
right: &Operand<'tcx>, right: &Operand<'tcx>,
source_info: SourceInfo, source_info: SourceInfo,
) -> Option<()> { ) -> Option<()> {
let tcx = self.tcx; let r = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?));
let r = self.use_ecx(|this| { let l = self.use_ecx(|this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?));
let val = this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?);
Self::check_interpresult(tcx, &val);
val
});
let l = self.use_ecx(|this| {
let val = this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?);
Self::check_interpresult(tcx, &val);
val
});
// Check for exceeding shifts *even if* we cannot evaluate the LHS. // Check for exceeding shifts *even if* we cannot evaluate the LHS.
if op == BinOp::Shr || op == BinOp::Shl { if op == BinOp::Shr || op == BinOp::Shl {
let r = r?; let r = r?;
@ -785,24 +742,18 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
rvalue: &Rvalue<'tcx>, rvalue: &Rvalue<'tcx>,
place: Place<'tcx>, place: Place<'tcx>,
) -> Option<()> { ) -> Option<()> {
let tcx = self.tcx;
self.use_ecx(|this| { self.use_ecx(|this| {
match rvalue { match rvalue {
Rvalue::BinaryOp(op, box (left, right)) Rvalue::BinaryOp(op, box (left, right))
| Rvalue::CheckedBinaryOp(op, box (left, right)) => { | Rvalue::CheckedBinaryOp(op, box (left, right)) => {
let l = this.ecx.eval_operand(left, None); let l = this.ecx.eval_operand(left, None);
let r = this.ecx.eval_operand(right, None); let r = this.ecx.eval_operand(right, None);
Self::check_interpresult(tcx, &l);
Self::check_interpresult(tcx, &r);
let const_arg = match (l, r) { let const_arg = match (l, r) {
(Ok(ref x), Err(_)) | (Err(_), Ok(ref x)) => this.ecx.read_immediate(x)?, (Ok(ref x), Err(_)) | (Err(_), Ok(ref x)) => this.ecx.read_immediate(x)?,
(Err(e), Err(_)) => return Err(e), (Err(e), Err(_)) => return Err(e),
(Ok(_), Ok(_)) => { (Ok(_), Ok(_)) => {
Self::check_interpresult( this.ecx.eval_rvalue_into_place(rvalue, place)?;
tcx,
&this.ecx.eval_rvalue_into_place(rvalue, place),
);
return Ok(()); return Ok(());
} }
}; };
@ -838,16 +789,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
} }
} }
_ => { _ => {
let res = this.ecx.eval_rvalue_into_place(rvalue, place); this.ecx.eval_rvalue_into_place(rvalue, place)?;
Self::check_interpresult(tcx, &res);
res?
} }
} }
} }
_ => { _ => {
let res = this.ecx.eval_rvalue_into_place(rvalue, place); this.ecx.eval_rvalue_into_place(rvalue, place)?;
Self::check_interpresult(tcx, &res);
res?
} }
} }