Skip to content

Commit

Permalink
Merge pull request #218 from oli-obk/minimal_ctfe
Browse files Browse the repository at this point in the history
Initial work towards checking const eval rules in miri
  • Loading branch information
oli-obk committed Jun 27, 2017
2 parents 14d1309 + ab400f3 commit f3832c6
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/error.rs
Expand Up @@ -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>>;
Expand Down Expand Up @@ -156,6 +158,10 @@ impl<'tcx> Error for EvalError<'tcx> {
"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",
}
}

Expand Down Expand Up @@ -191,6 +197,10 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
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()),
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/eval_context.rs
Expand Up @@ -655,6 +655,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

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);
Expand Down Expand Up @@ -701,6 +704,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

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)?;
Expand All @@ -712,6 +718,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

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)?;
}
Expand Down Expand Up @@ -1583,6 +1592,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}

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 {
Expand Down
7 changes: 7 additions & 0 deletions src/operator.rs
Expand Up @@ -151,6 +151,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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;
Expand Down
14 changes: 14 additions & 0 deletions src/terminator/mod.rs
Expand Up @@ -37,6 +37,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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)?;
Expand Down Expand Up @@ -92,6 +95,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {

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);
Expand Down Expand Up @@ -424,11 +430,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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),
Expand Down

0 comments on commit f3832c6

Please sign in to comment.