Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

const_prop: detect and avoid catching Miri errors that require allocation #66832

Merged
merged 1 commit into from
Dec 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions src/librustc/mir/interpret/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ CloneTypeFoldableImpls! {
pub type ConstEvalRawResult<'tcx> = Result<RawConst<'tcx>, ErrorHandled>;
pub type ConstEvalResult<'tcx> = Result<&'tcx ty::Const<'tcx>, ErrorHandled>;

#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
#[derive(Clone, Debug)]
pub struct ConstEvalErr<'tcx> {
pub span: Span,
pub error: crate::mir::interpret::InterpError<'tcx>,
pub stacktrace: Vec<FrameInfo<'tcx>>,
}

#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
#[derive(Clone, Debug)]
pub struct FrameInfo<'tcx> {
/// This span is in the caller.
pub call_site: Span,
Expand Down Expand Up @@ -327,7 +327,7 @@ impl<O: fmt::Debug> fmt::Debug for PanicInfo<O> {
/// 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
/// where we work on generic code or execution does not have all information available.
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
#[derive(Clone, HashStable)]
pub enum InvalidProgramInfo<'tcx> {
/// Resolution can fail if we are in a too generic context.
TooGeneric,
Expand Down Expand Up @@ -357,7 +357,7 @@ impl fmt::Debug for InvalidProgramInfo<'tcx> {
}

/// Error information for when the program caused Undefined Behavior.
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
#[derive(Clone, HashStable)]
pub enum UndefinedBehaviorInfo {
/// Free-form case. Only for errors that are never caught!
Ub(String),
Expand Down Expand Up @@ -390,11 +390,15 @@ impl fmt::Debug for UndefinedBehaviorInfo {
///
/// Currently, we also use this as fall-back error kind for errors that have not been
/// categorized yet.
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
#[derive(Clone, HashStable)]
pub enum UnsupportedOpInfo<'tcx> {
/// Free-form case. Only for errors that are never caught!
Unsupported(String),

/// When const-prop encounters a situation it does not support, it raises this error.
/// This must not allocate for performance reasons.
ConstPropUnsupported(&'tcx str),

// -- Everything below is not categorized yet --
FunctionAbiMismatch(Abi, Abi),
FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
Expand Down Expand Up @@ -555,13 +559,15 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
not a power of two"),
Unsupported(ref msg) =>
write!(f, "{}", msg),
ConstPropUnsupported(ref msg) =>
write!(f, "Constant propagation encountered an unsupported situation: {}", msg),
}
}
}

/// Error information for when the program exhausted the resources granted to it
/// by the interpreter.
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
#[derive(Clone, HashStable)]
pub enum ResourceExhaustionInfo {
/// The stack grew too big.
StackFrameLimitReached,
Expand All @@ -582,7 +588,7 @@ impl fmt::Debug for ResourceExhaustionInfo {
}
}

#[derive(Clone, RustcEncodable, RustcDecodable, HashStable)]
#[derive(Clone, HashStable)]
pub enum InterpError<'tcx> {
/// The program panicked.
Panic(PanicInfo<u64>),
Expand Down
32 changes: 25 additions & 7 deletions src/librustc_mir/transform/const_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
_ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
_unwind: Option<BasicBlock>
) -> InterpResult<'tcx> {
throw_unsup_format!("calling intrinsics isn't supported in ConstProp");
throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"));
}

fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer,
) -> InterpResult<'tcx, u64> {
throw_unsup_format!("ptr-to-int casts aren't supported in ConstProp");
throw_unsup!(ConstPropUnsupported("ptr-to-int casts aren't supported in ConstProp"));
}

fn binary_ptr_op(
Expand All @@ -185,7 +185,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
_right: ImmTy<'tcx>,
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
// We can't do this because aliasing of memory can differ between const eval and llvm
throw_unsup_format!("pointer arithmetic or comparisons aren't supported in ConstProp");
throw_unsup!(ConstPropUnsupported("pointer arithmetic or comparisons aren't supported \
in ConstProp"));
}

fn find_foreign_static(
Expand Down Expand Up @@ -218,7 +219,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_dest: PlaceTy<'tcx>,
) -> InterpResult<'tcx> {
throw_unsup_format!("can't const prop `box` keyword");
throw_unsup!(ConstPropUnsupported("can't const prop `box` keyword"));
}

fn access_local(
Expand All @@ -229,7 +230,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
let l = &frame.locals[local];

if l.value == LocalValue::Uninitialized {
throw_unsup_format!("tried to access an uninitialized local");
throw_unsup!(ConstPropUnsupported("tried to access an uninitialized local"));
}

l.access()
Expand All @@ -241,7 +242,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
// if the static allocation is mutable or if it has relocations (it may be legal to mutate
// the memory behind that in the future), then we can't const prop it
if allocation.mutability == Mutability::Mutable || allocation.relocations().len() > 0 {
throw_unsup_format!("can't eval mutable statics in ConstProp");
throw_unsup!(ConstPropUnsupported("can't eval mutable statics in ConstProp"));
}

Ok(())
Expand Down Expand Up @@ -389,9 +390,26 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let r = match f(self) {
Ok(val) => Some(val),
Err(error) => {
use rustc::mir::interpret::InterpError::*;
use rustc::mir::interpret::{
UnsupportedOpInfo,
UndefinedBehaviorInfo,
InterpError::*
};
match error.kind {
Exit(_) => bug!("the CTFE program cannot exit"),

// Some error shouldn't come up because creating them causes
// an allocation, which we should avoid. When that happens,
// dedicated error variants should be introduced instead.
// Only test this in debug builds though to avoid disruptions.
Unsupported(UnsupportedOpInfo::Unsupported(_))
| Unsupported(UnsupportedOpInfo::ValidationFailure(_))
| UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
| UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_))
if cfg!(debug_assertions) => {
bug!("const-prop encountered allocating error: {:?}", error.kind);
}

Unsupported(_)
| UndefinedBehavior(_)
| InvalidProgram(_)
Expand Down