From 2282289ad5fbfaaaffd6b4fe4ceb6665ab9ab391 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 10:27:14 +0200 Subject: [PATCH 01/12] refactor away intermediate allocations, stage1 --- src/interpreter/cast.rs | 1 + src/interpreter/mod.rs | 119 +++++++++-------------- src/interpreter/terminator/intrinsics.rs | 95 ++++++++++-------- src/interpreter/terminator/mod.rs | 34 +++---- src/interpreter/value.rs | 60 ++++++++++++ src/memory.rs | 17 ++++ src/primval.rs | 18 ++++ tests/run-pass/issue-33387.rs | 19 ++++ 8 files changed, 233 insertions(+), 130 deletions(-) create mode 100644 src/interpreter/value.rs create mode 100644 tests/run-pass/issue-33387.rs 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]); +} From 7714cccf2657151b634ae0cdcf908573e838e4d7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 10:38:30 +0200 Subject: [PATCH 02/12] implement "type_name" intrinsic --- src/interpreter/mod.rs | 45 +++++++++++++----------- src/interpreter/terminator/intrinsics.rs | 8 ++--- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c30c47961da..dcca9605b1a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -204,42 +204,45 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, Value> { + pub fn str_to_primval(&mut self, s: &str) -> EvalResult<'tcx, PrimVal> { + // FIXME: cache these allocs + let ptr = self.memory.allocate(s.len(), 1)?; + self.memory.write_bytes(ptr, s.as_bytes())?; + self.memory.freeze(ptr.alloc_id)?; + Ok(PrimVal::SlicePtr(ptr, s.len() as u64)) + } + + fn const_to_primval(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, PrimVal> { use rustc::middle::const_val::ConstVal::*; use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat}; let primval = match *const_val { - Integral(ConstInt::I8(i)) => Value::ByVal(PrimVal::I8(i)), - Integral(ConstInt::U8(i)) => Value::ByVal(PrimVal::U8(i)), + Integral(ConstInt::I8(i)) => PrimVal::I8(i), + Integral(ConstInt::U8(i)) => PrimVal::U8(i), Integral(ConstInt::Isize(ConstIsize::Is16(i))) | - Integral(ConstInt::I16(i)) => Value::ByVal(PrimVal::I16(i)), + Integral(ConstInt::I16(i)) => PrimVal::I16(i), Integral(ConstInt::Usize(ConstUsize::Us16(i))) | - Integral(ConstInt::U16(i)) => Value::ByVal(PrimVal::U16(i)), + Integral(ConstInt::U16(i)) => PrimVal::U16(i), Integral(ConstInt::Isize(ConstIsize::Is32(i))) | - Integral(ConstInt::I32(i)) => Value::ByVal(PrimVal::I32(i)), + Integral(ConstInt::I32(i)) => PrimVal::I32(i), Integral(ConstInt::Usize(ConstUsize::Us32(i))) | - Integral(ConstInt::U32(i)) => Value::ByVal(PrimVal::U32(i)), + Integral(ConstInt::U32(i)) => PrimVal::U32(i), Integral(ConstInt::Isize(ConstIsize::Is64(i))) | - Integral(ConstInt::I64(i)) => Value::ByVal(PrimVal::I64(i)), + Integral(ConstInt::I64(i)) => PrimVal::I64(i), Integral(ConstInt::Usize(ConstUsize::Us64(i))) | - Integral(ConstInt::U64(i)) => Value::ByVal(PrimVal::U64(i)), - Float(ConstFloat::F32(f)) => Value::ByVal(PrimVal::F32(f)), - Float(ConstFloat::F64(f)) => Value::ByVal(PrimVal::F64(f)), - Bool(b) => Value::ByVal(PrimVal::Bool(b)), - Char(c) => Value::ByVal(PrimVal::Char(c)), + Integral(ConstInt::U64(i)) => PrimVal::U64(i), + Float(ConstFloat::F32(f)) => PrimVal::F32(f), + Float(ConstFloat::F64(f)) => PrimVal::F64(f), + Bool(b) => PrimVal::Bool(b), + Char(c) => PrimVal::Char(c), - Str(ref s) => { - let ptr = self.memory.allocate(s.len(), 1)?; - self.memory.write_bytes(ptr, s.as_bytes())?; - self.memory.freeze(ptr.alloc_id)?; - Value::ByVal(PrimVal::SlicePtr(ptr, s.len() as u64)) - } + Str(ref s) => self.str_to_primval(s)?, ByteStr(ref bs) => { let ptr = self.memory.allocate(bs.len(), 1)?; self.memory.write_bytes(ptr, bs)?; self.memory.freeze(ptr.alloc_id)?; - Value::ByVal(PrimVal::Ptr(ptr)) + PrimVal::Ptr(ptr) } Struct(_) => unimplemented!(), @@ -787,7 +790,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; let value = match *literal { - Literal::Value { ref value } => self.const_to_value(value)?, + Literal::Value { ref value } => Value::ByVal(self.const_to_primval(value)?), Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 94ab3014ffd..103fe111fa3 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -199,14 +199,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?; self.memory.write_uint(dest, size, pointer_size)?; } - // FIXME: wait for eval_operand_to_ptr to be gone - /* "type_name" => { let ty = substs.type_at(0); let ty_name = ty.to_string(); - let s = self.str_to_value(&ty_name)?; - self.memory.write_ptr(dest, s)?; - }*/ + let s = self.str_to_primval(&ty_name)?; + self.memory.write_primval(dest, s)?; + } "type_id" => { let ty = substs.type_at(0); let n = self.tcx.type_id_hash(ty); From d743c0784ed487c3329010b77a935eb13b538816 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 15:23:01 +0200 Subject: [PATCH 03/12] clean up get_fat_ptr usage in Unsize --- src/interpreter/mod.rs | 11 +++++------ src/interpreter/value.rs | 13 ------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index dcca9605b1a..e381ebdba69 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -610,8 +610,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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_value(src, ptr, src_ty)?; let src_pointee_ty = pointee_type(src_ty).unwrap(); let dest_pointee_ty = pointee_type(dest_ty).unwrap(); @@ -620,20 +618,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { - self.memory.write_usize(extra, length as u64)?; + let ptr = src.read_ptr(&self.memory)?; + self.memory.write_primval(dest, PrimVal::SlicePtr(ptr, length as u64))?; } (&ty::TyTrait(_), &ty::TyTrait(_)) => { // For now, upcasts are limited to changes in marker // traits, and hence never actually require an actual // change to the vtable. - let src_extra = src.expect_fat_ptr_extra(&self.memory)?; - self.memory.write_primval(extra, src_extra)?; + self.write_value(src, dest, dest_ty)?; }, (_, &ty::TyTrait(ref data)) => { let trait_ref = data.principal.with_self_ty(self.tcx, src_pointee_ty); let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; - self.memory.write_ptr(extra, vtable)?; + let ptr = src.read_ptr(&self.memory)?; + self.memory.write_primval(dest, PrimVal::VtablePtr(ptr, vtable))?; }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 0237afd06a3..58805ee4c5c 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -44,17 +44,4 @@ impl Value { _ => 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!(), - } - } } From b3190359df5234e0b707f59635365be841bf8848 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 23 Sep 2016 15:48:23 +0200 Subject: [PATCH 04/12] refactor away `get_fat_ptr` --- src/interpreter/mod.rs | 47 +++++------- tests/run-pass/dst-struct.rs | 134 +++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 28 deletions(-) create mode 100644 tests/run-pass/dst-struct.rs diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index e381ebdba69..9e8c2996d26 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -579,15 +579,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ref(_, _, ref lvalue) => { let lv = self.eval_lvalue(lvalue)?; - let (ptr, extra) = self.get_fat_ptr(dest); - self.memory.write_ptr(ptr, lv.ptr)?; match lv.extra { - LvalueExtra::None => {}, + LvalueExtra::None => self.memory.write_ptr(dest, lv.ptr)?, LvalueExtra::Length(len) => { - self.memory.write_usize(extra, len)?; + self.memory.write_primval(dest, PrimVal::SlicePtr(lv.ptr, len))?; } LvalueExtra::Vtable(ptr) => { - self.memory.write_ptr(extra, ptr)?; + self.memory.write_primval(dest, PrimVal::VtablePtr(lv.ptr, ptr))?; }, LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), @@ -902,21 +900,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, Deref => { - let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer"); - let pointee_ty = self.tcx.struct_tail(pointee_ty); - let ptr = self.memory.read_ptr(base.ptr)?; - let extra = match pointee_ty.sty { - ty::TySlice(_) | ty::TyStr => { - let (_, extra) = self.get_fat_ptr(base.ptr); - let len = self.memory.read_usize(extra)?; - LvalueExtra::Length(len) - } - ty::TyTrait(_) => { - let (_, extra) = self.get_fat_ptr(base.ptr); - let vtable = self.memory.read_ptr(extra)?; - LvalueExtra::Vtable(vtable) - }, - _ => LvalueExtra::None, + let (ptr, extra) = match self.read_primval(base.ptr, base_ty)? { + PrimVal::SlicePtr(ptr, n) => (ptr, LvalueExtra::Length(n)), + PrimVal::VtablePtr(ptr, vptr) => (ptr, LvalueExtra::Vtable(vptr)), + PrimVal::Ptr(ptr) => (ptr, LvalueExtra::None), + _ => bug!("can't deref non pointer types"), }; return Ok(Lvalue { ptr: ptr, extra: extra }); } @@ -942,12 +930,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } - fn get_fat_ptr(&self, ptr: Pointer) -> (Pointer, Pointer) { - assert_eq!(layout::FAT_PTR_ADDR, 0); - assert_eq!(layout::FAT_PTR_EXTRA, 1); - (ptr, ptr.offset(self.memory.pointer_size() as isize)) - } - fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { self.monomorphize(lvalue.ty(&self.mir(), self.tcx).to_ty(self.tcx), self.substs()) } @@ -1038,12 +1020,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty)) }, &ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::FnPtr)?, + &ty::TyBox(ty) | &ty::TyRef(_, ty::TypeAndMut { ty, .. }) | &ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(ty) { - PrimVal::Ptr(self.memory.read_ptr(ptr)?) + PrimVal::Ptr(p) } else { - bug!("primitive read of fat pointer type: {:?}", ty); + // FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>` + let extra = ptr.offset(self.memory.pointer_size() as isize); + match self.tcx.struct_tail(ty).sty { + ty::TyTrait(..) => PrimVal::VtablePtr(p, self.memory.read_ptr(extra)?), + ty::TySlice(..) | + ty::TyStr => PrimVal::SlicePtr(p, self.memory.read_usize(extra)?), + _ => bug!("unsized primval ptr read from {:?}", ty), + } } } diff --git a/tests/run-pass/dst-struct.rs b/tests/run-pass/dst-struct.rs new file mode 100644 index 00000000000..932b571eccd --- /dev/null +++ b/tests/run-pass/dst-struct.rs @@ -0,0 +1,134 @@ +// Copyright 2014 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. + + +#![allow(unused_features)] +#![feature(box_syntax)] + +struct Fat { + f1: isize, + f2: &'static str, + ptr: T +} + +// x is a fat pointer +fn foo(x: &Fat<[isize]>) { + let y = &x.ptr; + assert_eq!(x.ptr.len(), 3); + assert_eq!(y[0], 1); + assert_eq!(x.ptr[1], 2); + assert_eq!(x.f1, 5); + assert_eq!(x.f2, "some str"); +} + +fn foo2(x: &Fat<[T]>) { + let y = &x.ptr; + let bar = Bar; + assert_eq!(x.ptr.len(), 3); + assert_eq!(y[0].to_bar(), bar); + assert_eq!(x.ptr[1].to_bar(), bar); + assert_eq!(x.f1, 5); + assert_eq!(x.f2, "some str"); +} + +fn foo3(x: &Fat>) { + let y = &x.ptr.ptr; + assert_eq!(x.f1, 5); + assert_eq!(x.f2, "some str"); + assert_eq!(x.ptr.f1, 8); + assert_eq!(x.ptr.f2, "deep str"); + assert_eq!(x.ptr.ptr.len(), 3); + assert_eq!(y[0], 1); + assert_eq!(x.ptr.ptr[1], 2); +} + + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +struct Bar; + +trait ToBar { + fn to_bar(&self) -> Bar; +} + +impl ToBar for Bar { + fn to_bar(&self) -> Bar { + *self + } +} + +pub fn main() { + // With a vec of ints. + let f1 = Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(&f1); + let f2 = &f1; + foo(f2); + let f3: &Fat<[isize]> = f2; + foo(f3); + let f4: &Fat<[isize]> = &f1; + foo(f4); + let f5: &Fat<[isize]> = &Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(f5); + + // With a vec of Bars. + let bar = Bar; + let f1 = Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] }; + foo2(&f1); + let f2 = &f1; + foo2(f2); + let f3: &Fat<[Bar]> = f2; + foo2(f3); + let f4: &Fat<[Bar]> = &f1; + foo2(f4); + let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [bar, bar, bar] }; + foo2(f5); + + // Assignment. + let f5: &mut Fat<[isize]> = &mut Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + f5.ptr[1] = 34; + assert_eq!(f5.ptr[0], 1); + assert_eq!(f5.ptr[1], 34); + assert_eq!(f5.ptr[2], 3); + + // Zero size vec. + let f5: &Fat<[isize]> = &Fat { f1: 5, f2: "some str", ptr: [] }; + assert!(f5.ptr.is_empty()); + let f5: &Fat<[Bar]> = &Fat { f1: 5, f2: "some str", ptr: [] }; + assert!(f5.ptr.is_empty()); + + // Deeply nested. + let f1 = Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} }; + foo3(&f1); + let f2 = &f1; + foo3(f2); + let f3: &Fat> = f2; + foo3(f3); + let f4: &Fat> = &f1; + foo3(f4); + let f5: &Fat> = + &Fat { f1: 5, f2: "some str", ptr: Fat { f1: 8, f2: "deep str", ptr: [1, 2, 3]} }; + foo3(f5); + + // Box. + let f1 = Box::new([1, 2, 3]); + assert_eq!((*f1)[1], 2); + let f2: Box<[isize]> = f1; + assert_eq!((*f2)[1], 2); + + // Nested Box. + let f1 : Box> = box Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }; + foo(&*f1); + let f2 : Box> = f1; + foo(&*f2); + + // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. + let f3 : Box> = + Box::>::new(Fat { f1: 5, f2: "some str", ptr: [1, 2, 3] }); + foo(&*f3); +} From fcbaf990f222cdfbfcd89f639793290bba1fed23 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 11:37:23 +0200 Subject: [PATCH 05/12] check `Pointer::to_int` for non-integer pointers --- src/interpreter/cast.rs | 11 ++++++++++- src/memory.rs | 8 ++++++-- src/primval.rs | 4 ++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 4f31cc21033..a924d59782a 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -8,7 +8,7 @@ use primval::PrimVal; use memory::Pointer; use rustc::ty::Ty; -use syntax::ast; +use syntax::ast::{self, IntTy, UintTy}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn cast_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { @@ -38,6 +38,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(..) | ty::TyRawPtr(_) => Ok(Ptr(ptr)), ty::TyFnPtr(_) => Ok(FnPtr(ptr)), + // FIXME: can truncation happen here? + ty::TyInt(IntTy::I8) => Ok(I8(ptr.to_int()? as i8)), + ty::TyInt(IntTy::I16) => Ok(I16(ptr.to_int()? as i16)), + ty::TyInt(IntTy::I32) => Ok(I32(ptr.to_int()? as i32)), + ty::TyInt(IntTy::I64) => Ok(I64(ptr.to_int()? as i64)), + ty::TyUint(UintTy::U8) => Ok(U8(ptr.to_int()? as u8)), + ty::TyUint(UintTy::U16) => Ok(U16(ptr.to_int()? as u16)), + ty::TyUint(UintTy::U32) => Ok(U32(ptr.to_int()? as u32)), + ty::TyUint(UintTy::U64) => Ok(U64(ptr.to_int()? as u64)), _ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))), } } diff --git a/src/memory.rs b/src/memory.rs index d1c0c740843..bdd9cd2fa0f 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -55,8 +55,12 @@ 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 to_int<'tcx>(&self) -> EvalResult<'tcx, usize> { + if self.points_to_zst() { + Ok(self.offset) + } else { + Err(EvalError::ReadPointerAsBytes) + } } pub fn from_int(i: usize) -> Self { Pointer { diff --git a/src/primval.rs b/src/primval.rs index 82e26162524..7d88e2ee7b1 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -46,7 +46,7 @@ impl PrimVal { U16(u) => u as u64, U32(u) => u as u64, U64(u) => u, - Ptr(ptr) => ptr.to_int() as u64, + Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as u64, _ => bug!("{}", error_msg), } } @@ -58,7 +58,7 @@ impl PrimVal { I16(i) => i as i64, I32(i) => i as i64, I64(i) => i, - Ptr(ptr) => ptr.to_int() as i64, + Ptr(ptr) => ptr.to_int().expect("non abstract ptr") as i64, _ => bug!("{}", error_msg), } } From fe614e342d6f40a7a4dc79aaf2cf349c424bceaf Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 11:40:09 +0200 Subject: [PATCH 06/12] remove `move_value`, which is a dupe of `write_value` --- src/interpreter/mod.rs | 7 ------- src/interpreter/terminator/intrinsics.rs | 6 +++--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 9e8c2996d26..1c6bd25f712 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -938,13 +938,6 @@ 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); diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index 103fe111fa3..efdd47e64da 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -136,7 +136,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "move_val_init" => { let ty = substs.type_at(0); let ptr = args_ptrs[0].read_ptr(&self.memory)?; - self.move_value(args_ptrs[1], ptr, ty)?; + self.write_value(args_ptrs[1], ptr, ty)?; } "needs_drop" => { @@ -213,7 +213,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "transmute" => { let ty = substs.type_at(0); - self.move_value(args_ptrs[0], dest, ty)?; + self.write_value(args_ptrs[0], dest, ty)?; } "try" => unimplemented!(), @@ -229,7 +229,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "volatile_store" => { let ty = substs.type_at(0); let dest = args_ptrs[0].read_ptr(&self.memory)?; - self.move_value(args_ptrs[1], dest, ty)?; + self.write_value(args_ptrs[1], dest, ty)?; } name => return Err(EvalError::Unimplemented(format!("unimplemented intrinsic: {}", name))), From 5d1080d0caf0e334a9ec800086723ad15721b3c1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 17:49:30 +0200 Subject: [PATCH 07/12] refactor `Lvalue` and `PrimVal::{SlicePtr, VtablePtr}` into `Value::ByValPair` --- src/interpreter/cast.rs | 1 - src/interpreter/mod.rs | 224 +++++++++++++---------- src/interpreter/terminator/intrinsics.rs | 7 +- src/interpreter/terminator/mod.rs | 6 +- src/interpreter/value.rs | 31 +++- src/memory.rs | 14 -- src/primval.rs | 2 - 7 files changed, 159 insertions(+), 126 deletions(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index a924d59782a..03e62176881 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -28,7 +28,6 @@ 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 1c6bd25f712..aec16a668de 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -101,20 +101,6 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -struct Lvalue { - ptr: Pointer, - extra: LvalueExtra, -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -enum LvalueExtra { - None, - Length(u64), - Vtable(Pointer), - DowncastVariant(usize), -} - #[derive(Clone)] pub enum CachedMir<'mir, 'tcx: 'mir> { Ref(&'mir mir::Mir<'tcx>), @@ -204,15 +190,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn str_to_primval(&mut self, s: &str) -> EvalResult<'tcx, PrimVal> { + fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> { // FIXME: cache these allocs let ptr = self.memory.allocate(s.len(), 1)?; self.memory.write_bytes(ptr, s.as_bytes())?; self.memory.freeze(ptr.alloc_id)?; - Ok(PrimVal::SlicePtr(ptr, s.len() as u64)) + Ok(Value::ByValPair(PrimVal::Ptr(ptr), self.target_usize_primval(s.len() as u64))) } - fn const_to_primval(&mut self, const_val: &ConstVal) -> EvalResult<'tcx, PrimVal> { + 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}; @@ -236,7 +222,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Bool(b) => PrimVal::Bool(b), Char(c) => PrimVal::Char(c), - Str(ref s) => self.str_to_primval(s)?, + Str(ref s) => return self.str_to_value(s), ByteStr(ref bs) => { let ptr = self.memory.allocate(bs.len(), 1)?; @@ -258,7 +244,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { bug!("uninferred constants only exist before typeck"), }; - Ok(primval) + Ok(Value::ByVal(primval)) } fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { @@ -565,30 +551,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Len(ref lvalue) => { let src = self.eval_lvalue(lvalue)?; let ty = self.lvalue_ty(lvalue); - let len = match ty.sty { - ty::TyArray(_, n) => n as u64, - ty::TySlice(_) => if let LvalueExtra::Length(n) = src.extra { - n + match ty.sty { + ty::TyArray(_, n) => self.memory.write_usize(dest, n as u64)?, + ty::TySlice(_) => if let Value::ByValPair(_, len) = src { + self.memory.write_primval(dest, len)?; } else { bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); }, _ => bug!("Rvalue::Len expected array or slice, got {:?}", ty), - }; - self.memory.write_usize(dest, len)?; + } } Ref(_, _, ref lvalue) => { - let lv = self.eval_lvalue(lvalue)?; - match lv.extra { - LvalueExtra::None => self.memory.write_ptr(dest, lv.ptr)?, - LvalueExtra::Length(len) => { - self.memory.write_primval(dest, PrimVal::SlicePtr(lv.ptr, len))?; - } - LvalueExtra::Vtable(ptr) => { - self.memory.write_primval(dest, PrimVal::VtablePtr(lv.ptr, ptr))?; - }, - LvalueExtra::DowncastVariant(..) => - bug!("attempted to take a reference to an enum downcast lvalue"), + match self.eval_lvalue(lvalue)? { + Value::ByRef(ptr) => self.memory.write_ptr(dest, ptr)?, + Value::ByVal(..) => bug!("cannot take reference of immediate"), + pair @ Value::ByValPair(..) => self.write_value(pair, dest, dest_ty)?, } } @@ -617,7 +595,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match (&src_pointee_ty.sty, &dest_pointee_ty.sty) { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; - self.memory.write_primval(dest, PrimVal::SlicePtr(ptr, length as u64))?; + self.memory.write_ptr(dest, ptr)?; + let ptr_size = self.memory.pointer_size() as isize; + let dest_extra = dest.offset(ptr_size); + self.memory.write_usize(dest_extra, length as u64)?; } (&ty::TyTrait(_), &ty::TyTrait(_)) => { // For now, upcasts are limited to changes in marker @@ -630,7 +611,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let trait_ref = self.tcx.erase_regions(&trait_ref); let vtable = self.get_vtable(trait_ref)?; let ptr = src.read_ptr(&self.memory)?; - self.memory.write_primval(dest, PrimVal::VtablePtr(ptr, vtable))?; + + self.memory.write_ptr(dest, ptr)?; + let ptr_size = self.memory.pointer_size() as isize; + let dest_extra = dest.offset(ptr_size); + self.memory.write_ptr(dest_extra, vtable)?; }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), @@ -646,17 +631,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { trace!("misc cast: {:?}", src); let ptr_size = self.memory.pointer_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::ByValPair(data, meta), true) => { + self.memory.write_primval(dest, data)?; + self.memory.write_primval(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::ByValPair(data, _), false) => { + self.memory.write_primval(dest, data)?; }, (Value::ByRef(ptr), true) => { self.memory.copy(ptr, dest, ptr_size * 2, ptr_size)?; @@ -782,12 +762,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => Ok(Value::ByRef(self.eval_lvalue(lvalue)?.to_ptr())), + Consume(ref lvalue) => self.eval_lvalue(lvalue), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; let value = match *literal { - Literal::Value { ref value } => Value::ByVal(self.const_to_primval(value)?), + Literal::Value { ref value } => self.const_to_value(value)?, Literal::Item { def_id, substs } => { if let ty::TyFnDef(..) = ty.sty { @@ -822,14 +802,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { + fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Lvalue::*; - let ptr = match *lvalue { - ReturnPointer => self.frame().return_ptr - .expect("ReturnPointer used in a function with no return value"), - Arg(i) => self.frame().locals[i.index()], - Var(i) => self.frame().locals[self.frame().var_offset + i.index()], - Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()], + let value = match *lvalue { + ReturnPointer => Value::ByRef(self.frame().return_ptr + .expect("ReturnPointer used in a function with no return value")), + Arg(i) => Value::ByRef(self.frame().locals[i.index()]), + Var(i) => Value::ByRef(self.frame().locals[self.frame().var_offset + i.index()]), + Temp(i) => Value::ByRef(self.frame().locals[self.frame().temp_offset + i.index()]), Static(def_id) => { let substs = subst::Substs::empty(self.tcx); @@ -838,25 +818,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - *self.statics.get(&cid).expect("static should have been cached (lvalue)") + Value::ByRef(*self.statics.get(&cid).expect("static should have been cached (lvalue)")) }, Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; trace!("projection base: {:?}", base); trace!("projection: {:?}", proj.elem); + match base { + Value::ByRef(ptr) => self.memory.dump(ptr.alloc_id), + _ => {}, + } let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); use rustc::mir::repr::ProjectionElem::*; match proj.elem { Field(field, field_ty) => { + let field_ty = self.monomorphize(field_ty, self.substs()); use rustc::ty::layout::Layout::*; let variant = match *base_layout { Univariant { ref variant, .. } => variant, General { ref variants, .. } => { - if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { - &variants[variant_idx] + if let Value::ByValPair(PrimVal::Ptr(ptr), variant_idx) = base { + // early exit, because enum variant field access is passed + // as a non-fat-pointer ByValPair + let idx = variant_idx.expect_uint("enum variant id not integral") as usize; + let offset = variants[idx].field_offset(field.index()).bytes() as isize; + return Ok(ByRef(ptr.offset(offset))); } else { bug!("field access on enum had no variant index"); } @@ -869,17 +858,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let offset = variant.field_offset(field.index()).bytes(); - let ptr = base.ptr.offset(offset as isize); - match (&field_ty.sty, base.extra) { - (&ty::TyStr, extra @ LvalueExtra::Length(_)) | - (&ty::TySlice(_), extra @ LvalueExtra::Length(_)) | - (&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue { - ptr: ptr, - extra: extra, - }), - (&ty::TyTrait(_), _) => bug!("trait field without vtable"), - _ => ptr, + let offset = variant.field_offset(field.index()).bytes() as isize; + use self::value::Value::*; + match base { + ByRef(ptr) => if self.type_is_fat_ptr(field_ty) { + self.read_value(ptr.offset(offset), field_ty)? + } else { + ByRef(ptr.offset(offset)) + }, + // indexing into a field of an unsized struct + ByValPair(PrimVal::Ptr(ptr), extra) => if self.type_is_sized(field_ty) { + ByRef(ptr.offset(offset)) + } else { + ByValPair(PrimVal::Ptr(ptr.offset(offset)), extra) + }, + other => bug!("expected thin ptr, got: {:?}", other), } }, @@ -887,10 +880,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *base_layout { General { ref variants, .. } => { - return Ok(Lvalue { - ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize), - extra: LvalueExtra::DowncastVariant(variant), - }); + use self::value::Value::*; + match base { + ByRef(ptr) => return Ok(ByValPair( + PrimVal::Ptr(ptr.offset(variants[variant].field_offset(1).bytes() as isize)), + PrimVal::U64(variant as u64), + )), + other => bug!("bad downcast base: {:?}", other), + } } RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { return Ok(base); @@ -899,15 +896,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, - Deref => { - let (ptr, extra) = match self.read_primval(base.ptr, base_ty)? { - PrimVal::SlicePtr(ptr, n) => (ptr, LvalueExtra::Length(n)), - PrimVal::VtablePtr(ptr, vptr) => (ptr, LvalueExtra::Vtable(vptr)), - PrimVal::Ptr(ptr) => (ptr, LvalueExtra::None), - _ => bug!("can't deref non pointer types"), - }; - return Ok(Lvalue { ptr: ptr, extra: extra }); - } + Deref => match self.follow_ref(base, base_ty)? { + Value::ByRef(..) => bug!("follow_ref broken"), + // magical deref + Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr), + Value::ByVal(..) => bug!("can't deref non pointer types"), + // deref ops on fat pointers are no-ops + pair @ Value::ByValPair(..) => pair, + }, Index(ref operand) => { let elem_size = match base_ty.sty { @@ -918,7 +914,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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) + match base { + Value::ByRef(ptr) | + Value::ByValPair(PrimVal::Ptr(ptr), _) | + Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr.offset(n as isize * elem_size as isize)), + other => bug!("index op on {:?}", other), + } } ConstantIndex { .. } => unimplemented!(), @@ -926,8 +927,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } }; - - Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) + Ok(value) } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { @@ -958,15 +958,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(ptr, primval)?; Ok(ptr) } + + Value::ByValPair(a, b) => { + let size = self.type_size(ty); + let align = self.type_align(ty); + let ptr = self.memory.allocate(size, align)?; + let ptr_size = self.memory.pointer_size() as isize; + self.memory.write_primval(ptr, a)?; + self.memory.write_primval(ptr.offset(ptr_size), b)?; + Ok(ptr) + } } } fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { match value { - Value::ByRef(ptr) => self.read_primval(ptr, ty), + Value::ByRef(ptr) => match self.read_value(ptr, ty)? { + Value::ByRef(_) => bug!("read_value can't result in `ByRef`"), + Value::ByVal(primval) => Ok(primval), + Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), + }, // TODO(solson): Sanity-check the primval type against the input type. Value::ByVal(primval) => Ok(primval), + Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"), } } @@ -979,10 +994,23 @@ 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(a, b) => { + self.memory.write_primval(dest, a)?; + let extra_dest = dest.offset(self.memory.pointer_size() as isize); + self.memory.write_primval(extra_dest, b) + } } } - pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + // ensures that this value isn't a `ByRef` anymore + fn follow_ref(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + match value { + Value::ByRef(ptr) => self.read_value(ptr, ty), + other => Ok(other), + } + } + + fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { use syntax::ast::{IntTy, UintTy, FloatTy}; let val = match &ty.sty { &ty::TyBool => PrimVal::Bool(self.memory.read_bool(ptr)?), @@ -1022,12 +1050,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } else { // FIXME: extract the offset to the tail field for `Box<(i64, i32, [u8])>` let extra = ptr.offset(self.memory.pointer_size() as isize); - match self.tcx.struct_tail(ty).sty { - ty::TyTrait(..) => PrimVal::VtablePtr(p, self.memory.read_ptr(extra)?), + let extra = match self.tcx.struct_tail(ty).sty { + ty::TyTrait(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | - ty::TyStr => PrimVal::SlicePtr(p, self.memory.read_usize(extra)?), + ty::TyStr => self.target_usize_primval(self.memory.read_usize(extra)?), _ => bug!("unsized primval ptr read from {:?}", ty), - } + }; + return Ok(Value::ByValPair(PrimVal::Ptr(p), extra)); } } @@ -1052,7 +1081,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("primitive read of non-primitive type: {:?}", ty), }; - Ok(val) + Ok(Value::ByVal(val)) } fn frame(&self) -> &Frame<'a, 'tcx> { @@ -1083,13 +1112,6 @@ fn pointee_type(ptr_ty: ty::Ty) -> Option { } } -impl Lvalue { - fn to_ptr(self) -> Pointer { - assert_eq!(self.extra, LvalueExtra::None); - self.ptr - } -} - impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { type Target = mir::Mir<'tcx>; fn deref(&self) -> &mir::Mir<'tcx> { diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index efdd47e64da..c025852bc5e 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -2,7 +2,7 @@ use rustc::hir::def_id::DefId; use rustc::mir::repr as mir; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; -use rustc::ty; +use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use memory::Pointer; @@ -17,6 +17,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: &'tcx Substs<'tcx>, args: &[mir::Operand<'tcx>], dest: Pointer, + dest_ty: Ty<'tcx>, dest_layout: &'tcx Layout, ) -> EvalResult<'tcx, ()> { let args_ptrs: EvalResult> = args.iter() @@ -202,8 +203,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "type_name" => { let ty = substs.type_at(0); let ty_name = ty.to_string(); - let s = self.str_to_primval(&ty_name)?; - self.memory.write_primval(dest, s)?; + let s = self.str_to_value(&ty_name)?; + self.write_value(s, dest, dest_ty)?; } "type_id" => { let ty = substs.type_at(0); diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index 3a4e2f5ff84..c4415415f6e 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -43,13 +43,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); + let discr_ptr = self.eval_lvalue(discr)?; let discr_ty = self.lvalue_ty(discr); let discr_size = self .type_layout(discr_ty) .size(&self.tcx.data_layout) .bytes() as usize; - let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; + let discr_val = discr_ptr.read_uint(&self.memory, discr_size)?; if let ty::TyChar = discr_ty.sty { if ::std::char::from_u32(discr_val as u32).is_none() { return Err(EvalError::InvalidChar(discr_val as u64)); @@ -165,7 +165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = fn_ty.sig.0.output; let layout = self.type_layout(ty); let (ret, target) = destination.unwrap(); - self.call_intrinsic(def_id, substs, arg_operands, ret, layout)?; + self.call_intrinsic(def_id, substs, arg_operands, ret, ty, layout)?; self.goto_block(target); Ok(()) } diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 58805ee4c5c..6512fe9b159 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -14,24 +14,48 @@ use primval::PrimVal; pub(super) enum Value { ByRef(Pointer), ByVal(PrimVal), + ByValPair(PrimVal, 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), + ByValPair(..) => unimplemented!(), ByVal(_other) => unimplemented!(), } } + pub(super) fn read_uint<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>, size: usize) -> EvalResult<'tcx, u64> { + use self::Value::*; + match *self { + ByRef(ptr) => mem.read_uint(ptr, size), + ByVal(PrimVal::U8(u)) => Ok(u as u64), + ByVal(PrimVal::U16(u)) => Ok(u as u64), + ByVal(PrimVal::U32(u)) => Ok(u as u64), + ByVal(PrimVal::U64(u)) => Ok(u as u64), + ByValPair(..) => unimplemented!(), + ByVal(_other) => unimplemented!(), + } + } + + pub(super) fn to_ptr(&self) -> Pointer { + use self::Value::*; + match *self { + ByRef(ptr) => ptr, + other => bug!("expected pointer, got {:?}", other), + } + } + 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), + ByValPair(_, PrimVal::Ptr(vtable)) => Ok(vtable), _ => unimplemented!(), } } @@ -40,7 +64,10 @@ impl Value { use self::Value::*; match *self { ByRef(ptr) => mem.read_usize(ptr.offset(mem.pointer_size() as isize)), - ByVal(PrimVal::SlicePtr(_, len)) => Ok(len), + ByValPair(_, PrimVal::U8(len)) => Ok(len as u64), + ByValPair(_, PrimVal::U16(len)) => Ok(len as u64), + ByValPair(_, PrimVal::U32(len)) => Ok(len as u64), + ByValPair(_, PrimVal::U64(len)) => Ok(len), _ => unimplemented!(), } } diff --git a/src/memory.rs b/src/memory.rs index bdd9cd2fa0f..eeae014c6da 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -550,20 +550,6 @@ 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 7d88e2ee7b1..70934c2a549 100644 --- a/src/primval.rs +++ b/src/primval.rs @@ -14,8 +14,6 @@ pub enum PrimVal { Ptr(Pointer), FnPtr(Pointer), - VtablePtr(Pointer, Pointer), - SlicePtr(Pointer, u64), Char(char), F32(f32), F64(f64), From e28f87375660ca0491a57bc3af0e3da98a9cf270 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Mon, 26 Sep 2016 17:49:53 +0200 Subject: [PATCH 08/12] actually error on failing miri-pass tests + remove a test that never succeeded --- tests/compiletest.rs | 1 + tests/run-pass/issue-33387.rs | 19 ------------------- 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 tests/run-pass/issue-33387.rs diff --git a/tests/compiletest.rs b/tests/compiletest.rs index bc112f40685..6870161e768 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -105,6 +105,7 @@ fn compile_test() { writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap(); writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap(); writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap(); + panic!("failed to run test"); } } Err(e) => { diff --git a/tests/run-pass/issue-33387.rs b/tests/run-pass/issue-33387.rs deleted file mode 100644 index edbf2b81ce9..00000000000 --- a/tests/run-pass/issue-33387.rs +++ /dev/null @@ -1,19 +0,0 @@ -// 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]); -} From d2d73a908dce98143b610ac154b7a2e35ad32847 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:14:53 +0200 Subject: [PATCH 09/12] reintroduce Lvalue and LvalueExtra --- src/interpreter/mod.rs | 151 +++++++++++++++--------------- src/interpreter/terminator/mod.rs | 4 +- src/interpreter/value.rs | 21 ----- 3 files changed, 80 insertions(+), 96 deletions(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index aec16a668de..96cc8a4e8b9 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -101,6 +101,20 @@ pub struct Frame<'a, 'tcx: 'a> { pub stmt: usize, } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +struct Lvalue { + ptr: Pointer, + extra: LvalueExtra, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum LvalueExtra { + None, + Length(u64), + Vtable(Pointer), + DowncastVariant(usize), +} + #[derive(Clone)] pub enum CachedMir<'mir, 'tcx: 'mir> { Ref(&'mir mir::Mir<'tcx>), @@ -553,8 +567,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = self.lvalue_ty(lvalue); match ty.sty { ty::TyArray(_, n) => self.memory.write_usize(dest, n as u64)?, - ty::TySlice(_) => if let Value::ByValPair(_, len) = src { - self.memory.write_primval(dest, len)?; + ty::TySlice(_) => if let LvalueExtra::Length(len) = src.extra { + self.memory.write_usize(dest, len)?; } else { bug!("Rvalue::Len of a slice given non-slice pointer: {:?}", src); }, @@ -563,10 +577,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } Ref(_, _, ref lvalue) => { - match self.eval_lvalue(lvalue)? { - Value::ByRef(ptr) => self.memory.write_ptr(dest, ptr)?, - Value::ByVal(..) => bug!("cannot take reference of immediate"), - pair @ Value::ByValPair(..) => self.write_value(pair, dest, dest_ty)?, + let lvalue = self.eval_lvalue(lvalue)?; + self.memory.write_ptr(dest, lvalue.ptr)?; + let extra_ptr = dest.offset(self.memory.pointer_size() as isize); + match lvalue.extra { + LvalueExtra::None => {}, + LvalueExtra::Length(len) => self.memory.write_usize(extra_ptr, len)?, + LvalueExtra::Vtable(ptr) => self.memory.write_ptr(extra_ptr, ptr)?, + LvalueExtra::DowncastVariant(..) => + bug!("attempted to take a reference to an enum downcast lvalue"), } } @@ -762,7 +781,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn eval_operand(&mut self, op: &mir::Operand<'tcx>) -> EvalResult<'tcx, Value> { use rustc::mir::repr::Operand::*; match *op { - Consume(ref lvalue) => self.eval_lvalue(lvalue), + Consume(ref lvalue) => Ok(Value::ByRef(self.eval_lvalue(lvalue)?.to_ptr())), Constant(mir::Constant { ref literal, ty, .. }) => { use rustc::mir::repr::Literal; @@ -802,14 +821,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { + fn eval_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue> { use rustc::mir::repr::Lvalue::*; - let value = match *lvalue { - ReturnPointer => Value::ByRef(self.frame().return_ptr - .expect("ReturnPointer used in a function with no return value")), - Arg(i) => Value::ByRef(self.frame().locals[i.index()]), - Var(i) => Value::ByRef(self.frame().locals[self.frame().var_offset + i.index()]), - Temp(i) => Value::ByRef(self.frame().locals[self.frame().temp_offset + i.index()]), + let ptr = match *lvalue { + ReturnPointer => self.frame().return_ptr + .expect("ReturnPointer used in a function with no return value"), + Arg(i) => self.frame().locals[i.index()], + Var(i) => self.frame().locals[self.frame().var_offset + i.index()], + Temp(i) => self.frame().locals[self.frame().temp_offset + i.index()], Static(def_id) => { let substs = subst::Substs::empty(self.tcx); @@ -818,17 +837,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { substs: substs, kind: ConstantKind::Global, }; - Value::ByRef(*self.statics.get(&cid).expect("static should have been cached (lvalue)")) + *self.statics.get(&cid).expect("static should have been cached (lvalue)") }, Projection(ref proj) => { let base = self.eval_lvalue(&proj.base)?; - trace!("projection base: {:?}", base); - trace!("projection: {:?}", proj.elem); - match base { - Value::ByRef(ptr) => self.memory.dump(ptr.alloc_id), - _ => {}, - } let base_ty = self.lvalue_ty(&proj.base); let base_layout = self.type_layout(base_ty); @@ -840,12 +853,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let variant = match *base_layout { Univariant { ref variant, .. } => variant, General { ref variants, .. } => { - if let Value::ByValPair(PrimVal::Ptr(ptr), variant_idx) = base { - // early exit, because enum variant field access is passed - // as a non-fat-pointer ByValPair - let idx = variant_idx.expect_uint("enum variant id not integral") as usize; - let offset = variants[idx].field_offset(field.index()).bytes() as isize; - return Ok(ByRef(ptr.offset(offset))); + if let LvalueExtra::DowncastVariant(variant_idx) = base.extra { + &variants[variant_idx] } else { bug!("field access on enum had no variant index"); } @@ -858,21 +867,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - let offset = variant.field_offset(field.index()).bytes() as isize; - use self::value::Value::*; - match base { - ByRef(ptr) => if self.type_is_fat_ptr(field_ty) { - self.read_value(ptr.offset(offset), field_ty)? - } else { - ByRef(ptr.offset(offset)) - }, - // indexing into a field of an unsized struct - ByValPair(PrimVal::Ptr(ptr), extra) => if self.type_is_sized(field_ty) { - ByRef(ptr.offset(offset)) - } else { - ByValPair(PrimVal::Ptr(ptr.offset(offset)), extra) - }, - other => bug!("expected thin ptr, got: {:?}", other), + let offset = variant.field_offset(field.index()).bytes(); + let ptr = base.ptr.offset(offset as isize); + trace!("{:?}", base); + trace!("{:?}", field_ty); + if self.type_is_sized(field_ty) { + ptr + } else { + match base.extra { + LvalueExtra::None => bug!("expected fat pointer"), + LvalueExtra::DowncastVariant(..) => bug!("Rust doesn't support unsized fields in enum variants"), + LvalueExtra::Vtable(_) | + LvalueExtra::Length(_) => {}, + } + return Ok(Lvalue { + ptr: ptr, + extra: base.extra, + }); } }, @@ -880,14 +891,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; match *base_layout { General { ref variants, .. } => { - use self::value::Value::*; - match base { - ByRef(ptr) => return Ok(ByValPair( - PrimVal::Ptr(ptr.offset(variants[variant].field_offset(1).bytes() as isize)), - PrimVal::U64(variant as u64), - )), - other => bug!("bad downcast base: {:?}", other), - } + return Ok(Lvalue { + ptr: base.ptr.offset(variants[variant].field_offset(1).bytes() as isize), + extra: LvalueExtra::DowncastVariant(variant), + }); } RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { return Ok(base); @@ -896,14 +903,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }, - Deref => match self.follow_ref(base, base_ty)? { - Value::ByRef(..) => bug!("follow_ref broken"), - // magical deref - Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr), - Value::ByVal(..) => bug!("can't deref non pointer types"), - // deref ops on fat pointers are no-ops - pair @ Value::ByValPair(..) => pair, - }, + Deref => { + use primval::PrimVal::*; + use interpreter::value::Value::*; + let (ptr, extra) = match self.read_value(base.ptr, base_ty)? { + ByValPair(Ptr(ptr), Ptr(vptr)) => (ptr, LvalueExtra::Vtable(vptr)), + ByValPair(Ptr(ptr), n) => (ptr, LvalueExtra::Length(n.expect_uint("slice length"))), + ByVal(Ptr(ptr)) => (ptr, LvalueExtra::None), + _ => bug!("can't deref non pointer types"), + }; + return Ok(Lvalue { ptr: ptr, extra: extra }); + } Index(ref operand) => { let elem_size = match base_ty.sty { @@ -914,12 +924,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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"); - match base { - Value::ByRef(ptr) | - Value::ByValPair(PrimVal::Ptr(ptr), _) | - Value::ByVal(PrimVal::Ptr(ptr)) => Value::ByRef(ptr.offset(n as isize * elem_size as isize)), - other => bug!("index op on {:?}", other), - } + base.ptr.offset(n as isize * elem_size as isize) } ConstantIndex { .. } => unimplemented!(), @@ -927,7 +932,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } }; - Ok(value) + + Ok(Lvalue { ptr: ptr, extra: LvalueExtra::None }) } fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { @@ -1002,14 +1008,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - // ensures that this value isn't a `ByRef` anymore - fn follow_ref(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { - match value { - Value::ByRef(ptr) => self.read_value(ptr, ty), - other => Ok(other), - } - } - fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { use syntax::ast::{IntTy, UintTy, FloatTy}; let val = match &ty.sty { @@ -1112,6 +1110,13 @@ fn pointee_type(ptr_ty: ty::Ty) -> Option { } } +impl Lvalue { + fn to_ptr(self) -> Pointer { + assert_eq!(self.extra, LvalueExtra::None); + self.ptr + } +} + impl<'mir, 'tcx: 'mir> Deref for CachedMir<'mir, 'tcx> { type Target = mir::Mir<'tcx>; fn deref(&self) -> &mir::Mir<'tcx> { diff --git a/src/interpreter/terminator/mod.rs b/src/interpreter/terminator/mod.rs index c4415415f6e..6e2f1381193 100644 --- a/src/interpreter/terminator/mod.rs +++ b/src/interpreter/terminator/mod.rs @@ -43,13 +43,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } SwitchInt { ref discr, ref values, ref targets, .. } => { - let discr_ptr = self.eval_lvalue(discr)?; + let discr_ptr = self.eval_lvalue(discr)?.to_ptr(); let discr_ty = self.lvalue_ty(discr); let discr_size = self .type_layout(discr_ty) .size(&self.tcx.data_layout) .bytes() as usize; - let discr_val = discr_ptr.read_uint(&self.memory, discr_size)?; + let discr_val = self.memory.read_uint(discr_ptr, discr_size)?; if let ty::TyChar = discr_ty.sty { if ::std::char::from_u32(discr_val as u32).is_none() { return Err(EvalError::InvalidChar(discr_val as u64)); diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 6512fe9b159..87a0e15cf75 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -30,27 +30,6 @@ impl Value { } } - pub(super) fn read_uint<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>, size: usize) -> EvalResult<'tcx, u64> { - use self::Value::*; - match *self { - ByRef(ptr) => mem.read_uint(ptr, size), - ByVal(PrimVal::U8(u)) => Ok(u as u64), - ByVal(PrimVal::U16(u)) => Ok(u as u64), - ByVal(PrimVal::U32(u)) => Ok(u as u64), - ByVal(PrimVal::U64(u)) => Ok(u as u64), - ByValPair(..) => unimplemented!(), - ByVal(_other) => unimplemented!(), - } - } - - pub(super) fn to_ptr(&self) -> Pointer { - use self::Value::*; - match *self { - ByRef(ptr) => ptr, - other => bug!("expected pointer, got {:?}", other), - } - } - pub(super) fn expect_vtable<'a, 'tcx: 'a>(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { From 7b70f4fe2cfb49acd98ff295264d47de0456dc42 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:33:47 +0200 Subject: [PATCH 10/12] typecheck `write_value` for `ByValPair` --- src/interpreter/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 96cc8a4e8b9..bdd812782df 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1002,7 +1002,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Value::ByVal(primval) => self.memory.write_primval(dest, primval), Value::ByValPair(a, b) => { self.memory.write_primval(dest, a)?; - let extra_dest = dest.offset(self.memory.pointer_size() as isize); + let layout = self.type_layout(dest_ty); + let offset = match *layout { + Layout::Univariant { ref variant, .. } => { + bug!("I don't think this can ever happen until we have custom fat pointers"); + //variant.field_offset(1).bytes() as isize + }, + Layout::FatPointer { .. } => self.memory.pointer_size() as isize, + _ => bug!("tried to write value pair of non-fat pointer type: {:?}", layout), + }; + let extra_dest = dest.offset(offset); self.memory.write_primval(extra_dest, b) } } From 055c1b14f753cb4cb1099596f668393e4c9bbb3f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:36:44 +0200 Subject: [PATCH 11/12] remove FIXME cleared up in the PR --- src/interpreter/cast.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/interpreter/cast.rs b/src/interpreter/cast.rs index 03e62176881..f53d22699ca 100644 --- a/src/interpreter/cast.rs +++ b/src/interpreter/cast.rs @@ -37,7 +37,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyRef(..) | ty::TyRawPtr(_) => Ok(Ptr(ptr)), ty::TyFnPtr(_) => Ok(FnPtr(ptr)), - // FIXME: can truncation happen here? ty::TyInt(IntTy::I8) => Ok(I8(ptr.to_int()? as i8)), ty::TyInt(IntTy::I16) => Ok(I16(ptr.to_int()? as i16)), ty::TyInt(IntTy::I32) => Ok(I32(ptr.to_int()? as i32)), From 7e29392ac4114029a589b42cc117266a99da61e3 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 27 Sep 2016 10:39:14 +0200 Subject: [PATCH 12/12] fix warnings --- src/interpreter/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index bdd812782df..c9b51a6213a 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -1004,7 +1004,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_primval(dest, a)?; let layout = self.type_layout(dest_ty); let offset = match *layout { - Layout::Univariant { ref variant, .. } => { + Layout::Univariant { .. } => { bug!("I don't think this can ever happen until we have custom fat pointers"); //variant.field_offset(1).bytes() as isize },