From 4a4640a331f20f3099687b99e81c438aedebeb81 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 25 Jul 2017 11:32:48 +0200 Subject: [PATCH] Move more non-CTFE operations to the Machine --- miri/lib.rs | 17 +++ miri/operator.rs | 152 +++++++++++++++++++ src/librustc_mir/interpret/const_eval.rs | 66 +++++++- src/librustc_mir/interpret/error.rs | 28 ++-- src/librustc_mir/interpret/eval_context.rs | 13 +- src/librustc_mir/interpret/machine.rs | 21 +++ src/librustc_mir/interpret/memory.rs | 4 +- src/librustc_mir/interpret/operator.rs | 119 +-------------- src/librustc_mir/interpret/terminator/mod.rs | 15 +- 9 files changed, 282 insertions(+), 153 deletions(-) create mode 100644 miri/operator.rs diff --git a/miri/lib.rs b/miri/lib.rs index ef2b65cf1e36..4482e8fcb7cc 100644 --- a/miri/lib.rs +++ b/miri/lib.rs @@ -7,6 +7,7 @@ #[macro_use] extern crate log; extern crate log_settings; +#[macro_use] extern crate rustc; extern crate rustc_const_math; extern crate rustc_data_structures; @@ -25,8 +26,10 @@ extern crate rustc_miri; pub use rustc_miri::interpret::*; mod missing_fns; +mod operator; use missing_fns::EvalContextExt as MissingFnsEvalContextExt; +use operator::EvalContextExt as OperatorEvalContextExt; pub fn eval_main<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -280,4 +283,18 @@ impl<'tcx> Machine<'tcx> for Evaluator { ) -> EvalResult<'tcx> { ecx.call_missing_fn(instance, destination, arg_operands, sig, path) } + fn ptr_op<'a>( + ecx: &rustc_miri::interpret::EvalContext<'a, 'tcx, Self>, + bin_op: mir::BinOp, + left: PrimVal, + left_ty: ty::Ty<'tcx>, + right: PrimVal, + right_ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> { + ecx.ptr_op(bin_op, left, left_ty, right, right_ty) + } + + fn check_non_const_fn_call(_instance: ty::Instance<'tcx>) -> EvalResult<'tcx> { + Ok(()) + } } diff --git a/miri/operator.rs b/miri/operator.rs new file mode 100644 index 000000000000..fcc3986015d9 --- /dev/null +++ b/miri/operator.rs @@ -0,0 +1,152 @@ +use rustc::ty; +use rustc::mir; + +use rustc_miri::interpret::*; + +pub trait EvalContextExt<'tcx> { + fn ptr_op( + &self, + bin_op: mir::BinOp, + left: PrimVal, + left_ty: ty::Ty<'tcx>, + right: PrimVal, + right_ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(PrimVal, bool)>>; + + fn ptr_int_arithmetic( + &self, + bin_op: mir::BinOp, + left: MemoryPointer, + right: i128, + signed: bool, + ) -> EvalResult<'tcx, (PrimVal, bool)>; +} + +impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> { + fn ptr_op( + &self, + bin_op: mir::BinOp, + left: PrimVal, + left_ty: ty::Ty<'tcx>, + right: PrimVal, + right_ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> { + use rustc_miri::interpret::PrimValKind::*; + use rustc::mir::BinOp::*; + let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); + let isize = PrimValKind::from_int_size(self.memory.pointer_size()); + let left_kind = self.ty_to_primval_kind(left_ty)?; + let right_kind = self.ty_to_primval_kind(right_ty)?; + 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; + let ptr = self.pointer_offset(left.into(), pointee_ty, right.to_bytes()? as i64)?; + Ok(Some((ptr.into_inner_primval(), false))) + }, + // These work on anything + Eq if left_kind == right_kind => { + let result = match (left, right) { + (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right, + (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right, + (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), + _ => false, + }; + Ok(Some((PrimVal::from_bool(result), false))) + } + Ne if left_kind == right_kind => { + let result = match (left, right) { + (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right, + (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right, + (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), + _ => true, + }; + Ok(Some((PrimVal::from_bool(result), false))) + } + // These need both pointers to be in the same allocation + Lt | Le | Gt | Ge | Sub + if left_kind == right_kind + && (left_kind == Ptr || left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_ptr() => { + let left = left.to_ptr()?; + let right = right.to_ptr()?; + if left.alloc_id == right.alloc_id { + let res = match bin_op { + Lt => left.offset < right.offset, + Le => left.offset <= right.offset, + Gt => left.offset > right.offset, + Ge => left.offset >= right.offset, + Sub => return self.binary_op( + Sub, + PrimVal::Bytes(left.offset as u128), + self.tcx.types.usize, + PrimVal::Bytes(right.offset as u128), + self.tcx.types.usize, + ).map(Some), + _ => bug!("We already established it has to be one of these operators."), + }; + Ok(Some((PrimVal::from_bool(res), false))) + } else { + // Both are pointers, but from different allocations. + Err(EvalError::InvalidPointerMath) + } + } + // These work if one operand is a pointer, the other an integer + Add | BitAnd | Sub + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_bytes() => { + // Cast to i128 is fine as we checked the kind to be ptr-sized + self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize).map(Some) + } + Add | BitAnd + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_bytes() && right.is_ptr() => { + // This is a commutative operation, just swap the operands + self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize).map(Some) + } + _ => Ok(None) + } + } + + fn ptr_int_arithmetic( + &self, + bin_op: mir::BinOp, + left: MemoryPointer, + right: i128, + signed: bool, + ) -> EvalResult<'tcx, (PrimVal, bool)> { + use rustc::mir::BinOp::*; + + fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) { + (PrimVal::Ptr(res), over) + } + + Ok(match bin_op { + Sub => + // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter + map_to_primval(left.overflowing_signed_offset(-right, self)), + Add if signed => + map_to_primval(left.overflowing_signed_offset(right, self)), + Add if !signed => + map_to_primval(left.overflowing_offset(right as u64, self)), + + BitAnd if !signed => { + let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1); + let right = right as u64; + if right & base_mask == base_mask { + // Case 1: The base address bits are all preserved, i.e., right is all-1 there + (PrimVal::Ptr(MemoryPointer::new(left.alloc_id, left.offset & right)), false) + } else if right & base_mask == 0 { + // Case 2: The base address bits are all taken away, i.e., right is all-0 there + (PrimVal::from_u128((left.offset & right) as u128), false) + } else { + return Err(EvalError::ReadPointerAsBytes); + } + } + + _ => { + let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" }); + return Err(EvalError::Unimplemented(msg)); + } + }) + } +} diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index 7a722b0344f1..4eec7d712abd 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -5,7 +5,7 @@ use rustc::mir; use syntax::ast::Mutability; use super::{ - EvalError, EvalResult, + EvalResult, EvalError, Global, GlobalId, Lvalue, PrimVal, EvalContext, StackPopCleanup, @@ -13,6 +13,9 @@ use super::{ use rustc_const_math::ConstInt; +use std::fmt; +use std::error::Error; + pub fn eval_body_as_primval<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>, @@ -21,7 +24,7 @@ pub fn eval_body_as_primval<'a, 'tcx>( let mut ecx = EvalContext::::new(tcx, limits, (), ()); let cid = GlobalId { instance, promoted: None }; if ecx.tcx.has_attr(instance.def_id(), "linkage") { - return Err(EvalError::NotConst("extern global".to_string())); + return Err(ConstEvalError::NotConst("extern global".to_string()).into()); } let mir = ecx.load_mir(instance.def)?; @@ -75,12 +78,52 @@ pub fn eval_body_as_integer<'a, 'tcx>( TyUint(UintTy::U64) => ConstInt::U64(prim as u64), TyUint(UintTy::U128) => ConstInt::U128(prim), TyUint(UintTy::Us) => ConstInt::Usize(ConstUsize::new(prim as u64, tcx.sess.target.uint_type).expect("miri should already have errored")), - _ => return Err(EvalError::NeedsRfc("evaluating anything other than isize/usize during typeck".to_string())), + _ => return Err(ConstEvalError::NeedsRfc("evaluating anything other than isize/usize during typeck".to_string()).into()), }) } struct Evaluator; +impl<'tcx> Into> for ConstEvalError { + fn into(self) -> EvalError<'tcx> { + EvalError::MachineError(Box::new(self)) + } +} + +#[derive(Clone, Debug)] +enum ConstEvalError { + NeedsRfc(String), + NotConst(String), +} + +impl fmt::Display for ConstEvalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ConstEvalError::*; + match *self { + NeedsRfc(ref msg) => + write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg), + NotConst(ref msg) => + write!(f, "Cannot evaluate within constants: \"{}\"", msg), + } + } +} + +impl Error for ConstEvalError { + fn description(&self) -> &str { + use self::ConstEvalError::*; + match *self { + NeedsRfc(_) => + "this feature needs an rfc before being allowed inside constants", + NotConst(_) => + "this feature is not compatible with constant evaluation", + } + } + + fn cause(&self) -> Option<&Error> { + None + } +} + impl<'tcx> super::Machine<'tcx> for Evaluator { type Data = (); type MemoryData = (); @@ -93,6 +136,21 @@ impl<'tcx> super::Machine<'tcx> for Evaluator { path: String, ) -> EvalResult<'tcx> { // some simple things like `malloc` might get accepted in the future - Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path))) + Err(ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path)).into()) + } + + fn ptr_op<'a>( + _ecx: &EvalContext<'a, 'tcx, Self>, + _bin_op: mir::BinOp, + _left: PrimVal, + _left_ty: Ty<'tcx>, + _right: PrimVal, + _right_ty: Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> { + Err(ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into()) + } + + fn check_non_const_fn_call(instance: ty::Instance<'tcx>) -> EvalResult<'tcx> { + return Err(ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into()); } } diff --git a/src/librustc_mir/interpret/error.rs b/src/librustc_mir/interpret/error.rs index a4976d8aec49..dd718c737af7 100644 --- a/src/librustc_mir/interpret/error.rs +++ b/src/librustc_mir/interpret/error.rs @@ -10,8 +10,11 @@ use super::{ use rustc_const_math::ConstMathErr; use syntax::codemap::Span; -#[derive(Clone, Debug)] +#[derive(Debug)] pub enum EvalError<'tcx> { + /// This variant is used by machines to signal their own errors that do not + /// match an existing variant + MachineError(Box), FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>), NoMirFor(String), UnterminatedCString(MemoryPointer), @@ -95,8 +98,6 @@ pub enum EvalError<'tcx> { HeapAllocNonPowerOfTwoAlignment(u64), Unreachable, Panic, - NeedsRfc(String), - NotConst(String), ReadFromReturnPointer, PathNotFound(Vec), } @@ -107,6 +108,7 @@ impl<'tcx> Error for EvalError<'tcx> { fn description(&self) -> &str { use self::EvalError::*; match *self { + MachineError(ref inner) => inner.description(), FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", InvalidMemoryAccess => @@ -211,10 +213,6 @@ impl<'tcx> Error for EvalError<'tcx> { "entered unreachable code", Panic => "the evaluated program panicked", - NeedsRfc(_) => - "this feature needs an rfc before being allowed inside constants", - NotConst(_) => - "this feature is not compatible with constant evaluation", ReadFromReturnPointer => "tried to read from the return pointer", EvalError::PathNotFound(_) => @@ -222,7 +220,13 @@ impl<'tcx> Error for EvalError<'tcx> { } } - fn cause(&self) -> Option<&Error> { None } + fn cause(&self) -> Option<&Error> { + use self::EvalError::*; + match *self { + MachineError(ref inner) => Some(&**inner), + _ => None, + } + } } impl<'tcx> fmt::Display for EvalError<'tcx> { @@ -278,12 +282,10 @@ impl<'tcx> fmt::Display for EvalError<'tcx> { write!(f, "expected primitive type, got {}", ty), Layout(ref err) => write!(f, "rustc layout computation failed: {:?}", err), - NeedsRfc(ref msg) => - write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg), - NotConst(ref msg) => - write!(f, "Cannot evaluate within constants: \"{}\"", msg), - EvalError::PathNotFound(ref path) => + PathNotFound(ref path) => write!(f, "Cannot find path {:?}", path), + MachineError(ref inner) => + write!(f, "machine error: {}", inner), _ => write!(f, "{}", self.description()), } } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index c75c98206552..88f9af75f950 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -666,9 +666,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } Len(ref lvalue) => { - if self.const_env() { - return Err(EvalError::NeedsRfc("computing the length of arrays".to_string())); - } + // FIXME(CTFE): don't allow computing the length of arrays in const eval let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); let (_, len) = src.elem_ty_and_len(ty); @@ -692,9 +690,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } NullaryOp(mir::NullOp::Box, ty) => { - if self.const_env() { - return Err(EvalError::NeedsRfc("\"heap\" allocations".to_string())); - } + // FIXME(CTFE): don't allow heap allocations in const eval // FIXME: call the `exchange_malloc` lang item if available let size = self.type_size(ty)?.expect("box only works with sized types"); if size == 0 { @@ -708,9 +704,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { } NullaryOp(mir::NullOp::SizeOf, ty) => { - if self.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)?; } @@ -944,7 +937,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ptr.wrapping_signed_offset(offset, self) } - pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { + pub fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { // This function raises an error if the offset moves the pointer outside of its allocation. We consider // ZSTs their own huge allocation that doesn't overlap with anything (and nothing moves in there because the size is 0). // We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index c907928ded0a..5d762c81a9ba 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -6,6 +6,7 @@ use super::{ EvalResult, EvalContext, Lvalue, + PrimVal }; use rustc::{mir, ty}; @@ -29,5 +30,25 @@ pub trait Machine<'tcx>: Sized { sig: ty::FnSig<'tcx>, path: String, ) -> EvalResult<'tcx>; + + /// Called when operating on the value of pointers. + /// + /// Returns `None` if the operation should be handled by the integer + /// op code + /// + /// Returns a (value, overflowed) pair otherwise + fn ptr_op<'a>( + ecx: &EvalContext<'a, 'tcx, Self>, + bin_op: mir::BinOp, + left: PrimVal, + left_ty: ty::Ty<'tcx>, + right: PrimVal, + right_ty: ty::Ty<'tcx>, + ) -> EvalResult<'tcx, Option<(PrimVal, bool)>>; + + /// Called when adding a frame for a function that's not `const fn` + /// + /// Const eval returns `Err`, miri returns `Ok` + fn check_non_const_fn_call(instance: ty::Instance<'tcx>) -> EvalResult<'tcx>; } diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index d5b562730d7c..28cc9b9a25e2 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -198,7 +198,7 @@ impl<'tcx> MemoryPointer { MemoryPointer::new(self.alloc_id, cx.data_layout().wrapping_signed_offset(self.offset, i)) } - pub(crate) fn overflowing_signed_offset(self, i: i128, cx: C) -> (Self, bool) { + pub fn overflowing_signed_offset(self, i: i128, cx: C) -> (Self, bool) { let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } @@ -207,7 +207,7 @@ impl<'tcx> MemoryPointer { Ok(MemoryPointer::new(self.alloc_id, cx.data_layout().signed_offset(self.offset, i)?)) } - pub(crate) fn overflowing_offset(self, i: u64, cx: C) -> (Self, bool) { + pub fn overflowing_offset(self, i: u64, cx: C) -> (Self, bool) { let (res, over) = cx.data_layout().overflowing_offset(self.offset, i); (MemoryPointer::new(self.alloc_id, res), over) } diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 62fe5cc33619..606761f371c2 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -1,10 +1,9 @@ use rustc::mir; -use rustc::ty::{self, Ty}; +use rustc::ty::Ty; use super::{ EvalError, EvalResult, EvalContext, - MemoryPointer, Lvalue, Machine, }; @@ -153,75 +152,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); // I: Handle operations that support pointers - 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.const_env() { - return Err(EvalError::NeedsRfc("Pointer arithmetic or comparison".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; - let ptr = self.pointer_offset(left.into(), pointee_ty, right.to_bytes()? as i64)?; - return Ok((ptr.into_inner_primval(), false)); - }, - // These work on anything - Eq if left_kind == right_kind => { - let result = match (left, right) { - (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right, - (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right, - (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), - _ => false, - }; - return Ok((PrimVal::from_bool(result), false)); - } - Ne if left_kind == right_kind => { - let result = match (left, right) { - (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right, - (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right, - (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), - _ => true, - }; - return Ok((PrimVal::from_bool(result), false)); - } - // These need both pointers to be in the same allocation - Lt | Le | Gt | Ge | Sub - if left_kind == right_kind - && (left_kind == Ptr || left_kind == usize || left_kind == isize) - && left.is_ptr() && right.is_ptr() => { - let left = left.to_ptr()?; - let right = right.to_ptr()?; - if left.alloc_id == right.alloc_id { - let res = match bin_op { - Lt => left.offset < right.offset, - Le => left.offset <= right.offset, - Gt => left.offset > right.offset, - Ge => left.offset >= right.offset, - Sub => { - return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset); - } - _ => bug!("We already established it has to be one of these operators."), - }; - return Ok((PrimVal::from_bool(res), false)); - } else { - // Both are pointers, but from different allocations. - return Err(EvalError::InvalidPointerMath); - } - } - // These work if one operand is a pointer, the other an integer - Add | BitAnd | Sub - if left_kind == right_kind && (left_kind == usize || left_kind == isize) - && left.is_ptr() && right.is_bytes() => { - // Cast to i128 is fine as we checked the kind to be ptr-sized - return self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize); - } - Add | BitAnd - if left_kind == right_kind && (left_kind == usize || left_kind == isize) - && left.is_bytes() && right.is_ptr() => { - // This is a commutative operation, just swap the operands - return self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize); - } - _ => {} + if let Some(handled) = M::ptr_op(self, bin_op, left, left_ty, right, right_ty)? { + return Ok(handled); } } @@ -270,6 +203,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { (Div, F64) => f64_arithmetic!(/, l, r), (Rem, F64) => f64_arithmetic!(%, l, r), + (Eq, _) => PrimVal::from_bool(l == r), + (Ne, _) => PrimVal::from_bool(l != r), + (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), (Lt, _) => PrimVal::from_bool(l < r), (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), @@ -297,49 +233,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Ok((val, false)) } - - fn ptr_int_arithmetic( - &self, - bin_op: mir::BinOp, - left: MemoryPointer, - right: i128, - signed: bool, - ) -> EvalResult<'tcx, (PrimVal, bool)> { - use rustc::mir::BinOp::*; - - fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) { - (PrimVal::Ptr(res), over) - } - - Ok(match bin_op { - Sub => - // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter - map_to_primval(left.overflowing_signed_offset(-right, self)), - Add if signed => - map_to_primval(left.overflowing_signed_offset(right, self)), - Add if !signed => - map_to_primval(left.overflowing_offset(right as u64, self)), - - BitAnd if !signed => { - let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1); - let right = right as u64; - if right & base_mask == base_mask { - // Case 1: The base address bits are all preserved, i.e., right is all-1 there - (PrimVal::Ptr(MemoryPointer::new(left.alloc_id, left.offset & right)), false) - } else if right & base_mask == 0 { - // Case 2: The base address bits are all taken away, i.e., right is all-0 there - (PrimVal::from_u128((left.offset & right) as u128), false) - } else { - return Err(EvalError::ReadPointerAsBytes); - } - } - - _ => { - let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" }); - return Err(EvalError::Unimplemented(msg)); - } - }) - } } pub fn unary_op<'tcx>( diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 8861d4bfdc96..779ab8c98742 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -40,9 +40,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Goto { target } => self.goto_block(target), SwitchInt { ref discr, ref values, ref targets, .. } => { - if self.const_env() { - return Err(EvalError::NeedsRfc("branching (if, match, loop, ...)".to_string())); - } + // FIXME(CTFE): forbid branching 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)?; @@ -100,9 +98,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { Drop { ref location, target, .. } => { trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs()); - if self.const_env() { - return Err(EvalError::NeedsRfc("invoking `Drop::drop`".to_string())); - } + // FIXME(CTFE): forbid drop in const eval let lval = self.eval_lvalue(location)?; let ty = self.lvalue_ty(location); self.goto_block(target); @@ -436,17 +432,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { let mir = match self.load_mir(instance.def) { Ok(mir) => mir, Err(EvalError::NoMirFor(path)) => { - if self.const_env() { - return Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path))); - } M::call_missing_fn(self, instance, destination, arg_operands, sig, path)?; return Ok(true); }, Err(other) => return Err(other), }; - if self.const_env() && !self.tcx.is_const_fn(instance.def_id()) { - return Err(EvalError::NotConst(format!("calling non-const fn `{}`", instance))); + if !self.tcx.is_const_fn(instance.def_id()) { + M::check_non_const_fn_call(instance)?; } let (return_lvalue, return_to_block) = match destination {