Use a wrapper type to differentiate between PrimVal and pointers

This commit is contained in:
Oliver Schneider 2017-07-04 14:33:15 +02:00
parent a8b957a0f8
commit 03f0a88002
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
8 changed files with 170 additions and 108 deletions

View File

@ -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<Value>> {
fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
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); }

View File

@ -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(),

View File

@ -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<ty::Instance<'tcx>>,
}
@ -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<ty::Instance<'tcx>>) -> 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<TlsKey>) -> EvalResult<'tcx, Option<(ty::Instance<'tcx>, PrimVal, TlsKey)>> {
pub(crate) fn fetch_tls_dtor(&mut self, key: Option<TlsKey>) -> 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> {

View File

@ -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 => {

View File

@ -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)

View File

@ -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" => {

View File

@ -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

View File

@ -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<PrimVal> for Pointer {
fn from(primval: PrimVal) -> Self {
Pointer { primval }
}
}
impl ::std::convert::From<MemoryPointer> 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);