diff --git a/src/error.rs b/src/error.rs index 919232d9eef..38b64870f89 100644 --- a/src/error.rs +++ b/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>; @@ -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()), } } diff --git a/src/eval_context.rs b/src/eval_context.rs index 2c27dea2843..74b0e6ec068 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -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( } 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) -> 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 { diff --git a/src/operator.rs b/src/operator.rs index e5ed99b2434..09058c23886 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -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; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index c773620cbb5..049b66c67a4 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -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),