diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 6227999569c..4f31cc21033 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -28,6 +28,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { U64(u) => self.cast_const_int(u, ty, false), FnPtr(ptr) | Ptr(ptr) => self.cast_ptr(ptr, ty), + VtablePtr(..) | SlicePtr(..) => unimplemented!(), } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9de72dc6225..c30c47961da 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -17,6 +17,7 @@ use syntax::codemap::{self, DUMMY_SP}; use error::{EvalError, EvalResult}; use memory::{Memory, Pointer, AllocId}; use primval::{self, PrimVal}; +use self::value::Value; use std::collections::HashMap; @@ -24,6 +25,7 @@ mod step; mod terminator; mod cast; mod vtable; +mod value; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -99,21 +101,6 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } -/// A `Value` represents a single self-contained Rust value. -/// -/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve -/// value held directly, outside of any allocation (`ByVal`). -/// -/// For optimization of a few very common cases, there is also a representation for a pair of -/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary -/// operations and fat pointers. This idea was taken from rustc's trans. -#[derive(Clone, Copy, Debug)] -enum Value { - ByRef(Pointer), - ByVal(PrimVal), - ByValPair(PrimVal, PrimVal), -} - #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Lvalue { ptr: Pointer, @@ -245,10 +232,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = self.memory.allocate(s.len(), 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Value::ByValPair( - PrimVal::Ptr(ptr), - self.target_usize_primval(s.len() as u64) - ) + Value::ByVal(PrimVal::SlicePtr(ptr, s.len() as u64)) } ByteStr(ref bs) => { @@ -618,13 +602,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::repr::CastKind::*; match kind { Unsize => { - let src = self.eval_operand_to_ptr(operand)?; + let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); let dest_ty = self.monomorphize(dest_ty, self.substs()); // FIXME: cases where dest_ty is not a fat pointer. e.g. Arc -> Arc assert!(self.type_is_fat_ptr(dest_ty)); let (ptr, extra) = self.get_fat_ptr(dest); - self.move_(src, ptr, src_ty)?; + self.move_value(src, ptr, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); @@ -639,9 +623,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. - let (_, src_extra) = self.get_fat_ptr(src); - let src_extra = self.memory.read_ptr(src_extra)?; - self.memory.write_ptr(extra, src_extra)?; + let src_extra = src.expect_fat_ptr_extra(&self.memory)?; + self.memory.write_primval(extra, src_extra)?; }, (_, &ty::TyTrait(ref data)) => { let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); @@ -655,25 +638,36 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Misc => { - let src = self.eval_operand_to_ptr(operand)?; + let src = self.eval_operand(operand)?; let src_ty = self.operand_ty(operand); + // FIXME: dest_ty should already be monomorphized + let dest_ty = self.monomorphize(dest_ty, self.substs()); if self.type_is_fat_ptr(src_ty) { - let (data_ptr, _meta_ptr) = self.get_fat_ptr(src); + trace!("misc cast: {:?}", src); let ptr_size = self.memory.pointer_size(); - let dest_ty = self.monomorphize(dest_ty, self.substs()); - if self.type_is_fat_ptr(dest_ty) { - // FIXME: add assertion that the extra part of the src_ty and - // dest_ty is of the same type - self.memory.copy(data_ptr, dest, ptr_size * 2, ptr_size)?; - } else { // cast to thin-ptr - // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and - // pointer-cast of that pointer to desired pointer type. - self.memory.copy(data_ptr, dest, ptr_size, ptr_size)?; + match (src, self.type_is_fat_ptr(dest_ty)) { + (Value::ByVal(PrimVal::VtablePtr(data, meta)), true) => { + self.memory.write_ptr(dest, data)?; + self.memory.write_ptr(dest.offset(ptr_size as isize), meta)?; + }, + (Value::ByVal(PrimVal::SlicePtr(data, meta)), true) => { + self.memory.write_ptr(dest, data)?; + self.memory.write_usize(dest.offset(ptr_size as isize), meta)?; + }, + (Value::ByVal(PrimVal::SlicePtr(data, _)), false) | + (Value::ByVal(PrimVal::VtablePtr(data, _)), false) => { + self.memory.write_ptr(dest, data)?; + }, + (Value::ByRef(ptr), true) => { + self.memory.copy(ptr, dest, ptr_size * 2, ptr_size)?; + }, + (Value::ByRef(ptr), false) => { + self.memory.copy(ptr, dest, ptr_size, ptr_size)?; + }, + (Value::ByVal(_), _) => bug!("expected fat ptr"), } } else { - // FIXME: dest_ty should already be monomorphized - let dest_ty = self.monomorphize(dest_ty, self.substs()); - let src_val = self.read_primval(src, src_ty)?; + let src_val = self.value_to_primval(src, src_ty)?; let dest_val = self.cast_primval(src_val, dest_ty)?; self.memory.write_primval(dest, dest_val)?; } @@ -689,8 +683,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { UnsafeFnPointer => match dest_ty.sty { ty::TyFnPtr(unsafe_fn_ty) => { - let src = self.eval_operand_to_ptr(operand)?; - let ptr = self.memory.read_ptr(src)?; + let src = self.eval_operand(operand)?; + let ptr = src.read_ptr(&self.memory)?; 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); self.memory.write_ptr(dest, fn_ptr)?; @@ -779,14 +773,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - // 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)?; - let ty = self.operand_ty(op); - self.value_to_ptr(value, ty) - } - 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); @@ -857,6 +843,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; + trace!("projection base: {:?}", base); + trace!("projection: {:?}", proj.elem); let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); @@ -937,8 +925,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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_to_ptr(operand)?; - let n = self.memory.read_usize(n_ptr)?; + let n_ptr = self.eval_operand(operand)?; + let usize = self.tcx.types.usize; + let n = self.value_to_primval(n_ptr, usize)?.expect_uint("Projection::Index expected usize"); base.ptr.offset(n as isize * elem_size as isize) } @@ -965,6 +954,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(&self.mir(), self.tcx), self.substs()) } + fn move_value(&mut self, src: Value, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { + match src { + Value::ByRef(ptr) => self.move_(ptr, dest, ty), + Value::ByVal(val) => self.memory.write_primval(dest, val), + } + } + fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> { let size = self.type_size(ty); let align = self.type_align(ty); @@ -974,7 +970,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson): This method unnecessarily allocates and should not be necessary. We can // remove it as soon as PrimVal can represent fat pointers. - fn value_to_ptr(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { + fn value_to_ptr_dont_use(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Pointer> { match value { Value::ByRef(ptr) => Ok(ptr), @@ -985,18 +981,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(ptr, primval)?; Ok(ptr) } - - Value::ByValPair(primval1, primval2) => { - let size = self.type_size(ty); - let align = self.type_align(ty); - let ptr = self.memory.allocate(size, align)?; - - // FIXME(solson): Major dangerous assumptions here. Ideally obliterate this - // function. - self.memory.write_primval(ptr, primval1)?; - self.memory.write_primval(ptr.offset((size / 2) as isize), primval2)?; - Ok(ptr) - } } } @@ -1006,7 +990,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // TODO(solson): Sanity-check the primval type against the input type. Value::ByVal(primval) => Ok(primval), - Value::ByValPair(..) => bug!("can't turn a ByValPair into a single PrimVal"), } } @@ -1019,14 +1002,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match value { Value::ByRef(ptr) => self.move_(ptr, dest, dest_ty), Value::ByVal(primval) => self.memory.write_primval(dest, primval), - Value::ByValPair(primval1, primval2) => { - let size = self.type_size(dest_ty); - - // FIXME(solson): Major dangerous assumptions here. - self.memory.write_primval(dest, primval1)?; - self.memory.write_primval(dest.offset((size / 2) as isize), primval2)?; - Ok(()) - } } } diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 38b62254130..94ab3014ffd 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -7,7 +7,8 @@ use rustc::ty; use error::{EvalError, EvalResult}; use memory::Pointer; use interpreter::EvalContext; -use primval; +use primval::{self, PrimVal}; +use interpreter::value::Value; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -18,12 +19,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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_to_ptr(arg)) + let args_ptrs: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) .collect(); - let args_ptrs = args_res?; + let args_ptrs = args_ptrs?; let pointer_size = self.memory.pointer_size(); + let i32 = self.tcx.types.i32; + let isize = self.tcx.types.isize; + let usize = self.tcx.types.usize; + let f32 = self.tcx.types.f32; + let f64 = self.tcx.types.f64; match &self.tcx.item_name(def_id).as_str()[..] { "add_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Add, &args[0], &args[1], dest, dest_layout)?, @@ -31,14 +36,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "mul_with_overflow" => self.intrinsic_with_overflow(mir::BinOp::Mul, &args[0], &args[1], dest, dest_layout)?, "arith_offset" => { - let ptr = self.memory.read_ptr(args_ptrs[0])?; - let offset = self.memory.read_int(args_ptrs[1], pointer_size)?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; + let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("arith_offset second arg not isize"); let new_ptr = ptr.offset(offset as isize); self.memory.write_ptr(dest, new_ptr)?; } "assume" => { - if !self.memory.read_bool(args_ptrs[0])? { + let bool = self.tcx.types.bool; + if !self.value_to_primval(args_ptrs[0], bool)?.expect_bool("assume arg not bool") { return Err(EvalError::AssumptionNotHeld); } } @@ -51,47 +57,59 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); let elem_align = self.type_align(elem_ty); - let src = self.memory.read_ptr(args_ptrs[0])?; - let dest = self.memory.read_ptr(args_ptrs[1])?; - let count = self.memory.read_isize(args_ptrs[2])?; + let src = args_ptrs[0].read_ptr(&self.memory)?; + let dest = args_ptrs[1].read_ptr(&self.memory)?; + let count = self.value_to_primval(args_ptrs[2], usize)?.expect_uint("arith_offset second arg not isize"); self.memory.copy(src, dest, count as usize * elem_size, elem_align)?; } "ctpop" => { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.count_ones(); + let num = self.value_to_primval(args_ptrs[2], elem_ty)?.expect_int("ctpop second arg not integral"); + let num = num.count_ones(); self.memory.write_uint(dest, num.into(), elem_size)?; } "ctlz" => { let elem_ty = substs.type_at(0); let elem_size = self.type_size(elem_ty); - let num = self.memory.read_uint(args_ptrs[0], elem_size)?.leading_zeros(); + let num = self.value_to_primval(args_ptrs[2], elem_ty)?; + let num = match num { + PrimVal::I8(i) => i.leading_zeros(), + PrimVal::U8(i) => i.leading_zeros(), + PrimVal::I16(i) => i.leading_zeros(), + PrimVal::U16(i) => i.leading_zeros(), + PrimVal::I32(i) => i.leading_zeros(), + PrimVal::U32(i) => i.leading_zeros(), + PrimVal::I64(i) => i.leading_zeros(), + PrimVal::U64(i) => i.leading_zeros(), + _ => bug!("ctlz called with non-integer type"), + }; self.memory.write_uint(dest, num.into(), elem_size)?; } "discriminant_value" => { let ty = substs.type_at(0); - let adt_ptr = self.memory.read_ptr(args_ptrs[0])?; + let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?; let discr_val = self.read_discriminant_value(adt_ptr, ty)?; self.memory.write_uint(dest, discr_val, 8)?; } "fabsf32" => { - let f = self.memory.read_f32(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[2], f32)?.expect_f32("fabsf32 read non f32"); self.memory.write_f32(dest, f.abs())?; } "fabsf64" => { - let f = self.memory.read_f64(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[2], f64)?.expect_f64("fabsf64 read non f64"); self.memory.write_f64(dest, f.abs())?; } "fadd_fast" => { let ty = substs.type_at(0); - let a = self.read_primval(args_ptrs[0], ty)?; - let b = self.read_primval(args_ptrs[0], ty)?; + let a = self.value_to_primval(args_ptrs[0], ty)?; + let b = self.value_to_primval(args_ptrs[0], ty)?; let result = primval::binary_op(mir::BinOp::Add, a, b)?; self.memory.write_primval(dest, result.0)?; } @@ -117,8 +135,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); - let ptr = self.memory.read_ptr(args_ptrs[0])?; - self.move_(args_ptrs[1], ptr, ty)?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; + self.move_value(args_ptrs[1], ptr, ty)?; } "needs_drop" => { @@ -129,10 +147,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "offset" => { let pointee_ty = substs.type_at(0); let pointee_size = self.type_size(pointee_ty) as isize; - let ptr_arg = args_ptrs[0]; - let offset = self.memory.read_isize(args_ptrs[1])?; + let offset = self.value_to_primval(args_ptrs[1], isize)?.expect_int("offset second arg not isize"); - let ptr = self.memory.read_ptr(ptr_arg)?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; let result_ptr = ptr.offset(offset as isize * pointee_size); self.memory.write_ptr(dest, result_ptr)?; } @@ -150,24 +167,24 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } "powif32" => { - let f = self.memory.read_f32(args_ptrs[0])?; - let i = self.memory.read_int(args_ptrs[1], 4)?; + let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("powif32 first arg not f32"); + let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif32 second arg not i32"); self.memory.write_f32(dest, f.powi(i as i32))?; } "powif64" => { - let f = self.memory.read_f32(args_ptrs[0])?; - let i = self.memory.read_int(args_ptrs[1], 4)?; - self.memory.write_f32(dest, f.powi(i as i32))?; + let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("powif64 first arg not f64"); + let i = self.value_to_primval(args_ptrs[1], i32)?.expect_int("powif64 second arg not i32"); + self.memory.write_f64(dest, f.powi(i as i32))?; } "sqrtf32" => { - let f = self.memory.read_f32(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[0], f32)?.expect_f32("sqrtf32 first arg not f32"); self.memory.write_f32(dest, f.sqrt())?; } "sqrtf64" => { - let f = self.memory.read_f64(args_ptrs[0])?; + let f = self.value_to_primval(args_ptrs[0], f64)?.expect_f64("sqrtf64 first arg not f64"); self.memory.write_f64(dest, f.sqrt())?; } @@ -198,7 +215,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let ty = substs.type_at(0); - self.move_(args_ptrs[0], dest, ty)?; + self.move_value(args_ptrs[0], dest, ty)?; } "try" => unimplemented!(), @@ -207,14 +224,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "volatile_load" => { let ty = substs.type_at(0); - let ptr = self.memory.read_ptr(args_ptrs[0])?; + let ptr = args_ptrs[0].read_ptr(&self.memory)?; self.move_(ptr, dest, ty)?; } "volatile_store" => { let ty = substs.type_at(0); - let dest = self.memory.read_ptr(args_ptrs[0])?; - self.move_(args_ptrs[1], dest, ty)?; + let dest = args_ptrs[0].read_ptr(&self.memory)?; + self.move_value(args_ptrs[1], dest, ty)?; } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), @@ -229,7 +246,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn size_and_align_of_dst( &self, ty: ty::Ty<'tcx>, - value: Pointer, + value: Value, ) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); if self.type_is_sized(ty) { @@ -306,8 +323,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } ty::TyTrait(..) => { - let (_, vtable) = self.get_fat_ptr(value); - let vtable = self.memory.read_ptr(vtable)?; + let vtable = value.expect_vtable(&self.memory)?; // the second entry in the vtable is the dynamic size of the object. let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?; let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?; @@ -317,10 +333,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TySlice(_) | ty::TyStr => { let elem_ty = ty.sequence_element_type(self.tcx); let elem_size = self.type_size(elem_ty) as u64; - let (_, len_ptr) = self.get_fat_ptr(value); - let n = self.memory.read_usize(len_ptr)?; + let len = value.expect_slice_len(&self.memory)?; let align = self.type_align(elem_ty); - Ok((n * elem_size, align as u64)) + Ok((len * elem_size, align as u64)) } _ => bug!("size_of_val::<{:?}>", ty), diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 10273881b67..3a4e2f5ff84 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -14,7 +14,8 @@ use syntax::{ast, attr}; use error::{EvalError, EvalResult}; use memory::Pointer; use primval::PrimVal; -use super::{EvalContext, IntegerExt, StackPopCleanup, Value}; +use super::{EvalContext, IntegerExt, StackPopCleanup}; +use super::value::Value; mod intrinsics; @@ -265,9 +266,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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_to_ptr(arg)) + let args_res: EvalResult> = args.iter() + .map(|arg| self.eval_operand(arg)) .collect(); let args = args_res?; @@ -276,26 +276,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Ok(()); } + let usize = self.tcx.types.usize; + match &link_name[..] { "__rust_allocate" => { - let size = self.memory.read_usize(args[0])?; - let align = self.memory.read_usize(args[1])?; + let size = self.value_to_primval(args[0], usize)?.expect_uint("__rust_allocate first arg not usize"); + let align = self.value_to_primval(args[1], usize)?.expect_uint("__rust_allocate second arg not usize"); let ptr = self.memory.allocate(size as usize, align as usize)?; self.memory.write_ptr(dest, ptr)?; } "__rust_reallocate" => { - let ptr = self.memory.read_ptr(args[0])?; - let size = self.memory.read_usize(args[2])?; - let align = self.memory.read_usize(args[3])?; + let ptr = args[0].read_ptr(&self.memory)?; + let size = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate third arg not usize"); + let align = self.value_to_primval(args[3], usize)?.expect_uint("__rust_reallocate fourth arg not usize"); let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?; self.memory.write_ptr(dest, new_ptr)?; } "memcmp" => { - let left = self.memory.read_ptr(args[0])?; - let right = self.memory.read_ptr(args[1])?; - let n = self.memory.read_usize(args[2])? as usize; + let left = args[0].read_ptr(&self.memory)?; + let right = args[1].read_ptr(&self.memory)?; + let n = self.value_to_primval(args[2], usize)?.expect_uint("__rust_reallocate first arg not usize") as usize; let result = { let left_bytes = self.memory.read_bytes(left, n)?; @@ -419,7 +421,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // intermediate function call. // FIXME: this is a memory leak, should probably add the pointer to the // current stack. - let first = self.value_to_ptr(args[0].0, args[0].1)?; + let first = self.value_to_ptr_dont_use(args[0].0, args[0].1)?; args[0].0 = Value::ByVal(PrimVal::Ptr(first)); args[0].1 = self.tcx.mk_mut_ptr(args[0].1); } @@ -442,11 +444,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { traits::VtableObject(ref data) => { let idx = self.tcx.get_vtable_index_of_object_method(data, def_id); if let Some(&mut(ref mut first_arg, ref mut first_ty)) = args.get_mut(0) { - // FIXME(solson): Remove this allocating hack. - let ptr = self.value_to_ptr(*first_arg, *first_ty)?; - *first_arg = Value::ByRef(ptr); - let (_, vtable) = self.get_fat_ptr(ptr); - let vtable = self.memory.read_ptr(vtable)?; + let vtable = first_arg.expect_vtable(&self.memory)?; let idx = idx + 3; let offset = idx * self.memory.pointer_size(); let fn_ptr = self.memory.read_ptr(vtable.offset(offset as isize))?; diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs new file mode 100644 index 00000000000..0237afd06a3 --- /dev/null +++ b/src/interpreter/value.rs @@ -0,0 +1,60 @@ +use memory::{Memory, Pointer}; +use error::EvalResult; +use primval::PrimVal; + +/// A `Value` represents a single self-contained Rust value. +/// +/// A `Value` can either refer to a block of memory inside an allocation (`ByRef`) or to a primitve +/// value held directly, outside of any allocation (`ByVal`). +/// +/// For optimization of a few very common cases, there is also a representation for a pair of +/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary +/// operations and fat pointers. This idea was taken from rustc's trans. +#[derive(Clone, Copy, Debug)] +pub(super) enum Value { + ByRef(Pointer), + ByVal(PrimVal), +} + +impl Value { + pub(super) fn read_ptr<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_ptr(ptr), + ByVal(PrimVal::Ptr(ptr)) | + ByVal(PrimVal::FnPtr(ptr)) => Ok(ptr), + ByVal(_other) => unimplemented!(), + } + } + + pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_ptr(ptr.offset(mem.pointer_size() as isize)), + ByVal(PrimVal::VtablePtr(_, vtable)) => Ok(vtable), + _ => unimplemented!(), + } + } + + pub(super) fn expect_slice_len<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, u64> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), + ByVal(PrimVal::SlicePtr(_, len)) => Ok(len), + _ => unimplemented!(), + } + } + + pub(super) fn expect_fat_ptr_extra<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> { + use self::Value::*; + match (*self, mem.pointer_size()) { + (ByRef(ptr), size) => mem.read_ptr(ptr.offset(size as isize)).map(PrimVal::Ptr), + (ByVal(PrimVal::SlicePtr(_, len)), 8) => Ok(PrimVal::U64(len)), + (ByVal(PrimVal::SlicePtr(_, len)), 4) => Ok(PrimVal::U32(len as u32)), + (ByVal(PrimVal::SlicePtr(_, len)), 2) => Ok(PrimVal::U16(len as u16)), + (ByVal(PrimVal::SlicePtr(_, len)), 1) => Ok(PrimVal::U8(len as u8)), + (ByVal(PrimVal::VtablePtr(_, ptr)), _) => Ok(PrimVal::Ptr(ptr)), + _ => unimplemented!(), + } + } +} diff --git a/src/memory.rs b/src/memory.rs index 27e6ffff00e..d1c0c740843 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -55,6 +55,9 @@ impl Pointer { pub fn points_to_zst(&self) -> bool { self.alloc_id == ZST_ALLOC_ID } + pub fn to_int(&self) -> usize { + self.offset + } pub fn from_int(i: usize) -> Self { Pointer { alloc_id: ZST_ALLOC_ID, @@ -543,6 +546,20 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { PrimVal::F64(f) => self.write_f64(ptr, f), PrimVal::FnPtr(p) | PrimVal::Ptr(p) => self.write_ptr(ptr, p), + PrimVal::VtablePtr(p, v) => { + assert_eq!(layout::FAT_PTR_ADDR, 0); + assert_eq!(layout::FAT_PTR_EXTRA, 1); + self.write_ptr(ptr, p)?; + let vptr = ptr.offset(self.pointer_size() as isize); + self.write_ptr(vptr, v) + } + PrimVal::SlicePtr(p, n) => { + assert_eq!(layout::FAT_PTR_ADDR, 0); + assert_eq!(layout::FAT_PTR_EXTRA, 1); + self.write_ptr(ptr, p)?; + let nptr = ptr.offset(self.pointer_size() as isize); + self.write_usize(nptr, n) + } } } diff --git a/src/primval.rs b/src/primval.rs index 717ad99dbcd..82e26162524 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,6 +14,8 @@ pub enum PrimVal { Ptr(Pointer), FnPtr(Pointer), + VtablePtr(Pointer, Pointer), + SlicePtr(Pointer, u64), Char(char), F32(f32), F64(f64), @@ -32,7 +34,10 @@ macro_rules! declare_expect_fn { impl PrimVal { declare_expect_fn!(expect_bool, Bool, bool); + declare_expect_fn!(expect_f32, F32, f32); + declare_expect_fn!(expect_f64, F64, f64); declare_expect_fn!(expect_fn_ptr, FnPtr, Pointer); + declare_expect_fn!(expect_ptr, Ptr, Pointer); pub fn expect_uint(self, error_msg: &str) -> u64 { use self::PrimVal::*; @@ -41,6 +46,19 @@ impl PrimVal { U16(u) => u as u64, U32(u) => u as u64, U64(u) => u, + Ptr(ptr) => ptr.to_int() as u64, + _ => bug!("{}", error_msg), + } + } + + pub fn expect_int(self, error_msg: &str) -> i64 { + use self::PrimVal::*; + match self { + I8(i) => i as i64, + I16(i) => i as i64, + I32(i) => i as i64, + I64(i) => i, + Ptr(ptr) => ptr.to_int() as i64, _ => bug!("{}", error_msg), } } diff --git a/tests/run-pass/issue-33387.rs b/tests/run-pass/issue-33387.rs new file mode 100644 index 00000000000..edbf2b81ce9 --- /dev/null +++ b/tests/run-pass/issue-33387.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::sync::Arc; + +trait Foo {} + +impl Foo for [u8; 2] {} + +fn main() { + let _: Arc = Arc::new([3, 4]); +}