diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 7bc70b08cc76..3cae24f34d8c 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1,4 +1,4 @@ -use rustc::middle::const_val; +use rustc::middle::const_val::ConstVal; use rustc::hir::def_id::DefId; use rustc::mir::mir_map::MirMap; use rustc::mir::repr as mir; @@ -99,6 +99,12 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } +#[derive(Clone, Copy, Debug, PartialEq)] +enum Value { + Ptr(Pointer), + Prim(PrimVal), +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Lvalue { ptr: Pointer, @@ -182,45 +188,30 @@ pub fn stack(&self) -> &[Frame<'a, 'tcx>] { &self.stack } - // TODO(solson): Try making const_to_primval instead. - fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> { + fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; - macro_rules! i2p { - ($i:ident, $n:expr) => {{ - let ptr = self.memory.allocate($n, $n)?; - self.memory.write_int(ptr, $i as i64, $n)?; - Ok(ptr) - }} - } - match *const_val { - Float(ConstFloat::F32(f)) => { - let ptr = self.memory.allocate(4, 4)?; - self.memory.write_f32(ptr, f)?; - Ok(ptr) - }, - Float(ConstFloat::F64(f)) => { - let ptr = self.memory.allocate(8, 8)?; - self.memory.write_f64(ptr, f)?; - Ok(ptr) - }, - Float(ConstFloat::FInfer{..}) | - Integral(ConstInt::Infer(_)) | - Integral(ConstInt::InferSigned(_)) => bug!("uninferred constants only exist before typeck"), - Integral(ConstInt::I8(i)) => i2p!(i, 1), - Integral(ConstInt::U8(i)) => i2p!(i, 1), + + let primval = match *const_val { + Integral(ConstInt::I8(i)) => Value::Prim(PrimVal::I8(i)), + Integral(ConstInt::U8(i)) => Value::Prim(PrimVal::U8(i)), Integral(ConstInt::Isize(ConstIsize::Is16(i))) | - Integral(ConstInt::I16(i)) => i2p!(i, 2), + Integral(ConstInt::I16(i)) => Value::Prim(PrimVal::I16(i)), Integral(ConstInt::Usize(ConstUsize::Us16(i))) | - Integral(ConstInt::U16(i)) => i2p!(i, 2), + Integral(ConstInt::U16(i)) => Value::Prim(PrimVal::U16(i)), Integral(ConstInt::Isize(ConstIsize::Is32(i))) | - Integral(ConstInt::I32(i)) => i2p!(i, 4), + Integral(ConstInt::I32(i)) => Value::Prim(PrimVal::I32(i)), Integral(ConstInt::Usize(ConstUsize::Us32(i))) | - Integral(ConstInt::U32(i)) => i2p!(i, 4), + Integral(ConstInt::U32(i)) => Value::Prim(PrimVal::U32(i)), Integral(ConstInt::Isize(ConstIsize::Is64(i))) | - Integral(ConstInt::I64(i)) => i2p!(i, 8), + Integral(ConstInt::I64(i)) => Value::Prim(PrimVal::I64(i)), Integral(ConstInt::Usize(ConstUsize::Us64(i))) | - Integral(ConstInt::U64(i)) => i2p!(i, 8), + Integral(ConstInt::U64(i)) => Value::Prim(PrimVal::U64(i)), + Float(ConstFloat::F32(f)) => Value::Prim(PrimVal::F32(f)), + Float(ConstFloat::F64(f)) => Value::Prim(PrimVal::F64(f)), + Bool(b) => Value::Prim(PrimVal::Bool(b)), + Char(c) => Value::Prim(PrimVal::Char(c)), + Str(ref s) => { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(s.len(), 1)?; @@ -229,33 +220,32 @@ macro_rules! i2p { self.memory.write_bytes(static_ptr, s.as_bytes())?; self.memory.write_ptr(ptr, static_ptr)?; self.memory.write_usize(extra, s.len() as u64)?; - Ok(ptr) + Value::Ptr(ptr) } + ByteStr(ref bs) => { let psize = self.memory.pointer_size(); let static_ptr = self.memory.allocate(bs.len(), 1)?; let ptr = self.memory.allocate(psize, psize)?; self.memory.write_bytes(static_ptr, bs)?; self.memory.write_ptr(ptr, static_ptr)?; - Ok(ptr) + Value::Ptr(ptr) } - Bool(b) => { - let ptr = self.memory.allocate(1, 1)?; - self.memory.write_bool(ptr, b)?; - Ok(ptr) - } - Char(c) => { - let ptr = self.memory.allocate(4, 4)?; - self.memory.write_uint(ptr, c as u64, 4)?; - Ok(ptr) - }, - Struct(_node_id) => unimplemented!(), - Tuple(_node_id) => unimplemented!(), - Function(_def_id) => unimplemented!(), - Array(_, _) => unimplemented!(), - Repeat(_, _) => unimplemented!(), - Dummy => unimplemented!(), - } + + Struct(_) => unimplemented!(), + Tuple(_) => unimplemented!(), + Function(_) => unimplemented!(), + Array(_, _) => unimplemented!(), + Repeat(_, _) => unimplemented!(), + Dummy => unimplemented!(), + + Float(ConstFloat::FInfer{..}) | + Integral(ConstInt::Infer(_)) | + Integral(ConstInt::InferSigned(_)) => + bug!("uninferred constants only exist before typeck"), + }; + + Ok(primval) } fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { @@ -404,15 +394,9 @@ fn intrinsic_overflowing( right: &mir::Operand<'tcx>, dest: Pointer, ) -> EvalResult<'tcx, bool> { - let left_ptr = self.eval_operand(left)?; - let left_ty = self.operand_ty(left); - let left_val = self.read_primval(left_ptr, left_ty)?; - - let right_ptr = self.eval_operand(right)?; - let right_ty = self.operand_ty(right); - let right_val = self.read_primval(right_ptr, right_ty)?; - - let (val, overflow) = primval::binary_op(op, left_val, right_val)?; + let left_primval = self.eval_operand_to_primval(left)?; + let right_primval = self.eval_operand_to_primval(right)?; + let (val, overflow) = primval::binary_op(op, left_primval, right_primval)?; self.memory.write_primval(dest, val)?; Ok(overflow) } @@ -424,17 +408,23 @@ fn assign_fields>( operands: &[mir::Operand<'tcx>], ) -> EvalResult<'tcx, ()> { for (offset, operand) in offsets.into_iter().zip(operands) { - let src = self.eval_operand(operand)?; - let src_ty = self.operand_ty(operand); + let value = self.eval_operand(operand)?; + let value_ty = self.operand_ty(operand); let field_dest = dest.offset(offset as isize); - self.move_(src, field_dest, src_ty)?; + self.write_value(value, field_dest, value_ty)?; } Ok(()) } - fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<'tcx>) - -> EvalResult<'tcx, ()> - { + /// Evaluate an assignment statement. + /// + /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue + /// type writes its results directly into the memory specified by the lvalue. + fn eval_rvalue_into_lvalue( + &mut self, + rvalue: &mir::Rvalue<'tcx>, + lvalue: &mir::Lvalue<'tcx>, + ) -> EvalResult<'tcx, ()> { let dest = self.eval_lvalue(lvalue)?.to_ptr(); let dest_ty = self.lvalue_ty(lvalue); let dest_layout = self.type_layout(dest_ty); @@ -442,8 +432,8 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' use rustc::mir::repr::Rvalue::*; match *rvalue { Use(ref operand) => { - let src = self.eval_operand(operand)?; - self.move_(src, dest, dest_ty)?; + let value = self.eval_operand(operand)?; + self.write_value(value, dest, dest_ty)?; } BinaryOp(bin_op, ref left, ref right) => { @@ -456,9 +446,7 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' } UnaryOp(un_op, ref operand) => { - let ptr = self.eval_operand(operand)?; - let ty = self.operand_ty(operand); - let val = self.read_primval(ptr, ty)?; + let val = self.eval_operand_to_primval(operand)?; self.memory.write_primval(dest, primval::unary_op(un_op, val)?)?; } @@ -499,9 +487,9 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' if nndiscr == variant as u64 { assert_eq!(operands.len(), 1); let operand = &operands[0]; - let src = self.eval_operand(operand)?; - let src_ty = self.operand_ty(operand); - self.move_(src, dest, src_ty)?; + let value = self.eval_operand(operand)?; + let value_ty = self.operand_ty(operand); + self.write_value(value, dest, value_ty)?; } else { assert_eq!(operands.len(), 0); self.memory.write_isize(dest, 0)?; @@ -549,15 +537,15 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' } Repeat(ref operand, _) => { - let (elem_size, elem_align, length) = match dest_ty.sty { - ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), self.type_align(elem_ty), n), + let (elem_ty, length) = match dest_ty.sty { + ty::TyArray(elem_ty, n) => (elem_ty, n), _ => bug!("tried to assign array-repeat to non-array type {:?}", dest_ty), }; - - let src = self.eval_operand(operand)?; + let elem_size = self.type_size(elem_ty); + let value = self.eval_operand(operand)?; for i in 0..length { let elem_dest = dest.offset((i * elem_size) as isize); - self.memory.copy(src, elem_dest, elem_size, elem_align)?; + self.write_value(value, elem_dest, elem_ty)?; } } @@ -604,7 +592,7 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' use rustc::mir::repr::CastKind::*; match kind { Unsize => { - let src = self.eval_operand(operand)?; + let src = self.eval_operand_to_ptr(operand)?; let src_ty = self.operand_ty(operand); let dest_ty = self.monomorphize(dest_ty, self.substs()); assert!(self.type_is_fat_ptr(dest_ty)); @@ -637,7 +625,7 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' } Misc => { - let src = self.eval_operand(operand)?; + let src = self.eval_operand_to_ptr(operand)?; let src_ty = self.operand_ty(operand); if self.type_is_fat_ptr(src_ty) { let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); @@ -671,7 +659,7 @@ fn eval_assignment(&mut self, lvalue: &mir::Lvalue<'tcx>, rvalue: &mir::Rvalue<' UnsafeFnPointer => match dest_ty.sty { ty::TyFnPtr(unsafe_fn_ty) => { - let src = self.eval_operand(operand)?; + let src = self.eval_operand_to_ptr(operand)?; let ptr = self.memory.read_ptr(src)?; let (def_id, substs, _) = self.memory.get_fn(ptr.alloc_id)?; let fn_ptr = self.memory.create_fn_ptr(def_id, substs, unsafe_fn_ty); @@ -761,36 +749,68 @@ fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, } } - fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { + // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can + // remove it as soon as PrimVal can represent fat pointers. + fn eval_operand_to_ptr(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Pointer> { + let value = self.eval_operand(op)?; + match value { + Value::Ptr(ptr) => Ok(ptr), + Value::Prim(primval) => { + let ty = self.operand_ty(op); + let size = self.type_size(ty); + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; + self.memory.write_primval(ptr, primval)?; + Ok(ptr) + } + } + } + + fn eval_operand_to_primval(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, PrimVal> { + let value = self.eval_operand(op)?; + let ty = self.operand_ty(op); + self.value_to_primval(value, ty) + } + + fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => Ok(self.eval_lvalue(lvalue)?.to_ptr()), + Consume(ref lvalue) => Ok(Value::Ptr(self.eval_lvalue(lvalue)?.to_ptr())), + Constant(mir::Constant { ref literal, ty, .. }) => { - use rustc::mir::repr::Literal::*; - match *literal { - Value { ref value } => Ok(self.const_to_ptr(value)?), - Item { def_id, substs } => { + use rustc::mir::repr::Literal; + let value = match *literal { + Literal::Value { ref value } => self.const_to_value(value)?, + + Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { // function items are zero sized - Ok(self.memory.allocate(0, 0)?) + Value::Ptr(self.memory.allocate(0, 0)?) } else { let cid = ConstantId { def_id: def_id, substs: substs, kind: ConstantKind::Global, }; - Ok(*self.statics.get(&cid).expect("static should have been cached (rvalue)")) + let static_ptr = *self.statics.get(&cid) + .expect("static should have been cached (rvalue)"); + Value::Ptr(static_ptr) } - }, - Promoted { index } => { + } + + Literal::Promoted { index } => { let cid = ConstantId { def_id: self.frame().def_id, substs: self.substs(), kind: ConstantKind::Promoted(index), }; - Ok(*self.statics.get(&cid).expect("a promoted constant hasn't been precomputed")) - }, - } + let static_ptr = *self.statics.get(&cid) + .expect("a promoted constant hasn't been precomputed"); + Value::Ptr(static_ptr) + } + }; + + Ok(value) } } } @@ -885,7 +905,7 @@ fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue ty::TySlice(elem_ty) => self.type_size(elem_ty), _ => bug!("indexing expected an array or slice, got {:?}", base_ty), }; - let n_ptr = self.eval_operand(operand)?; + let n_ptr = self.eval_operand_to_ptr(operand)?; let n = self.memory.read_usize(n_ptr)?; base.ptr.offset(n as isize * elem_size as isize) } @@ -920,6 +940,22 @@ fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tc Ok(()) } + fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + match value { + Value::Ptr(ptr) => self.read_primval(ptr, ty), + + // TODO(solson): Sanity-check the primval type against the input type. + Value::Prim(primval) => Ok(primval), + } + } + + fn write_value(&mut self, value: Value, dest: Pointer, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + match value { + Value::Ptr(ptr) => self.move_(ptr, dest, dest_ty), + Value::Prim(primval) => self.memory.write_primval(dest, primval), + } + } + pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use syntax::ast::{IntTy, UintTy, FloatTy}; let val = match (self.memory.pointer_size(), &ty.sty) { diff --git a/src/interpreter/step.rs b/src/interpreter/step.rs index 7eaf92c9b5ac..6f8a297a6d8f 100644 --- a/src/interpreter/step.rs +++ b/src/interpreter/step.rs @@ -77,7 +77,7 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx, ()> { use rustc::mir::repr::StatementKind::*; match stmt.kind { - Assign(ref lvalue, ref rvalue) => self.eval_assignment(lvalue, rvalue)?, + Assign(ref lvalue, ref rvalue) => self.eval_rvalue_into_lvalue(rvalue, lvalue)?, SetDiscriminant { .. } => unimplemented!(), // Miri can safely ignore these. Only translation needs them. diff --git a/src/interpreter/terminator.rs b/src/interpreter/terminator.rs index d8edad79f1eb..9914fcba4670 100644 --- a/src/interpreter/terminator.rs +++ b/src/interpreter/terminator.rs @@ -1,18 +1,19 @@ use rustc::hir::def_id::DefId; +use rustc::middle::const_val::ConstVal; use rustc::mir::repr as mir; use rustc::traits::{self, Reveal}; use rustc::ty::fold::TypeFoldable; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TyCtxt, BareFnTy}; -use std::rc::Rc; use std::iter; -use syntax::{ast, attr}; +use std::rc::Rc; use syntax::codemap::{DUMMY_SP, Span}; +use syntax::{ast, attr}; -use super::{EvalContext, IntegerExt, StackPopCleanup}; use error::{EvalError, EvalResult}; use memory::Pointer; +use super::{EvalContext, IntegerExt, StackPopCleanup}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { @@ -32,8 +33,8 @@ pub(super) fn eval_terminator( Goto { target } => self.goto_block(target), If { ref cond, targets: (then_target, else_target) } => { - let cond_ptr = self.eval_operand(cond)?; - let cond_val = self.memory.read_bool(cond_ptr)?; + let cond_val = self.eval_operand_to_primval(cond)? + .expect_bool("TerminatorKind::If condition constant was not a bool"); self.goto_block(if cond_val { then_target } else { else_target }); } @@ -54,9 +55,11 @@ pub(super) fn eval_terminator( // Branch to the `otherwise` case by default, if no match is found. let mut target_block = targets[targets.len() - 1]; - for (index, val_const) in values.iter().enumerate() { - let ptr = self.const_to_ptr(val_const)?; - let val = self.memory.read_uint(ptr, discr_size)?; + for (index, const_val) in values.iter().enumerate() { + let val = match const_val { + &ConstVal::Integral(i) => i.to_u64_unchecked(), + _ => bug!("TerminatorKind::SwitchInt branch constant was not an integer"), + }; if discr_val == val { target_block = targets[index]; break; @@ -88,7 +91,7 @@ pub(super) fn eval_terminator( let func_ty = self.operand_ty(func); match func_ty.sty { ty::TyFnPtr(bare_fn_ty) => { - let ptr = self.eval_operand(func)?; + let ptr = self.eval_operand_to_ptr(func)?; let fn_ptr = self.memory.read_ptr(ptr)?; let (def_id, substs, fn_ty) = self.memory.get_fn(fn_ptr.alloc_id)?; if fn_ty != bare_fn_ty { @@ -114,15 +117,16 @@ pub(super) fn eval_terminator( } Assert { ref cond, expected, ref msg, target, .. } => { - let cond_ptr = self.eval_operand(cond)?; - if expected == self.memory.read_bool(cond_ptr)? { + let cond_val = self.eval_operand_to_primval(cond)? + .expect_bool("TerminatorKind::Assert condition constant was not a bool"); + if expected == cond_val { self.goto_block(target); } else { return match *msg { mir::AssertMessage::BoundsCheck { ref len, ref index } => { - let len = self.eval_operand(len).expect("can't eval len"); + let len = self.eval_operand_to_ptr(len).expect("can't eval len"); let len = self.memory.read_usize(len).expect("can't read len"); - let index = self.eval_operand(index).expect("can't eval index"); + let index = self.eval_operand_to_ptr(index).expect("can't eval index"); let index = self.memory.read_usize(index).expect("can't read index"); Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index)) }, @@ -174,7 +178,7 @@ fn eval_fn_call( let mut arg_srcs = Vec::new(); for arg in args { - let src = self.eval_operand(arg)?; + let src = self.eval_operand_to_ptr(arg)?; let src_ty = self.operand_ty(arg); arg_srcs.push((src, src_ty)); } @@ -271,8 +275,9 @@ fn call_intrinsic( dest: Pointer, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { + // TODO(solson): We can probably remove this _to_ptr easily. let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand(arg)) + .map(|arg| self.eval_operand_to_ptr(arg)) .collect(); let args_ptrs = args_res?; let pointer_size = self.memory.pointer_size(); @@ -422,8 +427,9 @@ fn call_c_abi( None => name.as_str(), }; + // TODO(solson): We can probably remove this _to_ptr easily. let args_res: EvalResult> = args.iter() - .map(|arg| self.eval_operand(arg)) + .map(|arg| self.eval_operand_to_ptr(arg)) .collect(); let args = args_res?; diff --git a/src/primval.rs b/src/primval.rs index 6b24bf7530f8..267922204af6 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -20,6 +20,15 @@ pub enum PrimVal { F32(f32), F64(f64), } +impl PrimVal { + pub fn expect_bool(self, error_msg: &str) -> bool { + match self { + PrimVal::Bool(b) => b, + _ => bug!("{}", error_msg), + } + } +} + /// returns the result of the operation and whether the operation overflowed pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::repr::BinOp::*; diff --git a/tests/compile-fail/oom2.rs b/tests/compile-fail/oom2.rs index d0344e4faeb3..ac4b3a667448 100644 --- a/tests/compile-fail/oom2.rs +++ b/tests/compile-fail/oom2.rs @@ -30,6 +30,13 @@ fn bar(i: i32) { //~|NOTE inside call to bar //~|NOTE inside call to bar //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar + //~|NOTE inside call to bar } }