Initial work towards checking const eval rules in miri

This commit is contained in:
Oliver Schneider 2017-06-26 17:58:47 +02:00
parent 14d130946d
commit ab400f3eea
4 changed files with 46 additions and 0 deletions

View File

@ -63,6 +63,8 @@ pub enum EvalError<'tcx> {
HeapAllocNonPowerOfTwoAlignment(u64),
Unreachable,
Panic,
NeedsRfc(String),
NotConst(String),
}
pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
@ -156,6 +158,10 @@ fn description(&self) -> &str {
"entered unreachable code",
EvalError::Panic =>
"the evaluated program panicked",
EvalError::NeedsRfc(_) =>
"this feature needs an rfc before being allowed inside constants",
EvalError::NotConst(_) =>
"this feature is not compatible with constant evaluation",
}
}
@ -191,6 +197,10 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "expected primitive type, got {}", ty),
EvalError::Layout(ref err) =>
write!(f, "rustc layout computation failed: {:?}", err),
EvalError::NeedsRfc(ref msg) =>
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg),
EvalError::NotConst(ref msg) =>
write!(f, "Cannot evaluate within constants: \"{}\"", msg),
_ => write!(f, "{}", self.description()),
}
}

View File

@ -655,6 +655,9 @@ pub(super) fn eval_rvalue_into_lvalue(
}
Len(ref lvalue) => {
if self.frame().const_env() {
return Err(EvalError::NeedsRfc("computing the length of arrays".to_string()));
}
let src = self.eval_lvalue(lvalue)?;
let ty = self.lvalue_ty(lvalue);
let (_, len) = src.elem_ty_and_len(ty);
@ -701,6 +704,9 @@ pub(super) fn eval_rvalue_into_lvalue(
}
NullaryOp(mir::NullOp::Box, ty) => {
if self.frame().const_env() {
return Err(EvalError::NeedsRfc("\"heap\" allocations".to_string()));
}
// FIXME: call the `exchange_malloc` lang item if available
if self.type_size(ty)?.expect("box only works with sized types") == 0 {
let align = self.type_align(ty)?;
@ -712,6 +718,9 @@ pub(super) fn eval_rvalue_into_lvalue(
}
NullaryOp(mir::NullOp::SizeOf, ty) => {
if self.frame().const_env() {
return Err(EvalError::NeedsRfc("computing the size of types (size_of)".to_string()));
}
let size = self.type_size(ty)?.expect("SizeOf nullary MIR operator called for unsized type");
self.write_primval(dest, PrimVal::from_u128(size as u128), dest_ty)?;
}
@ -1583,6 +1592,12 @@ pub fn modify_local<F>(
}
impl<'tcx> Frame<'tcx> {
pub fn const_env(&self) -> bool {
match self.return_to_block {
StackPopCleanup::MarkStatic(_) => true,
_ => false,
}
}
pub fn get_local(&self, local: mir::Local, field: Option<usize>) -> EvalResult<'tcx, Value> {
// Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0.
if let Some(field) = field {

View File

@ -151,6 +151,13 @@ pub fn binary_op(
let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
let isize = PrimValKind::from_int_size(self.memory.pointer_size());
if !left_kind.is_float() && !right_kind.is_float() {
if (!left.is_bytes() && !right.is_bytes()) && self.frame().const_env() {
if left.is_ptr() && right.is_ptr() {
return Err(EvalError::NotConst("Comparing pointers".to_string()));
} else {
return Err(EvalError::NeedsRfc("Comparing Pointers integers with pointers".to_string()));
}
}
match bin_op {
Offset if left_kind == Ptr && right_kind == usize => {
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;

View File

@ -37,6 +37,9 @@ pub(super) fn eval_terminator(
Goto { target } => self.goto_block(target),
SwitchInt { ref discr, ref values, ref targets, .. } => {
if self.frame().const_env() {
return Err(EvalError::NeedsRfc("branching (if, match, loop, ...)".to_string()));
}
let discr_val = self.eval_operand(discr)?;
let discr_ty = self.operand_ty(discr);
let discr_prim = self.value_to_primval(discr_val, discr_ty)?;
@ -92,6 +95,9 @@ pub(super) fn eval_terminator(
Drop { ref location, target, .. } => {
trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs());
if self.frame().const_env() {
return Err(EvalError::NeedsRfc("invoking `Drop::drop`".to_string()));
}
let lval = self.eval_lvalue(location)?;
let ty = self.lvalue_ty(location);
self.goto_block(target);
@ -424,11 +430,19 @@ fn eval_fn_call_inner(
let mir = match self.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => {
if self.frame().const_env() {
return Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path)));
}
self.call_missing_fn(instance, destination, arg_operands, sig, path)?;
return Ok(true);
},
Err(other) => return Err(other),
};
if self.frame().const_env() && !self.tcx.is_const_fn(instance.def_id()) {
return Err(EvalError::NotConst(format!("calling non-const fn `{}`", instance)));
}
let (return_lvalue, return_to_block) = match destination {
Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
None => (Lvalue::undef(), StackPopCleanup::None),