Merge pull request #218 from oli-obk/minimal_ctfe
Initial work towards checking const eval rules in miri
This commit is contained in:
commit
f3832c6476
10
src/error.rs
10
src/error.rs
@ -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()),
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user