diff --git a/src/eval_context.rs b/src/eval_context.rs index 57c8b353bfb9..8a1e991628f6 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -19,7 +19,7 @@ use error::{EvalError, EvalResult}; use lvalue::{Global, GlobalId, Lvalue, LvalueExtra}; use memory::{Memory, MemoryPointer, TlsKey}; use operator; -use value::{PrimVal, PrimValKind, Value}; +use value::{PrimVal, PrimValKind, Value, Pointer}; pub struct EvalContext<'a, 'tcx: 'a> { /// The results of the type checker, from rustc. @@ -395,7 +395,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?; let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); - self.write_primval(dest, ptr, ty)?; + self.write_ptr(dest, ptr, ty)?; } } } @@ -444,7 +444,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.memory.write_uint(discr_dest, discr_val, discr_size)?; let dest = Lvalue::Ptr { - ptr: PrimVal::Ptr(dest_ptr), + ptr: dest_ptr.into(), extra: LvalueExtra::DowncastVariant(variant_idx), }; @@ -580,7 +580,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); assert_eq!(self.type_size(operand_ty)?, Some(0)); } - self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; + self.write_null(dest, dest_ty)?; } } else { bug!("tried to assign {:?} to Layout::RawNullablePointer", kind); @@ -662,7 +662,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let value = self.eval_operand(operand)?; // FIXME(solson) - let dest = PrimVal::Ptr(self.force_allocation(dest)?.to_ptr()?); + let dest = Pointer::from(self.force_allocation(dest)?.to_ptr()?); for i in 0..length { let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; @@ -686,9 +686,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = self.lvalue_ty(lvalue); let val = match extra { - LvalueExtra::None => Value::ByVal(ptr), - LvalueExtra::Length(len) => Value::ByValPair(ptr, PrimVal::from_u128(len as u128)), - LvalueExtra::Vtable(vtable) => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), + LvalueExtra::None => ptr.to_value(), + LvalueExtra::Length(len) => ptr.with_extra(PrimVal::from_u128(len as u128)), + LvalueExtra::Vtable(vtable) => ptr.with_extra(PrimVal::Ptr(vtable)), LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), }; @@ -928,14 +928,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn wrapping_pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { + pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = offset.overflowing_mul(pointee_size).0; ptr.wrapping_signed_offset(offset, self.memory.layout) } - pub(super) fn pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> { + pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { // This function raises an error if the offset moves the pointer outside of its allocation. We consider // ZSTs their own huge allocation that doesn't overlap with anything (and nothing moves in there because the size is 0). // We also consider the NULL pointer its own separate allocation, and all the remaining integers pointers their own @@ -949,7 +949,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return if let Some(offset) = offset.checked_mul(pointee_size) { let ptr = ptr.signed_offset(offset, self.memory.layout)?; // Do not do bounds-checking for integers; they can never alias a normal pointer anyway. - if let PrimVal::Ptr(ptr) = ptr { + if let PrimVal::Ptr(ptr) = ptr.into_inner_primval() { self.memory.check_bounds(ptr, false)?; } else if ptr.is_null()? { // We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now. @@ -1002,7 +1002,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.monomorphize(operand.ty(self.mir(), self.tcx), self.substs()) } - fn copy(&mut self, src: PrimVal, dest: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx> { + fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx> { let size = self.type_size(ty)?.expect("cannot copy from an unsized type"); let align = self.type_align(ty)?; self.memory.copy(src, dest, size, align, false)?; @@ -1026,8 +1026,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty = self.monomorphize(ty, self.stack[frame].instance.substs); let substs = self.stack[frame].instance.substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(PrimVal::Ptr(ptr))); // it stays live - self.write_value_to_ptr(val, PrimVal::Ptr(ptr), ty)?; + self.stack[frame].locals[local.index() - 1] = Some(Value::ByRef(ptr.into())); // it stays live + self.write_value_to_ptr(val, ptr.into(), ty)?; Lvalue::from_ptr(ptr) } } @@ -1040,14 +1040,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => { let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?; self.memory.mark_static(ptr.alloc_id); - self.write_value_to_ptr(global_val.value, PrimVal::Ptr(ptr), global_val.ty)?; + self.write_value_to_ptr(global_val.value, ptr.into(), global_val.ty)?; // see comment on `initialized` field if global_val.initialized { self.memory.mark_static_initalized(ptr.alloc_id, global_val.mutable)?; } let lval = self.globals.get_mut(&cid).expect("already checked"); *lval = Global { - value: Value::ByRef(PrimVal::Ptr(ptr)), + value: Value::ByRef(ptr.into()), .. global_val }; Lvalue::from_ptr(ptr) @@ -1087,6 +1087,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_primval(dest, PrimVal::Bytes(0), dest_ty) } + pub(super) fn write_ptr( + &mut self, + dest: Lvalue<'tcx>, + val: Pointer, + dest_ty: Ty<'tcx>, + ) -> EvalResult<'tcx> { + self.write_value(val.to_value(), dest, dest_ty) + } + pub(super) fn write_primval( &mut self, dest: Lvalue<'tcx>, @@ -1172,9 +1181,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Ok(Some(src_val)) = self.try_read_value(src_ptr, dest_ty) { write_dest(self, src_val)?; } else { - let dest_ptr = self.alloc_ptr(dest_ty)?; - self.copy(src_ptr, PrimVal::Ptr(dest_ptr), dest_ty)?; - write_dest(self, Value::ByRef(PrimVal::Ptr(dest_ptr)))?; + let dest_ptr = self.alloc_ptr(dest_ty)?.into(); + self.copy(src_ptr, dest_ptr, dest_ty)?; + write_dest(self, Value::ByRef(dest_ptr))?; } } else { @@ -1188,7 +1197,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn write_value_to_ptr( &mut self, value: Value, - dest: PrimVal, + dest: Pointer, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx> { match value { @@ -1218,8 +1227,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_0, self.memory.layout)?), a, field_0_size)?; - self.memory.write_primval(PrimVal::Ptr(ptr.offset(field_1, self.memory.layout)?), b, field_1_size)?; + self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?.into(), a, field_0_size)?; + self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?.into(), b, field_1_size)?; Ok(()) } @@ -1322,7 +1331,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub(super) fn read_value(&mut self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { + pub(super) fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { if let Some(val) = self.try_read_value(ptr, ty)? { Ok(val) } else { @@ -1333,21 +1342,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn read_ptr(&self, ptr: MemoryPointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> { let p = self.memory.read_ptr(ptr)?; if self.type_is_sized(pointee_ty) { - Ok(Value::ByVal(p)) + Ok(p.to_value()) } else { trace!("reading fat pointer extra of type {}", pointee_ty); let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?; let extra = match self.tcx.struct_tail(pointee_ty).sty { - ty::TyDynamic(..) => self.memory.read_ptr(extra)?, + ty::TyDynamic(..) => self.memory.read_ptr(extra)?.into_inner_primval(), ty::TySlice(..) | ty::TyStr => PrimVal::from_u128(self.memory.read_usize(extra)? as u128), _ => bug!("unsized primval ptr read from {:?}", pointee_ty), }; - Ok(Value::ByValPair(p, extra)) + Ok(p.with_extra(extra)) } } - fn try_read_value(&mut self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { + fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option> { use syntax::ast::FloatTy; let val = match ty.sty { @@ -1373,7 +1382,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // if we transmute a ptr to an isize, reading it back into a primval shouldn't panic // Due to read_ptr ignoring the sign, we need to jump around some hoops match self.memory.read_int(ptr.to_ptr()?, size) { - Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?, + Err(EvalError::ReadPointerAsBytes) if size == self.memory.pointer_size() => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(), other => PrimVal::from_i128(other?), } } @@ -1390,7 +1399,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }; if size == self.memory.pointer_size() { // if we transmute a ptr to an usize, reading it back into a primval shouldn't panic - self.memory.read_ptr(ptr.to_ptr()?)? + self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval() } else { PrimVal::from_u128(self.memory.read_uint(ptr.to_ptr()?, size)?) } @@ -1399,7 +1408,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr.to_ptr()?)?), ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr.to_ptr()?)?), - ty::TyFnPtr(_) => self.memory.read_ptr(ptr.to_ptr()?)?, + ty::TyFnPtr(_) => self.memory.read_ptr(ptr.to_ptr()?)?.into_inner_primval(), ty::TyRef(_, ref tam) | ty::TyRawPtr(ref tam) => return self.read_ptr(ptr.to_ptr()?, tam.ty).map(Some), @@ -1458,7 +1467,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (&ty::TyArray(_, length), &ty::TySlice(_)) => { let ptr = src.read_ptr(&self.memory)?; let len = PrimVal::from_u128(length as u128); - self.write_value(Value::ByValPair(ptr, len), dest, dest_ty) + self.write_value(ptr.with_extra(len), dest, dest_ty) } (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { // For now, upcasts are limited to changes in marker @@ -1472,7 +1481,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let vtable = self.get_vtable(src_pointee_ty, trait_ref)?; let ptr = src.read_ptr(&self.memory)?; let extra = PrimVal::Ptr(vtable); - self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty) + self.write_value(ptr.with_extra(extra), dest, dest_ty) }, _ => bug!("invalid unsizing {:?} -> {:?}", src_ty, dest_ty), @@ -1532,7 +1541,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; if src_fty == dst_fty { - self.copy(src_f_ptr, PrimVal::Ptr(dst_f_ptr), src_fty)?; + self.copy(src_f_ptr, dst_f_ptr.into(), src_fty)?; } else { self.unsize_into(Value::ByRef(src_f_ptr), src_fty, Lvalue::from_ptr(dst_f_ptr), dst_fty)?; } @@ -1561,13 +1570,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Err(err) => { panic!("Failed to access local: {:?}", err); } - Ok(Value::ByRef(PrimVal::Ptr(ptr))) => { - write!(msg, " by ref:").unwrap(); - allocs.push(ptr.alloc_id); - } - Ok(Value::ByRef(ptr)) => { - write!(msg, " integral by ref: {:?}", ptr).unwrap(); - } + Ok(Value::ByRef(ptr)) => match ptr.into_inner_primval() { + PrimVal::Ptr(ptr) => { + write!(msg, " by ref:").unwrap(); + allocs.push(ptr.alloc_id); + }, + ptr => write!(msg, " integral by ref: {:?}", ptr).unwrap(), + }, Ok(Value::ByVal(val)) => { write!(msg, " {:?}", val).unwrap(); if let PrimVal::Ptr(ptr) = val { allocs.push(ptr.alloc_id); } diff --git a/src/lvalue.rs b/src/lvalue.rs index de7d4d917160..3eafe1d0eabb 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -6,7 +6,7 @@ use rustc_data_structures::indexed_vec::Idx; use error::{EvalError, EvalResult}; use eval_context::{EvalContext}; use memory::MemoryPointer; -use value::{PrimVal, Value}; +use value::{PrimVal, Value, Pointer}; #[derive(Copy, Clone, Debug)] pub enum Lvalue<'tcx> { @@ -15,7 +15,7 @@ pub enum Lvalue<'tcx> { /// An lvalue may have an invalid (integral or undef) pointer, /// since it might be turned back into a reference /// before ever being dereferenced. - ptr: PrimVal, + ptr: Pointer, extra: LvalueExtra, }, @@ -64,18 +64,18 @@ pub struct Global<'tcx> { impl<'tcx> Lvalue<'tcx> { /// Produces an Lvalue that will error if attempted to be read from pub fn undef() -> Self { - Self::from_primval_ptr(PrimVal::Undef) + Self::from_primval_ptr(PrimVal::Undef.into()) } - pub(crate) fn from_primval_ptr(ptr: PrimVal) -> Self { + pub(crate) fn from_primval_ptr(ptr: Pointer) -> Self { Lvalue::Ptr { ptr, extra: LvalueExtra::None } } pub(crate) fn from_ptr(ptr: MemoryPointer) -> Self { - Self::from_primval_ptr(PrimVal::Ptr(ptr)) + Self::from_primval_ptr(ptr.into()) } - pub(super) fn to_ptr_and_extra(self) -> (PrimVal, LvalueExtra) { + pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) { match self { Lvalue::Ptr { ptr, extra } => (ptr, extra), _ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self), @@ -315,7 +315,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = match base_extra { LvalueExtra::Vtable(tab) => { - let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(base_ptr, PrimVal::Ptr(tab)))?; + let (_, align) = self.size_and_align_of_dst(base_ty, base_ptr.with_extra(PrimVal::Ptr(tab)))?; offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes() } _ => offset.bytes(), diff --git a/src/memory.rs b/src/memory.rs index 568da336f669..caf7f3a65161 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -6,7 +6,7 @@ use rustc::ty; use rustc::ty::layout::{self, TargetDataLayout}; use error::{EvalError, EvalResult}; -use value::{PrimVal, self}; +use value::{PrimVal, self, Pointer}; //////////////////////////////////////////////////////////////////////////////// // Allocations and pointers @@ -87,7 +87,7 @@ pub type TlsKey = usize; #[derive(Copy, Clone, Debug)] pub struct TlsEntry<'tcx> { - data: PrimVal, // Will eventually become a map from thread IDs to `PrimVal`s, if we ever support more than one thread. + data: Pointer, // Will eventually become a map from thread IDs to `Pointer`s, if we ever support more than one thread. dtor: Option>, } @@ -223,7 +223,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // TODO(solson): Track which allocations were returned from __rust_allocate and report an error // when reallocating/deallocating any others. - pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, Pointer> { + pub fn reallocate(&mut self, ptr: MemoryPointer, old_size: u64, old_align: u64, new_size: u64, new_align: u64) -> EvalResult<'tcx, MemoryPointer> { use std::cmp::min; // TODO(solson): Report error about non-__rust_allocate'd pointer. @@ -236,7 +236,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc" let new_ptr = self.allocate(new_size, new_align)?; - self.copy(PrimVal::Ptr(ptr), PrimVal::Ptr(new_ptr), min(old_size, new_size), min(old_align, new_align), /*nonoverlapping*/true)?; + self.copy(ptr.into(), new_ptr.into(), min(old_size, new_size), min(old_align, new_align), /*nonoverlapping*/true)?; self.deallocate(ptr, Some((old_size, old_align)))?; Ok(new_ptr) @@ -278,8 +278,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { self.layout.endian } - pub fn check_align(&self, ptr: PrimVal, align: u64, len: u64) -> EvalResult<'tcx> { - let offset = match ptr { + pub fn check_align(&self, ptr: Pointer, align: u64, len: u64) -> EvalResult<'tcx> { + let offset = match ptr.into_inner_primval() { PrimVal::Ptr(ptr) => { let alloc = self.get(ptr.alloc_id)?; // check whether the memory was marked as packed @@ -353,7 +353,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub(crate) fn create_tls_key(&mut self, dtor: Option>) -> TlsKey { let new_key = self.next_thread_local; self.next_thread_local += 1; - self.thread_local.insert(new_key, TlsEntry { data: PrimVal::Bytes(0), dtor }); + self.thread_local.insert(new_key, TlsEntry { data: Pointer::null(), dtor }); trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor); return new_key; } @@ -368,7 +368,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, PrimVal> { + pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> { return match self.thread_local.get(&key) { Some(&TlsEntry { data, .. }) => { trace!("TLS key {} loaded: {:?}", key, data); @@ -378,7 +378,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: PrimVal) -> EvalResult<'tcx> { + pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> { return match self.thread_local.get_mut(&key) { Some(&mut TlsEntry { ref mut data, .. }) => { trace!("TLS key {} stored: {:?}", key, new_data); @@ -407,7 +407,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// with associated destructors, implementations may stop calling destructors, /// or they may continue calling destructors until no non-NULL values with /// associated destructors exist, even though this might result in an infinite loop. - pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, PrimVal, TlsKey)>> { + pub(crate) fn fetch_tls_dtor(&mut self, key: Option) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, Pointer, TlsKey)>> { use std::collections::Bound::*; let start = match key { Some(key) => Excluded(key), @@ -417,7 +417,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if !data.is_null()? { if let Some(dtor) = dtor { let ret = Some((dtor, *data, key)); - *data = PrimVal::Bytes(0); + *data = Pointer::null(); return Ok(ret); } } @@ -575,7 +575,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&[]); } - self.check_align(PrimVal::Ptr(ptr), align, size)?; + // FIXME: check alignment for zst memory accesses? + self.check_align(ptr.into(), align, size)?; self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); @@ -588,7 +589,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { if size == 0 { return Ok(&mut []); } - self.check_align(PrimVal::Ptr(ptr), align, size)?; + // FIXME: check alignment for zst memory accesses? + self.check_align(ptr.into(), align, size)?; self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); @@ -609,7 +611,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_mut(&mut self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> { assert_ne!(size, 0); self.clear_relocations(ptr, size)?; - self.mark_definedness(PrimVal::Ptr(ptr), size, true)?; + self.mark_definedness(ptr.into(), size, true)?; self.get_bytes_unchecked_mut(ptr, size, align) } } @@ -661,7 +663,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn copy(&mut self, src: PrimVal, dest: PrimVal, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> { + pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> { if size == 0 { // TODO: Should we check for alignment here? (Also see write_bytes intrinsic) return Ok(()); @@ -713,7 +715,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn read_bytes(&self, ptr: PrimVal, size: u64) -> EvalResult<'tcx, &[u8]> { + pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> { if size == 0 { return Ok(&[]); } @@ -729,7 +731,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn write_repeat(&mut self, ptr: PrimVal, val: u8, count: u64) -> EvalResult<'tcx> { + pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> { if count == 0 { return Ok(()); } @@ -738,10 +740,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Ok(()) } - pub fn read_ptr(&self, ptr: MemoryPointer) -> EvalResult<'tcx, PrimVal> { + pub fn read_ptr(&self, ptr: MemoryPointer) -> EvalResult<'tcx, Pointer> { let size = self.pointer_size(); if self.check_defined(ptr, size).is_err() { - return Ok(PrimVal::Undef); + return Ok(PrimVal::Undef.into()); } let endianess = self.endianess(); let bytes = self.get_bytes_unchecked(ptr, size, size)?; @@ -750,8 +752,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { let offset = offset as u64; let alloc = self.get(ptr.alloc_id)?; match alloc.relocations.get(&ptr.offset) { - Some(&alloc_id) => Ok(PrimVal::Ptr(MemoryPointer::new(alloc_id, offset))), - None => Ok(PrimVal::Bytes(offset as u128)), + Some(&alloc_id) => Ok(PrimVal::Ptr(MemoryPointer::new(alloc_id, offset)).into()), + None => Ok(PrimVal::Bytes(offset as u128).into()), } } @@ -763,7 +765,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn write_primval( &mut self, - dest: PrimVal, + dest: Pointer, val: PrimVal, size: u64, ) -> EvalResult<'tcx> { @@ -970,7 +972,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn mark_definedness( &mut self, - ptr: PrimVal, + ptr: Pointer, size: u64, new_state: bool ) -> EvalResult<'tcx> { diff --git a/src/operator.rs b/src/operator.rs index c0369a785b1f..47bd66641b1e 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -158,8 +158,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match bin_op { Offset if left_kind == Ptr && right_kind == usize => { let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; - let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?; - return Ok((ptr, false)); + let ptr = self.pointer_offset(left.into(), pointee_ty, right.to_bytes()? as i64)?; + return Ok((ptr.into_inner_primval(), false)); }, // These work on anything Eq if left_kind == right_kind => { diff --git a/src/terminator/drop.rs b/src/terminator/drop.rs index 072a5d16a1be..e035aa58fd15 100644 --- a/src/terminator/drop.rs +++ b/src/terminator/drop.rs @@ -12,9 +12,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(crate) fn drop_lvalue(&mut self, lval: Lvalue<'tcx>, instance: ty::Instance<'tcx>, ty: Ty<'tcx>, span: Span) -> EvalResult<'tcx> { trace!("drop_lvalue: {:#?}", lval); let val = match self.force_allocation(lval)? { - Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => Value::ByValPair(ptr, PrimVal::Ptr(vtable)), - Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => Value::ByValPair(ptr, PrimVal::Bytes(len as u128)), - Lvalue::Ptr { ptr, extra: LvalueExtra::None } => Value::ByVal(ptr), + Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable) } => ptr.with_extra(PrimVal::Ptr(vtable)), + Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => ptr.with_extra(PrimVal::Bytes(len as u128)), + Lvalue::Ptr { ptr, extra: LvalueExtra::None } => ptr.to_value(), _ => bug!("force_allocation broken"), }; self.drop(val, instance, ty, span) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index f2b0446618a0..94c1bac459d6 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -7,7 +7,7 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; use lvalue::{Lvalue, LvalueExtra}; -use value::{PrimVal, PrimValKind, Value}; +use value::{PrimVal, PrimValKind, Value, Pointer}; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn call_intrinsic( @@ -46,7 +46,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?; - self.write_primval(dest, result_ptr, dest_ty)?; + self.write_ptr(dest, result_ptr, dest_ty)?; } "assume" => { @@ -257,8 +257,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(_) => Value::ByVal(PrimVal::Bytes(0)), Err(_) => { let ptr = this.alloc_ptr_with_substs(dest_ty, substs)?; - this.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; - Value::ByRef(PrimVal::Ptr(ptr)) + let ptr = Pointer::from(PrimVal::Ptr(ptr)); + this.memory.write_repeat(ptr, 0, size)?; + Value::ByRef(ptr) } }, Value::ByVal(_) => Value::ByVal(PrimVal::Bytes(0)), @@ -307,7 +308,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64; let ptr = arg_vals[0].read_ptr(&self.memory)?; let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?; - self.write_primval(dest, result_ptr, dest_ty)?; + self.write_ptr(dest, result_ptr, dest_ty)?; } "overflowing_sub" => { @@ -397,7 +398,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(dest_ty)?.expect("transmute() type must be sized"); let ptr = self.force_allocation(dest)?.to_ptr()?; self.memory.mark_packed(ptr, size); - self.write_value_to_ptr(arg_vals[0], PrimVal::Ptr(ptr), src_ty)?; + self.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)?; } "unchecked_shl" => { diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 63ab17514b96..9602e5798a7b 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -10,8 +10,7 @@ use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited}; use lvalue::Lvalue; use memory::{MemoryPointer, TlsKey}; -use value::PrimVal; -use value::Value; +use value::{PrimVal, Value}; use rustc_data_structures::indexed_vec::Idx; mod drop; @@ -569,7 +568,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align)); } let ptr = self.memory.allocate(size, align)?; - self.memory.write_repeat(PrimVal::Ptr(ptr), 0, size)?; + self.memory.write_repeat(ptr.into(), 0, size)?; self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; } "alloc::heap::::__rust_dealloc" => { @@ -705,7 +704,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?; let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_primval(arg_dest, data, u8_ptr_ty)?; + self.write_ptr(arg_dest, data, u8_ptr_ty)?; // We ourselves return 0 self.write_null(dest, dest_ty)?; @@ -744,7 +743,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?; - self.write_primval(dest, new_ptr, dest_ty)?; + self.write_ptr(dest, new_ptr, dest_ty)?; } else { self.write_null(dest, dest_ty)?; } @@ -756,7 +755,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { let new_ptr = ptr.offset(idx as u64, self.memory.layout)?; - self.write_primval(dest, new_ptr, dest_ty)?; + self.write_ptr(dest, new_ptr, dest_ty)?; } else { self.write_null(dest, dest_ty)?; } @@ -865,7 +864,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { "mmap" => { // This is a horrible hack, but well... the guard page mechanism calls mmap and expects a particular return value, so we give it that value let addr = args[0].read_ptr(&self.memory)?; - self.write_primval(dest, addr, dest_ty)?; + self.write_ptr(dest, addr, dest_ty)?; } // Hook pthread calls that go to the thread-local storage memory subsystem @@ -873,7 +872,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let key_ptr = args[0].read_ptr(&self.memory)?; // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - let dtor = match args[1].read_ptr(&self.memory)? { + let dtor = match args[1].read_ptr(&self.memory)?.into_inner_primval() { PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), PrimVal::Bytes(0) => None, PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), @@ -910,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey; let ptr = self.memory.load_tls(key)?; - self.write_primval(dest, ptr, dest_ty)?; + self.write_ptr(dest, ptr, dest_ty)?; } "pthread_setspecific" => { // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t diff --git a/src/value.rs b/src/value.rs index a783f9893644..e0937fcb8361 100644 --- a/src/value.rs +++ b/src/value.rs @@ -33,11 +33,70 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 { /// operations and fat pointers. This idea was taken from rustc's trans. #[derive(Clone, Copy, Debug)] pub enum Value { - ByRef(PrimVal), + ByRef(Pointer), ByVal(PrimVal), ByValPair(PrimVal, PrimVal), } +/// A wrapper type around `PrimVal` that cannot be turned back into a `PrimVal` accidentally. +/// This type clears up a few APIs where having a `PrimVal` argument for something that is +/// potentially an integer pointer or a pointer to an allocation was unclear. +#[derive(Clone, Copy, Debug)] +pub struct Pointer { + primval: PrimVal, +} + +impl<'tcx> Pointer { + pub fn null() -> Self { + PrimVal::Bytes(0).into() + } + pub fn to_ptr(self) -> EvalResult<'tcx, MemoryPointer> { + self.primval.to_ptr() + } + pub fn into_inner_primval(self) -> PrimVal { + self.primval + } + + pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + self.primval.signed_offset(i, layout).map(Pointer::from) + } + + pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + self.primval.offset(i, layout).map(Pointer::from) + } + + pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + self.primval.wrapping_signed_offset(i, layout).map(Pointer::from) + } + + pub fn is_null(self) -> EvalResult<'tcx, bool> { + match self.primval { + PrimVal::Bytes(b) => Ok(b == 0), + PrimVal::Ptr(_) => Ok(false), + PrimVal::Undef => Err(EvalError::ReadUndefBytes), + } + } + + pub fn with_extra(self, extra: PrimVal) -> Value { + Value::ByValPair(self.primval, extra) + } + pub fn to_value(self) -> Value { + Value::ByVal(self.primval) + } +} + +impl ::std::convert::From for Pointer { + fn from(primval: PrimVal) -> Self { + Pointer { primval } + } +} + +impl ::std::convert::From for Pointer { + fn from(ptr: MemoryPointer) -> Self { + PrimVal::Ptr(ptr).into() + } +} + /// A `PrimVal` represents an immediate, primitive value existing outside of a /// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in /// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes @@ -69,18 +128,18 @@ pub enum PrimValKind { } impl<'a, 'tcx: 'a> Value { - pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> { + pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> { use self::Value::*; match *self { ByRef(ptr) => mem.read_ptr(ptr.to_ptr()?), - ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr), + ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.into()), } } pub(super) fn expect_ptr_vtable_pair( &self, mem: &Memory<'a, 'tcx> - ) -> EvalResult<'tcx, (PrimVal, MemoryPointer)> { + ) -> EvalResult<'tcx, (Pointer, MemoryPointer)> { use self::Value::*; match *self { ByRef(ref_ptr) => { @@ -89,13 +148,13 @@ impl<'a, 'tcx: 'a> Value { Ok((ptr, vtable.to_ptr()?)) } - ByValPair(ptr, vtable) => Ok((ptr, vtable.to_ptr()?)), + ByValPair(ptr, vtable) => Ok((ptr.into(), vtable.to_ptr()?)), _ => bug!("expected ptr and vtable, got {:?}", self), } } - pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, u64)> { + pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> { use self::Value::*; match *self { ByRef(ref_ptr) => { @@ -106,7 +165,7 @@ impl<'a, 'tcx: 'a> Value { ByValPair(ptr, val) => { let len = val.to_u128()?; assert_eq!(len as u64 as u128, len); - Ok((ptr, len as u64)) + Ok((ptr.into(), len as u64)) }, ByVal(_) => unimplemented!(), } @@ -220,15 +279,7 @@ impl<'tcx> PrimVal { } } - pub fn is_null(self) -> EvalResult<'tcx, bool> { - match self { - PrimVal::Bytes(b) => Ok(b == 0), - PrimVal::Ptr(_) => Ok(false), - PrimVal::Undef => Err(EvalError::ReadUndefBytes), - } - } - - pub fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); @@ -239,7 +290,7 @@ impl<'tcx> PrimVal { } } - pub fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b); @@ -250,7 +301,7 @@ impl<'tcx> PrimVal { } } - pub fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { + pub(crate) fn wrapping_signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { match self { PrimVal::Bytes(b) => { assert_eq!(b as u64 as u128, b);