1
Fork 0

Auto merge of #68969 - RalfJung:dont-panic, r=oli-obk

remove Panic variant from InterpError

The interpreter engine itself does not raise `Panic` errors any more, so remove them from the error enum. Instead, const-prop and const-eval have to do their own handling of panics.

I used the opportunity to refactor the const-eval error handling a bit to use the `MachineStop` variant.

Also, in const-prop I could do some cleanup as now, no more lints are being reported in `use_ecx`. However, I am quite puzzled by why exactly the linting there works the way it does -- the code can certainly be cleaned up more, but I don't know enough of the intent to do that. I left some questions for the most confusing parts, but for now behavior should be unchanged by this PR (so, all that weirdness I am asking about is pre-existing and merely maintained here). Cc @wesleywiser

Fixes https://github.com/rust-lang/rust/issues/66902

r? @oli-obk
This commit is contained in:
bors 2020-02-13 12:53:43 +00:00
commit d538b80ad7
17 changed files with 271 additions and 274 deletions

View file

@ -7,11 +7,9 @@ use crate::ty::query::TyCtxtAt;
use crate::ty::{self, layout, Ty}; use crate::ty::{self, layout, Ty};
use backtrace::Backtrace; use backtrace::Backtrace;
use hir::GeneratorKind;
use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_errors::{struct_span_err, DiagnosticBuilder};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_span::symbol::Symbol;
use rustc_span::{Pos, Span}; use rustc_span::{Pos, Span};
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use std::{any::Any, env, fmt}; use std::{any::Any, env, fmt};
@ -128,9 +126,15 @@ impl<'tcx> ConstEvalErr<'tcx> {
} }
} }
/// Sets the message passed in via `message` and adds span labels before handing control back /// Create a diagnostic for this const eval error.
/// to `emit` to do any final processing. It's the caller's responsibility to call emit(), ///
/// stash(), etc. within the `emit` function to dispose of the diagnostic properly. /// Sets the message passed in via `message` and adds span labels with detailed error
/// information before handing control back to `emit` to do any final processing.
/// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
/// function to dispose of the diagnostic properly.
///
/// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
/// (Except that for some errors, we ignore all that -- see `must_error` below.)
fn struct_generic( fn struct_generic(
&self, &self,
tcx: TyCtxtAt<'tcx>, tcx: TyCtxtAt<'tcx>,
@ -139,20 +143,30 @@ impl<'tcx> ConstEvalErr<'tcx> {
lint_root: Option<hir::HirId>, lint_root: Option<hir::HirId>,
) -> Result<(), ErrorHandled> { ) -> Result<(), ErrorHandled> {
let must_error = match self.error { let must_error = match self.error {
InterpError::MachineStop(_) => bug!("CTFE does not stop"),
err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
return Err(ErrorHandled::TooGeneric); return Err(ErrorHandled::TooGeneric);
} }
err_inval!(TypeckError) => return Err(ErrorHandled::Reported), err_inval!(TypeckError) => return Err(ErrorHandled::Reported),
// We must *always* hard error on these, even if the caller wants just a lint.
err_inval!(Layout(LayoutError::SizeOverflow(_))) => true, err_inval!(Layout(LayoutError::SizeOverflow(_))) => true,
_ => false, _ => false,
}; };
trace!("reporting const eval failure at {:?}", self.span); trace!("reporting const eval failure at {:?}", self.span);
let add_span_labels = |err: &mut DiagnosticBuilder<'_>| { let err_msg = match &self.error {
if !must_error { InterpError::MachineStop(msg) => {
err.span_label(self.span, self.error.to_string()); // A custom error (`ConstEvalErrKind` in `librustc_mir/interp/const_eval/error.rs`).
// Should be turned into a string by now.
msg.downcast_ref::<String>().expect("invalid MachineStop payload").clone()
} }
err => err.to_string(),
};
let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
if let Some(span_msg) = span_msg {
err.span_label(self.span, span_msg);
}
// Add spans for the stacktrace.
// Skip the last, which is just the environment of the constant. The stacktrace // Skip the last, which is just the environment of the constant. The stacktrace
// is sometimes empty because we create "fake" eval contexts in CTFE to do work // is sometimes empty because we create "fake" eval contexts in CTFE to do work
// on constant values. // on constant values.
@ -161,35 +175,37 @@ impl<'tcx> ConstEvalErr<'tcx> {
err.span_label(frame_info.call_site, frame_info.to_string()); err.span_label(frame_info.call_site, frame_info.to_string());
} }
} }
// Let the caller finish the job.
emit(err)
}; };
if let (Some(lint_root), false) = (lint_root, must_error) { if must_error {
let hir_id = self // The `message` makes little sense here, this is a more serious error than the
.stacktrace // caller thinks anyway.
.iter() // See <https://github.com/rust-lang/rust/pull/63152>.
.rev() finish(struct_error(tcx, &err_msg), None);
.filter_map(|frame| frame.lint_root)
.next()
.unwrap_or(lint_root);
tcx.struct_span_lint_hir(
rustc_session::lint::builtin::CONST_ERR,
hir_id,
tcx.span,
|lint| {
let mut err = lint.build(message);
add_span_labels(&mut err);
emit(err);
},
);
} else { } else {
let mut err = if must_error { // Regular case.
struct_error(tcx, &self.error.to_string()) if let Some(lint_root) = lint_root {
// Report as lint.
let hir_id = self
.stacktrace
.iter()
.rev()
.filter_map(|frame| frame.lint_root)
.next()
.unwrap_or(lint_root);
tcx.struct_span_lint_hir(
rustc_session::lint::builtin::CONST_ERR,
hir_id,
tcx.span,
|lint| finish(lint.build(message), Some(err_msg)),
);
} else { } else {
struct_error(tcx, message) // Report as hard error.
}; finish(struct_error(tcx, message), Some(err_msg));
add_span_labels(&mut err); }
emit(err); }
};
Ok(()) Ok(())
} }
} }
@ -259,63 +275,6 @@ impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
} }
} }
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
pub enum PanicInfo<O> {
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
BoundsCheck { len: O, index: O },
Overflow(mir::BinOp),
OverflowNeg,
DivisionByZero,
RemainderByZero,
ResumedAfterReturn(GeneratorKind),
ResumedAfterPanic(GeneratorKind),
}
/// Type for MIR `Assert` terminator error messages.
pub type AssertMessage<'tcx> = PanicInfo<mir::Operand<'tcx>>;
impl<O> PanicInfo<O> {
/// Getting a description does not require `O` to be printable, and does not
/// require allocation.
/// The caller is expected to handle `Panic` and `BoundsCheck` separately.
pub fn description(&self) -> &'static str {
use PanicInfo::*;
match self {
Overflow(mir::BinOp::Add) => "attempt to add with overflow",
Overflow(mir::BinOp::Sub) => "attempt to subtract with overflow",
Overflow(mir::BinOp::Mul) => "attempt to multiply with overflow",
Overflow(mir::BinOp::Div) => "attempt to divide with overflow",
Overflow(mir::BinOp::Rem) => "attempt to calculate the remainder with overflow",
OverflowNeg => "attempt to negate with overflow",
Overflow(mir::BinOp::Shr) => "attempt to shift right with overflow",
Overflow(mir::BinOp::Shl) => "attempt to shift left with overflow",
Overflow(op) => bug!("{:?} cannot overflow", op),
DivisionByZero => "attempt to divide by zero",
RemainderByZero => "attempt to calculate the remainder with a divisor of zero",
ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
Panic { .. } | BoundsCheck { .. } => bug!("Unexpected PanicInfo"),
}
}
}
impl<O: fmt::Debug> fmt::Debug for PanicInfo<O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use PanicInfo::*;
match self {
Panic { ref msg, line, col, ref file } => {
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
}
BoundsCheck { ref len, ref index } => {
write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index)
}
_ => write!(f, "{}", self.description()),
}
}
}
/// Error information for when the program we executed turned out not to actually be a valid /// Error information for when the program we executed turned out not to actually be a valid
/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp /// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
/// where we work on generic code or execution does not have all information available. /// where we work on generic code or execution does not have all information available.
@ -616,8 +575,6 @@ impl fmt::Debug for ResourceExhaustionInfo {
} }
pub enum InterpError<'tcx> { pub enum InterpError<'tcx> {
/// The program panicked.
Panic(PanicInfo<u64>),
/// The program caused undefined behavior. /// The program caused undefined behavior.
UndefinedBehavior(UndefinedBehaviorInfo), UndefinedBehavior(UndefinedBehaviorInfo),
/// The program did something the interpreter does not support (some of these *might* be UB /// The program did something the interpreter does not support (some of these *might* be UB
@ -650,8 +607,7 @@ impl fmt::Debug for InterpError<'_> {
InvalidProgram(ref msg) => write!(f, "{:?}", msg), InvalidProgram(ref msg) => write!(f, "{:?}", msg),
UndefinedBehavior(ref msg) => write!(f, "{:?}", msg), UndefinedBehavior(ref msg) => write!(f, "{:?}", msg),
ResourceExhaustion(ref msg) => write!(f, "{:?}", msg), ResourceExhaustion(ref msg) => write!(f, "{:?}", msg),
Panic(ref msg) => write!(f, "{:?}", msg), MachineStop(_) => bug!("unhandled MachineStop"),
MachineStop(_) => write!(f, "machine caused execution to stop"),
} }
} }
} }

View file

@ -37,15 +37,6 @@ macro_rules! err_ub_format {
($($tt:tt)*) => { err_ub!(Ub(format!($($tt)*))) }; ($($tt:tt)*) => { err_ub!(Ub(format!($($tt)*))) };
} }
#[macro_export]
macro_rules! err_panic {
($($tt:tt)*) => {
$crate::mir::interpret::InterpError::Panic(
$crate::mir::interpret::PanicInfo::$($tt)*
)
};
}
#[macro_export] #[macro_export]
macro_rules! err_exhaust { macro_rules! err_exhaust {
($($tt:tt)*) => { ($($tt:tt)*) => {
@ -80,11 +71,6 @@ macro_rules! throw_ub_format {
($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) }; ($($tt:tt)*) => { throw_ub!(Ub(format!($($tt)*))) };
} }
#[macro_export]
macro_rules! throw_panic {
($($tt:tt)*) => { return Err(err_panic!($($tt)*).into()) };
}
#[macro_export] #[macro_export]
macro_rules! throw_exhaust { macro_rules! throw_exhaust {
($($tt:tt)*) => { return Err(err_exhaust!($($tt)*).into()) }; ($($tt:tt)*) => { return Err(err_exhaust!($($tt)*).into()) };
@ -104,9 +90,9 @@ mod queries;
mod value; mod value;
pub use self::error::{ pub use self::error::{
struct_error, AssertMessage, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled, struct_error, ConstEvalErr, ConstEvalRawResult, ConstEvalResult, ErrorHandled, FrameInfo,
FrameInfo, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, PanicInfo, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo, ResourceExhaustionInfo,
ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
}; };
pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUndef}; pub use self::value::{get_slice_bytes, ConstValue, RawConst, Scalar, ScalarMaybeUndef};

View file

@ -2,7 +2,7 @@
//! //!
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/mir/index.html //! [rustc guide]: https://rust-lang.github.io/rustc-guide/mir/index.html
use crate::mir::interpret::{GlobalAlloc, PanicInfo, Scalar}; use crate::mir::interpret::{GlobalAlloc, Scalar};
use crate::mir::visit::MirVisitable; use crate::mir::visit::MirVisitable;
use crate::ty::adjustment::PointerCast; use crate::ty::adjustment::PointerCast;
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
@ -36,7 +36,6 @@ pub use syntax::ast::Mutability;
use syntax::ast::Name; use syntax::ast::Name;
pub use self::cache::{BodyAndCache, ReadOnlyBodyAndCache}; pub use self::cache::{BodyAndCache, ReadOnlyBodyAndCache};
pub use self::interpret::AssertMessage;
pub use self::query::*; pub use self::query::*;
pub use crate::read_only; pub use crate::read_only;
@ -1154,6 +1153,21 @@ pub enum TerminatorKind<'tcx> {
}, },
} }
/// Information about an assertion failure.
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
pub enum AssertKind<O> {
BoundsCheck { len: O, index: O },
Overflow(BinOp),
OverflowNeg,
DivisionByZero,
RemainderByZero,
ResumedAfterReturn(GeneratorKind),
ResumedAfterPanic(GeneratorKind),
}
/// Type for MIR `Assert` terminator error messages.
pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
pub type Successors<'a> = pub type Successors<'a> =
iter::Chain<option::IntoIter<&'a BasicBlock>, slice::Iter<'a, BasicBlock>>; iter::Chain<option::IntoIter<&'a BasicBlock>, slice::Iter<'a, BasicBlock>>;
pub type SuccessorsMut<'a> = pub type SuccessorsMut<'a> =
@ -1383,6 +1397,45 @@ impl<'tcx> BasicBlockData<'tcx> {
} }
} }
impl<O> AssertKind<O> {
/// Getting a description does not require `O` to be printable, and does not
/// require allocation.
/// The caller is expected to handle `BoundsCheck` separately.
pub fn description(&self) -> &'static str {
use AssertKind::*;
match self {
Overflow(BinOp::Add) => "attempt to add with overflow",
Overflow(BinOp::Sub) => "attempt to subtract with overflow",
Overflow(BinOp::Mul) => "attempt to multiply with overflow",
Overflow(BinOp::Div) => "attempt to divide with overflow",
Overflow(BinOp::Rem) => "attempt to calculate the remainder with overflow",
OverflowNeg => "attempt to negate with overflow",
Overflow(BinOp::Shr) => "attempt to shift right with overflow",
Overflow(BinOp::Shl) => "attempt to shift left with overflow",
Overflow(op) => bug!("{:?} cannot overflow", op),
DivisionByZero => "attempt to divide by zero",
RemainderByZero => "attempt to calculate the remainder with a divisor of zero",
ResumedAfterReturn(GeneratorKind::Gen) => "generator resumed after completion",
ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
BoundsCheck { .. } => bug!("Unexpected AssertKind"),
}
}
}
impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use AssertKind::*;
match self {
BoundsCheck { ref len, ref index } => {
write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index)
}
_ => write!(f, "{}", self.description()),
}
}
}
impl<'tcx> Debug for TerminatorKind<'tcx> { impl<'tcx> Debug for TerminatorKind<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
self.fmt_head(fmt)?; self.fmt_head(fmt)?;
@ -2666,13 +2719,12 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
} }
} }
Assert { ref cond, expected, ref msg, target, cleanup } => { Assert { ref cond, expected, ref msg, target, cleanup } => {
use PanicInfo::*; use AssertKind::*;
let msg = match msg { let msg = match msg {
BoundsCheck { ref len, ref index } => { BoundsCheck { ref len, ref index } => {
BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) } BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder) }
} }
Panic { .. } Overflow(_)
| Overflow(_)
| OverflowNeg | OverflowNeg
| DivisionByZero | DivisionByZero
| RemainderByZero | RemainderByZero
@ -2716,13 +2768,12 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
} }
Assert { ref cond, ref msg, .. } => { Assert { ref cond, ref msg, .. } => {
if cond.visit_with(visitor) { if cond.visit_with(visitor) {
use PanicInfo::*; use AssertKind::*;
match msg { match msg {
BoundsCheck { ref len, ref index } => { BoundsCheck { ref len, ref index } => {
len.visit_with(visitor) || index.visit_with(visitor) len.visit_with(visitor) || index.visit_with(visitor)
} }
Panic { .. } Overflow(_)
| Overflow(_)
| OverflowNeg | OverflowNeg
| DivisionByZero | DivisionByZero
| RemainderByZero | RemainderByZero

View file

@ -533,13 +533,13 @@ macro_rules! make_mir_visitor {
fn super_assert_message(&mut self, fn super_assert_message(&mut self,
msg: & $($mutability)? AssertMessage<'tcx>, msg: & $($mutability)? AssertMessage<'tcx>,
location: Location) { location: Location) {
use crate::mir::interpret::PanicInfo::*; use crate::mir::AssertKind::*;
match msg { match msg {
BoundsCheck { len, index } => { BoundsCheck { len, index } => {
self.visit_operand(len, location); self.visit_operand(len, location);
self.visit_operand(index, location); self.visit_operand(index, location);
} }
Panic { .. } | Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero | Overflow(_) | OverflowNeg | DivisionByZero | RemainderByZero |
ResumedAfterReturn(_) | ResumedAfterPanic(_) => { ResumedAfterReturn(_) | ResumedAfterPanic(_) => {
// Nothing to visit // Nothing to visit
} }

View file

@ -11,7 +11,7 @@ use crate::MemFlags;
use rustc::middle::lang_items; use rustc::middle::lang_items;
use rustc::mir; use rustc::mir;
use rustc::mir::interpret::PanicInfo; use rustc::mir::AssertKind;
use rustc::ty::layout::{self, FnAbiExt, HasTyCtxt, LayoutOf}; use rustc::ty::layout::{self, FnAbiExt, HasTyCtxt, LayoutOf};
use rustc::ty::{self, Instance, Ty, TypeFoldable}; use rustc::ty::{self, Instance, Ty, TypeFoldable};
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
@ -378,7 +378,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// checked operation, just a comparison with the minimum // checked operation, just a comparison with the minimum
// value, so we have to check for the assert message. // value, so we have to check for the assert message.
if !bx.check_overflow() { if !bx.check_overflow() {
if let PanicInfo::OverflowNeg = *msg { if let AssertKind::OverflowNeg = *msg {
const_cond = Some(expected); const_cond = Some(expected);
} }
} }
@ -412,7 +412,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// Put together the arguments to the panic entry point. // Put together the arguments to the panic entry point.
let (lang_item, args) = match msg { let (lang_item, args) = match msg {
PanicInfo::BoundsCheck { ref len, ref index } => { AssertKind::BoundsCheck { ref len, ref index } => {
let len = self.codegen_operand(&mut bx, len).immediate(); let len = self.codegen_operand(&mut bx, len).immediate();
let index = self.codegen_operand(&mut bx, index).immediate(); let index = self.codegen_operand(&mut bx, index).immediate();
(lang_items::PanicBoundsCheckFnLangItem, vec![location, index, len]) (lang_items::PanicBoundsCheckFnLangItem, vec![location, index, len])

View file

@ -153,8 +153,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
} }
TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
self.consume_operand(location, cond); self.consume_operand(location, cond);
use rustc::mir::interpret::PanicInfo; use rustc::mir::AssertKind;
if let PanicInfo::BoundsCheck { ref len, ref index } = *msg { if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
self.consume_operand(location, len); self.consume_operand(location, len);
self.consume_operand(location, index); self.consume_operand(location, index);
} }

View file

@ -654,8 +654,8 @@ impl<'cx, 'tcx> dataflow::generic::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt
} }
TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => { TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
self.consume_operand(loc, (cond, span), flow_state); self.consume_operand(loc, (cond, span), flow_state);
use rustc::mir::interpret::PanicInfo; use rustc::mir::AssertKind;
if let PanicInfo::BoundsCheck { ref len, ref index } = *msg { if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
self.consume_operand(loc, (len, span), flow_state); self.consume_operand(loc, (len, span), flow_state);
self.consume_operand(loc, (index, span), flow_state); self.consume_operand(loc, (index, span), flow_state);
} }

View file

@ -9,9 +9,9 @@ use rustc::infer::canonical::QueryRegionConstraints;
use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::outlives::env::RegionBoundPairs;
use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin}; use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin};
use rustc::mir::interpret::PanicInfo;
use rustc::mir::tcx::PlaceTy; use rustc::mir::tcx::PlaceTy;
use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc::mir::AssertKind;
use rustc::mir::*; use rustc::mir::*;
use rustc::traits::query::type_op; use rustc::traits::query::type_op;
use rustc::traits::query::type_op::custom::CustomTypeOp; use rustc::traits::query::type_op::custom::CustomTypeOp;
@ -1562,7 +1562,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty);
} }
if let PanicInfo::BoundsCheck { ref len, ref index } = *msg { if let AssertKind::BoundsCheck { ref len, ref index } = *msg {
if len.ty(body, tcx) != tcx.types.usize { if len.ty(body, tcx) != tcx.types.usize {
span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) span_mirbug!(self, len, "bounds-check length non-usize {:?}", len)
} }

View file

@ -1,33 +1,47 @@
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use rustc::mir::AssertKind;
use rustc_span::Symbol;
use super::InterpCx; use super::InterpCx;
use crate::interpret::{ConstEvalErr, InterpErrorInfo, Machine}; use crate::interpret::{ConstEvalErr, InterpError, InterpErrorInfo, Machine};
/// The CTFE machine has some custom error kinds.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ConstEvalError { pub enum ConstEvalErrKind {
NeedsRfc(String), NeedsRfc(String),
ConstAccessesStatic, ConstAccessesStatic,
AssertFailure(AssertKind<u64>),
Panic { msg: Symbol, line: u32, col: u32, file: Symbol },
} }
impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalError { // The errors become `MachineStop` with plain strings when being raised.
// `ConstEvalErr` (in `librustc/mir/interpret/error.rs`) knows to
// handle these.
impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
fn into(self) -> InterpErrorInfo<'tcx> { fn into(self) -> InterpErrorInfo<'tcx> {
err_unsup!(Unsupported(self.to_string())).into() InterpError::MachineStop(Box::new(self.to_string())).into()
} }
} }
impl fmt::Display for ConstEvalError { impl fmt::Display for ConstEvalErrKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::ConstEvalError::*; use self::ConstEvalErrKind::*;
match *self { match *self {
NeedsRfc(ref msg) => { NeedsRfc(ref msg) => {
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg) write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
} }
ConstAccessesStatic => write!(f, "constant accesses static"), ConstAccessesStatic => write!(f, "constant accesses static"),
AssertFailure(ref msg) => write!(f, "{:?}", msg),
Panic { msg, line, col, file } => {
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
}
} }
} }
} }
impl Error for ConstEvalError {} impl Error for ConstEvalErrKind {}
/// Turn an interpreter error into something to report to the user. /// Turn an interpreter error into something to report to the user.
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace. /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.

View file

@ -8,11 +8,13 @@ use std::hash::Hash;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc::mir::AssertMessage;
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
use crate::interpret::{ use crate::interpret::{
self, snapshot, AllocId, Allocation, AssertMessage, GlobalId, ImmTy, InterpCx, InterpResult, self, snapshot, AllocId, Allocation, GlobalId, ImmTy, InterpCx, InterpResult, Memory,
Memory, MemoryKind, OpTy, PlaceTy, Pointer, Scalar, MemoryKind, OpTy, PlaceTy, Pointer, Scalar,
}; };
use super::error::*; use super::error::*;
@ -56,6 +58,32 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
self.dump_place(*dest); self.dump_place(*dest);
return Ok(true); return Ok(true);
} }
/// "Intercept" a function call to a panic-related function
/// because we have something special to do for it.
/// If this returns successfully (`Ok`), the function should just be evaluated normally.
fn hook_panic_fn(
&mut self,
span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx>],
) -> InterpResult<'tcx> {
let def_id = instance.def_id();
if Some(def_id) == self.tcx.lang_items().panic_fn()
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
{
// &'static str
assert!(args.len() == 1);
let msg_place = self.deref_operand(args[0])?;
let msg = Symbol::intern(self.read_str(msg_place)?);
let span = self.find_closest_untracked_caller_location().unwrap_or(span);
let (file, line, col) = self.location_triple_for_span(span);
Err(ConstEvalErrKind::Panic { msg, file, line, col }.into())
} else {
Ok(())
}
}
} }
/// Number of steps until the detector even starts doing anything. /// Number of steps until the detector even starts doing anything.
@ -198,13 +226,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
} }
} else { } else {
// Some functions we support even if they are non-const -- but avoid testing // Some functions we support even if they are non-const -- but avoid testing
// that for const fn! We certainly do *not* want to actually call the fn // that for const fn!
ecx.hook_panic_fn(span, instance, args)?;
// We certainly do *not* want to actually call the fn
// though, so be sure we return here. // though, so be sure we return here.
return if ecx.hook_panic_fn(span, instance, args)? { throw_unsup_format!("calling non-const function `{}`", instance)
Ok(None)
} else {
throw_unsup_format!("calling non-const function `{}`", instance)
};
} }
} }
// This is a const fn. Call it. // This is a const fn. Call it.
@ -212,7 +238,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
Ok(body) => *body, Ok(body) => *body,
Err(err) => { Err(err) => {
if let err_unsup!(NoMirFor(ref path)) = err.kind { if let err_unsup!(NoMirFor(ref path)) = err.kind {
return Err(ConstEvalError::NeedsRfc(format!( return Err(ConstEvalErrKind::NeedsRfc(format!(
"calling extern function `{}`", "calling extern function `{}`",
path path
)) ))
@ -246,7 +272,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
} }
// An intrinsic that we do not support // An intrinsic that we do not support
let intrinsic_name = ecx.tcx.item_name(instance.def_id()); let intrinsic_name = ecx.tcx.item_name(instance.def_id());
Err(ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into()) Err(ConstEvalErrKind::NeedsRfc(format!("calling intrinsic `{}`", intrinsic_name)).into())
} }
fn assert_panic( fn assert_panic(
@ -255,8 +281,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
msg: &AssertMessage<'tcx>, msg: &AssertMessage<'tcx>,
_unwind: Option<mir::BasicBlock>, _unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
use rustc::mir::interpret::PanicInfo::*; use rustc::mir::AssertKind::*;
Err(match msg { // Convert `AssertKind<Operand>` to `AssertKind<u64>`.
let err = match msg {
BoundsCheck { ref len, ref index } => { BoundsCheck { ref len, ref index } => {
let len = ecx let len = ecx
.read_immediate(ecx.eval_operand(len, None)?) .read_immediate(ecx.eval_operand(len, None)?)
@ -268,21 +295,20 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
.expect("can't eval index") .expect("can't eval index")
.to_scalar()? .to_scalar()?
.to_machine_usize(&*ecx)?; .to_machine_usize(&*ecx)?;
err_panic!(BoundsCheck { len, index }) BoundsCheck { len, index }
} }
Overflow(op) => err_panic!(Overflow(*op)), Overflow(op) => Overflow(*op),
OverflowNeg => err_panic!(OverflowNeg), OverflowNeg => OverflowNeg,
DivisionByZero => err_panic!(DivisionByZero), DivisionByZero => DivisionByZero,
RemainderByZero => err_panic!(RemainderByZero), RemainderByZero => RemainderByZero,
ResumedAfterReturn(generator_kind) => err_panic!(ResumedAfterReturn(*generator_kind)), ResumedAfterReturn(generator_kind) => ResumedAfterReturn(*generator_kind),
ResumedAfterPanic(generator_kind) => err_panic!(ResumedAfterPanic(*generator_kind)), ResumedAfterPanic(generator_kind) => ResumedAfterPanic(*generator_kind),
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"), };
} Err(ConstEvalErrKind::AssertFailure(err).into())
.into())
} }
fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> { fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> {
Err(ConstEvalError::NeedsRfc("pointer-to-integer cast".to_string()).into()) Err(ConstEvalErrKind::NeedsRfc("pointer-to-integer cast".to_string()).into())
} }
fn binary_ptr_op( fn binary_ptr_op(
@ -291,7 +317,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
_left: ImmTy<'tcx>, _left: ImmTy<'tcx>,
_right: ImmTy<'tcx>, _right: ImmTy<'tcx>,
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
Err(ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into()) Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into())
} }
fn find_foreign_static( fn find_foreign_static(
@ -321,7 +347,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ecx: &mut InterpCx<'mir, 'tcx, Self>,
_dest: PlaceTy<'tcx>, _dest: PlaceTy<'tcx>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
Err(ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into()) Err(ConstEvalErrKind::NeedsRfc("heap allocations via `box` keyword".to_string()).into())
} }
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
@ -355,7 +381,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
if memory_extra.can_access_statics { if memory_extra.can_access_statics {
Ok(()) Ok(())
} else { } else {
Err(ConstEvalError::ConstAccessesStatic.into()) Err(ConstEvalErrKind::ConstAccessesStatic.into())
} }
} }
} }

View file

@ -376,32 +376,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(true) Ok(true)
} }
/// "Intercept" a function call to a panic-related function
/// because we have something special to do for it.
/// Returns `true` if an intercept happened.
pub fn hook_panic_fn(
&mut self,
span: Span,
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, M::PointerTag>],
) -> InterpResult<'tcx, bool> {
let def_id = instance.def_id();
if Some(def_id) == self.tcx.lang_items().panic_fn()
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
{
// &'static str
assert!(args.len() == 1);
let msg_place = self.deref_operand(args[0])?;
let msg = Symbol::intern(self.read_str(msg_place)?);
let span = self.find_closest_untracked_caller_location().unwrap_or(span);
let (file, line, col) = self.location_triple_for_span(span);
throw_panic!(Panic { msg, file, line, col })
} else {
return Ok(false);
}
}
pub fn exact_div( pub fn exact_div(
&mut self, &mut self,
a: ImmTy<'tcx, M::PointerTag>, a: ImmTy<'tcx, M::PointerTag>,

View file

@ -54,12 +54,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
location location
} }
pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::PointerTag> { crate fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
let (file, line, column) = self.location_triple_for_span(span);
self.alloc_caller_location(file, line, column)
}
pub(super) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
( (
@ -68,4 +63,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
caller.col_display as u32 + 1, caller.col_display as u32 + 1,
) )
} }
pub fn alloc_caller_location_for_span(&mut self, span: Span) -> MPlaceTy<'tcx, M::PointerTag> {
let (file, line, column) = self.location_triple_for_span(span);
self.alloc_caller_location(file, line, column)
}
} }

View file

@ -11,8 +11,8 @@ use rustc_hir::def_id::DefId;
use rustc_span::Span; use rustc_span::Span;
use super::{ use super::{
AllocId, Allocation, AllocationExtra, AssertMessage, Frame, ImmTy, InterpCx, InterpResult, AllocId, Allocation, AllocationExtra, Frame, ImmTy, InterpCx, InterpResult, Memory, MemoryKind,
Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Scalar, OpTy, Operand, PlaceTy, Pointer, Scalar,
}; };
/// Data returned by Machine::stack_pop, /// Data returned by Machine::stack_pop,
@ -171,7 +171,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
fn assert_panic( fn assert_panic(
ecx: &mut InterpCx<'mir, 'tcx, Self>, ecx: &mut InterpCx<'mir, 'tcx, Self>,
span: Span, span: Span,
msg: &AssertMessage<'tcx>, msg: &mir::AssertMessage<'tcx>,
unwind: Option<mir::BasicBlock>, unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx>; ) -> InterpResult<'tcx>;

View file

@ -4,13 +4,13 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::Cell; use std::cell::Cell;
use rustc::mir::interpret::{InterpResult, PanicInfo, Scalar}; use rustc::mir::interpret::{InterpError, InterpResult, Scalar};
use rustc::mir::visit::{ use rustc::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
}; };
use rustc::mir::{ use rustc::mir::{
read_only, AggregateKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate, Constant, read_only, AggregateKind, AssertKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate,
Local, LocalDecl, LocalKind, Location, Operand, Place, ReadOnlyBodyAndCache, Rvalue, Constant, Local, LocalDecl, LocalKind, Location, Operand, Place, ReadOnlyBodyAndCache, Rvalue,
SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
UnOp, RETURN_PLACE, UnOp, RETURN_PLACE,
}; };
@ -25,7 +25,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::HirId; use rustc_hir::HirId;
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_span::{Span, DUMMY_SP}; use rustc_span::Span;
use syntax::ast::Mutability; use syntax::ast::Mutability;
use crate::const_eval::error_to_const_error; use crate::const_eval::error_to_const_error;
@ -198,7 +198,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
fn assert_panic( fn assert_panic(
_ecx: &mut InterpCx<'mir, 'tcx, Self>, _ecx: &mut InterpCx<'mir, 'tcx, Self>,
_span: Span, _span: Span,
_msg: &rustc::mir::interpret::AssertMessage<'tcx>, _msg: &rustc::mir::AssertMessage<'tcx>,
_unwind: Option<rustc::mir::BasicBlock>, _unwind: Option<rustc::mir::BasicBlock>,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
bug!("panics terminators are not evaluated in ConstProp"); bug!("panics terminators are not evaluated in ConstProp");
@ -410,15 +410,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
} }
} }
fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T> fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
where where
F: FnOnce(&mut Self) -> InterpResult<'tcx, T>, F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
{ {
self.ecx.tcx.span = source_info.span;
// FIXME(eddyb) move this to the `Panic(_)` error case, so that
// `f(self)` is always called, and that the only difference when the
// scope's `local_data` is missing, is that the lint isn't emitted.
let lint_root = self.lint_root(source_info)?;
let r = match f(self) { let r = match f(self) {
Ok(val) => Some(val), Ok(val) => Some(val),
Err(error) => { Err(error) => {
@ -447,20 +442,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
| ResourceExhaustion(_) => { | ResourceExhaustion(_) => {
// Ignore these errors. // Ignore these errors.
} }
Panic(_) => {
let diagnostic = error_to_const_error(&self.ecx, error);
diagnostic.report_as_lint(
self.ecx.tcx,
"this expression will panic at runtime",
lint_root,
None,
);
}
} }
None None
} }
}; };
self.ecx.tcx.span = DUMMY_SP;
r r
} }
@ -504,37 +489,49 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
} }
} }
fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> { fn eval_place(&mut self, place: &Place<'tcx>) -> Option<OpTy<'tcx>> {
trace!("eval_place(place={:?})", place); trace!("eval_place(place={:?})", place);
self.use_ecx(source_info, |this| this.ecx.eval_place_to_op(place, None)) self.use_ecx(|this| this.ecx.eval_place_to_op(place, None))
} }
fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> { fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
match *op { match *op {
Operand::Constant(ref c) => self.eval_constant(c, source_info), Operand::Constant(ref c) => self.eval_constant(c, source_info),
Operand::Move(ref place) | Operand::Copy(ref place) => { Operand::Move(ref place) | Operand::Copy(ref place) => self.eval_place(place),
self.eval_place(place, source_info)
}
} }
} }
fn report_panic_as_lint(&self, source_info: SourceInfo, panic: AssertKind<u64>) -> Option<()> {
// Somewhat convoluted way to re-use the CTFE error reporting code.
let lint_root = self.lint_root(source_info)?;
let error = InterpError::MachineStop(Box::new(format!("{:?}", panic)));
let mut diagnostic = error_to_const_error(&self.ecx, error.into());
diagnostic.span = source_info.span; // fix the span
diagnostic.report_as_lint(
self.tcx.at(source_info.span),
"this expression will panic at runtime",
lint_root,
None,
);
None
}
fn check_unary_op( fn check_unary_op(
&mut self, &mut self,
op: UnOp, op: UnOp,
arg: &Operand<'tcx>, arg: &Operand<'tcx>,
source_info: SourceInfo, source_info: SourceInfo,
) -> Option<()> { ) -> Option<()> {
self.use_ecx(source_info, |this| { if self.use_ecx(|this| {
let val = this.ecx.read_immediate(this.ecx.eval_operand(arg, None)?)?; let val = this.ecx.read_immediate(this.ecx.eval_operand(arg, None)?)?;
let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, val)?; let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, val)?;
Ok(overflow)
if overflow { })? {
assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); // `AssertKind` only has an `OverflowNeg` variant, so make sure that is
throw_panic!(OverflowNeg); // appropriate to use.
} assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow");
self.report_panic_as_lint(source_info, AssertKind::OverflowNeg)?;
Ok(()) }
})?;
Some(()) Some(())
} }
@ -546,11 +543,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
right: &Operand<'tcx>, right: &Operand<'tcx>,
source_info: SourceInfo, source_info: SourceInfo,
place_layout: TyLayout<'tcx>, place_layout: TyLayout<'tcx>,
overflow_check: bool,
) -> Option<()> { ) -> Option<()> {
let r = self.use_ecx(source_info, |this| { let r =
this.ecx.read_immediate(this.ecx.eval_operand(right, None)?) self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?))?;
})?; // 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 left_bits = place_layout.size.bits(); let left_bits = place_layout.size.bits();
let right_size = r.layout.size; let right_size = r.layout.size;
@ -570,21 +566,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
} }
} }
// If overflow checking is enabled (like in debug mode by default), // The remaining operators are handled through `overflowing_binary_op`.
// then we'll already catch overflow when we evaluate the `Assert` statement if self.use_ecx(|this| {
// in MIR. However, if overflow checking is disabled, then there won't be any let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
// `Assert` statement and so we have to do additional checking here. let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
if !overflow_check { Ok(overflow)
self.use_ecx(source_info, |this| { })? {
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; self.report_panic_as_lint(source_info, AssertKind::Overflow(op))?;
let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
if overflow {
throw_panic!(Overflow(op));
}
Ok(())
})?;
} }
Some(()) Some(())
@ -617,9 +605,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// 2. Working around bugs in other parts of the compiler // 2. Working around bugs in other parts of the compiler
// - In this case, we'll return `None` from this function to stop evaluation. // - In this case, we'll return `None` from this function to stop evaluation.
match rvalue { match rvalue {
// Additional checking: if overflow checks are disabled (which is usually the case in // Additional checking: give lints to the user if an overflow would occur.
// release mode), then we need to do additional checking here to give lints to the user // If `overflow_check` is set, running const-prop on the `Assert` terminators
// if an overflow would occur. // will already generate the appropriate messages.
Rvalue::UnaryOp(op, arg) if !overflow_check => { Rvalue::UnaryOp(op, arg) if !overflow_check => {
trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg);
self.check_unary_op(*op, arg, source_info)?; self.check_unary_op(*op, arg, source_info)?;
@ -627,9 +615,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// Additional checking: check for overflows on integer binary operations and report // Additional checking: check for overflows on integer binary operations and report
// them to the user as lints. // them to the user as lints.
Rvalue::BinaryOp(op, left, right) => { // If `overflow_check` is set, running const-prop on the `Assert` terminators
// will already generate the appropriate messages.
Rvalue::BinaryOp(op, left, right) if !overflow_check => {
trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right); trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
self.check_binary_op(*op, left, right, source_info, place_layout, overflow_check)?; self.check_binary_op(*op, left, right, source_info, place_layout)?;
} }
// Do not try creating references (#67862) // Do not try creating references (#67862)
@ -642,7 +632,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
_ => {} _ => {}
} }
self.use_ecx(source_info, |this| { self.use_ecx(|this| {
trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place); trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
this.ecx.eval_rvalue_into_place(rvalue, place)?; this.ecx.eval_rvalue_into_place(rvalue, place)?;
Ok(()) Ok(())
@ -675,7 +665,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
} }
// FIXME> figure out what tho do when try_read_immediate fails // FIXME> figure out what tho do when try_read_immediate fails
let imm = self.use_ecx(source_info, |this| this.ecx.try_read_immediate(value)); let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value));
if let Some(Ok(imm)) = imm { if let Some(Ok(imm)) = imm {
match *imm { match *imm {
@ -698,7 +688,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
if let ty::Tuple(substs) = ty { if let ty::Tuple(substs) = ty {
// Only do it if tuple is also a pair with two scalars // Only do it if tuple is also a pair with two scalars
if substs.len() == 2 { if substs.len() == 2 {
let opt_ty1_ty2 = self.use_ecx(source_info, |this| { let opt_ty1_ty2 = self.use_ecx(|this| {
let ty1 = substs[0].expect_ty(); let ty1 = substs[0].expect_ty();
let ty2 = substs[1].expect_ty(); let ty2 = substs[1].expect_ty();
let ty_is_scalar = |ty| { let ty_is_scalar = |ty| {
@ -920,11 +910,11 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
span, span,
|lint| { |lint| {
let msg = match msg { let msg = match msg {
PanicInfo::Overflow(_) AssertKind::Overflow(_)
| PanicInfo::OverflowNeg | AssertKind::OverflowNeg
| PanicInfo::DivisionByZero | AssertKind::DivisionByZero
| PanicInfo::RemainderByZero => msg.description().to_owned(), | AssertKind::RemainderByZero => msg.description().to_owned(),
PanicInfo::BoundsCheck { ref len, ref index } => { AssertKind::BoundsCheck { ref len, ref index } => {
let len = self let len = self
.eval_operand(len, source_info) .eval_operand(len, source_info)
.expect("len must be const"); .expect("len must be const");

View file

@ -1022,7 +1022,7 @@ fn create_generator_resume_function<'tcx>(
let mut cases = create_cases(body, &transform, Operation::Resume); let mut cases = create_cases(body, &transform, Operation::Resume);
use rustc::mir::interpret::PanicInfo::{ResumedAfterPanic, ResumedAfterReturn}; use rustc::mir::AssertKind::{ResumedAfterPanic, ResumedAfterReturn};
// Jump to the entry point on the unresumed // Jump to the entry point on the unresumed
cases.insert(0, (UNRESUMED, BasicBlock::new(0))); cases.insert(0, (UNRESUMED, BasicBlock::new(0)));

View file

@ -5,7 +5,7 @@ use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::hair::*; use crate::hair::*;
use rustc::middle::region; use rustc::middle::region;
use rustc::mir::interpret::PanicInfo::BoundsCheck; use rustc::mir::AssertKind::BoundsCheck;
use rustc::mir::*; use rustc::mir::*;
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance}; use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
use rustc_span::Span; use rustc_span::Span;

View file

@ -6,7 +6,7 @@ use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::hair::*; use crate::hair::*;
use rustc::middle::region; use rustc::middle::region;
use rustc::mir::interpret::PanicInfo; use rustc::mir::AssertKind;
use rustc::mir::*; use rustc::mir::*;
use rustc::ty::{self, Ty, UpvarSubsts}; use rustc::ty::{self, Ty, UpvarSubsts};
use rustc_span::Span; use rustc_span::Span;
@ -86,7 +86,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block, block,
Operand::Move(is_min), Operand::Move(is_min),
false, false,
PanicInfo::OverflowNeg, AssertKind::OverflowNeg,
expr_span, expr_span,
); );
} }
@ -294,7 +294,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let val = tcx.mk_place_field(result_value.clone(), val_fld, ty); let val = tcx.mk_place_field(result_value.clone(), val_fld, ty);
let of = tcx.mk_place_field(result_value, of_fld, bool_ty); let of = tcx.mk_place_field(result_value, of_fld, bool_ty);
let err = PanicInfo::Overflow(op); let err = AssertKind::Overflow(op);
block = self.assert(block, Operand::Move(of), false, err, span); block = self.assert(block, Operand::Move(of), false, err, span);
@ -305,11 +305,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// and 2. there are two possible failure cases, divide-by-zero and overflow. // and 2. there are two possible failure cases, divide-by-zero and overflow.
let zero_err = if op == BinOp::Div { let zero_err = if op == BinOp::Div {
PanicInfo::DivisionByZero AssertKind::DivisionByZero
} else { } else {
PanicInfo::RemainderByZero AssertKind::RemainderByZero
}; };
let overflow_err = PanicInfo::Overflow(op); let overflow_err = AssertKind::Overflow(op);
// Check for / 0 // Check for / 0
let is_zero = self.temp(bool_ty, span); let is_zero = self.temp(bool_ty, span);