use error::{EvalResult, EvalError};
use eval_context::EvalContext;
-use memory::Pointer;
use value::PrimVal;
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false),
- FnPtr | Ptr => self.cast_ptr(val.to_ptr()?, dest_ty),
+ FnPtr | Ptr => self.cast_ptr(val, dest_ty),
}
}
TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
TyChar => Err(EvalError::InvalidChar(v)),
- TyRawPtr(_) => Ok(PrimVal::Ptr(Pointer::from_int(v as u64))),
+ TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))),
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
}
}
}
- fn cast_ptr(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
+ fn cast_ptr(&self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use rustc::ty::TypeVariants::*;
match ty.sty {
TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) =>
- Ok(PrimVal::Ptr(ptr)),
+ Ok(ptr),
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
}
}
allocation_size: u64,
},
ReadPointerAsBytes,
+ ReadBytesAsPointer,
InvalidPointerMath,
ReadUndefBytes,
DeadLocal,
"pointer offset outside bounds of allocation",
EvalError::ReadPointerAsBytes =>
"a raw memory access tried to access part of a pointer value as raw bytes",
+ EvalError::ReadBytesAsPointer =>
+ "a memory access tried to interpret some bytes as a pointer",
EvalError::InvalidPointerMath =>
"attempted to do math or a comparison on pointers into different allocations",
EvalError::ReadUndefBytes =>
pub return_to_block: StackPopCleanup,
/// The location where the result of the current stack frame should be written to.
- pub return_lvalue: Lvalue<'tcx>,
+ /// None if the function is a diverging function
+ pub return_lvalue: Option<Lvalue<'tcx>>,
/// The list of locals for this stack frame, stored in order as
/// `[arguments..., variables..., temporaries...]`. The locals are stored as `Option<Value>`s.
instance: ty::Instance<'tcx>,
span: codemap::Span,
mir: &'tcx mir::Mir<'tcx>,
- return_lvalue: Lvalue<'tcx>,
+ return_lvalue: Option<Lvalue<'tcx>>,
return_to_block: StackPopCleanup,
) -> EvalResult<'tcx> {
::log_settings::settings().indentation += 1;
::log_settings::settings().indentation -= 1;
let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
match frame.return_to_block {
- StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue {
+ StackPopCleanup::MarkStatic(mutable) => if let Lvalue::Global(id) = frame.return_lvalue.expect("diverging static") {
let global_value = self.globals.get_mut(&id)
.expect("global should have been cached (static)");
match global_value.value {
where J::IntoIter: ExactSizeIterator,
{
// FIXME(solson)
- let dest_ptr = self.force_allocation(dest)?.to_ptr();
+ let dest_ptr = self.force_allocation(dest)?.to_ptr()?;
let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?;
self.memory.write_uint(discr_dest, discr_val, discr_size)?;
let dest = Lvalue::Ptr {
- ptr: dest_ptr,
+ ptr: PrimVal::Ptr(dest_ptr),
extra: LvalueExtra::DowncastVariant(variant_idx),
};
match *dest_layout {
Univariant { ref variant, .. } => {
if variant.packed {
- let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0;
+ let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
self.memory.mark_packed(ptr, variant.stride().bytes());
}
self.assign_fields(dest, dest_ty, operands)?;
.to_u128_unchecked();
let discr_size = discr.size().bytes();
if variants[variant].packed {
- let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0;
+ let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
self.memory.mark_packed(ptr, variants[variant].stride().bytes());
}
StructWrappedNullablePointer { nndiscr, ref nonnull, ref discrfield, .. } => {
if let mir::AggregateKind::Adt(_, variant, _, _) = **kind {
if nonnull.packed {
- let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0;
+ let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0.to_ptr()?;
self.memory.mark_packed(ptr, nonnull.stride().bytes());
}
if nndiscr == variant as u64 {
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
// FIXME(solson)
- let dest = self.force_allocation(dest)?.to_ptr();
+ let dest = self.force_allocation(dest)?.to_ptr()?;
let dest = dest.offset(offset.bytes(), self.memory.layout)?;
let dest_size = self.type_size(ty)?
let value = self.eval_operand(operand)?;
// FIXME(solson)
- let dest = self.force_allocation(dest)?.to_ptr();
+ let dest = self.force_allocation(dest)?.to_ptr()?;
for i in 0..length {
let elem_dest = dest.offset(i * elem_size, self.memory.layout)?;
Ref(_, _, ref lvalue) => {
let src = self.eval_lvalue(lvalue)?;
- let (raw_ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra();
- let ptr = PrimVal::Ptr(raw_ptr);
+ let (ptr, extra) = self.force_allocation(src)?.to_ptr_and_extra();
let val = match extra {
LvalueExtra::None => Value::ByVal(ptr),
Discriminant(ref lvalue) => {
let lval = self.eval_lvalue(lvalue)?;
let ty = self.lvalue_ty(lvalue);
- let ptr = self.force_allocation(lval)?.to_ptr();
+ let ptr = self.force_allocation(lval)?.to_ptr()?;
let discr_val = self.read_discriminant_value(ptr, ty)?;
if let ty::TyAdt(adt_def, _) = ty.sty {
if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) {
}
}
- pub(super) fn wrapping_pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
+ pub(super) fn wrapping_pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> {
// 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;
- Ok(ptr.wrapping_signed_offset(offset, self.memory.layout))
+ ptr.wrapping_signed_offset(offset, self.memory.layout)
}
- pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> {
+ pub(super) fn pointer_offset(&self, ptr: PrimVal, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, PrimVal> {
if offset == 0 {
// rustc relies on Offset-by-0 to be well-defined even for "bad" pointers like Unique::empty().
return Ok(ptr);
let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64;
return if let Some(offset) = offset.checked_mul(pointee_size) {
let ptr = ptr.signed_offset(offset, self.memory.layout)?;
- self.memory.check_bounds(ptr, false)?;
+ self.memory.check_bounds(ptr.to_ptr()?, false)?;
Ok(ptr)
} else {
Err(EvalError::OverflowingMath)
Lvalue::Ptr { ptr, extra } => {
assert_eq!(extra, LvalueExtra::None);
- self.write_value_to_ptr(src_val, ptr, dest_ty)
+ self.write_value_to_ptr(src_val, ptr.to_ptr()?, dest_ty)
}
Lvalue::Local { frame, local, field } => {
}
}
- fn read_ptr(&mut self, ptr: Pointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
+ pub(crate) fn read_ptr(&self, ptr: Pointer, pointee_ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
let p = self.memory.read_ptr(ptr)?;
if self.type_is_sized(pointee_ty) {
- Ok(Value::ByVal(PrimVal::Ptr(p)))
+ Ok(Value::ByVal(p))
} 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(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?),
+ ty::TyDynamic(..) => self.memory.read_ptr(extra)?,
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(PrimVal::Ptr(p), extra))
+ Ok(Value::ByValPair(p, extra))
}
}
ty::TyFloat(FloatTy::F32) => PrimVal::from_f32(self.memory.read_f32(ptr)?),
ty::TyFloat(FloatTy::F64) => PrimVal::from_f64(self.memory.read_f64(ptr)?),
- ty::TyFnPtr(_) => self.memory.read_ptr(ptr).map(PrimVal::Ptr)?,
+ ty::TyFnPtr(_) => self.memory.read_ptr(ptr)?,
ty::TyRef(_, ref tam) |
ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, tam.ty).map(Some),
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
let ptr = src.read_ptr(&self.memory)?;
let len = PrimVal::from_u128(length as u128);
- let ptr = PrimVal::Ptr(ptr);
self.write_value(Value::ByValPair(ptr, len), dest, dest_ty)
}
(&ty::TyDynamic(..), &ty::TyDynamic(..)) => {
let trait_ref = self.tcx.erase_regions(&trait_ref);
let vtable = self.get_vtable(src_pointee_ty, trait_ref)?;
let ptr = src.read_ptr(&self.memory)?;
- let ptr = PrimVal::Ptr(ptr);
let extra = PrimVal::Ptr(vtable);
self.write_value(Value::ByValPair(ptr, extra), dest, dest_ty)
},
};
// FIXME(solson)
- let dest = self.force_allocation(dest)?.to_ptr();
+ let dest = self.force_allocation(dest)?.to_ptr()?;
let iter = src_fields.zip(dst_fields).enumerate();
for (i, (src_f, dst_f)) in iter {
let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a);
start_instance,
start_mir.span,
start_mir,
- Lvalue::from_ptr(ret_ptr),
+ Some(Lvalue::from_ptr(ret_ptr)),
StackPopCleanup::None,
)?;
main_instance,
main_mir.span,
main_mir,
- Lvalue::from_ptr(Pointer::zst_ptr()),
+ Some(Lvalue::zst()),
StackPopCleanup::None,
)?;
}
pub enum Lvalue<'tcx> {
/// An lvalue referring to a value allocated in the `Memory` system.
Ptr {
- ptr: Pointer,
+ /// 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,
extra: LvalueExtra,
},
}
impl<'tcx> Lvalue<'tcx> {
+ pub fn zst() -> Self {
+ Self::from_ptr(Pointer::zst_ptr())
+ }
+
pub fn from_ptr(ptr: Pointer) -> Self {
- Lvalue::Ptr { ptr, extra: LvalueExtra::None }
+ Lvalue::Ptr { ptr: PrimVal::Ptr(ptr), extra: LvalueExtra::None }
}
- pub(super) fn to_ptr_and_extra(self) -> (Pointer, LvalueExtra) {
+ pub(super) fn to_ptr_and_extra(self) -> (PrimVal, LvalueExtra) {
match self {
Lvalue::Ptr { ptr, extra } => (ptr, extra),
_ => bug!("to_ptr_and_extra: expected Lvalue::Ptr, got {:?}", self),
}
}
- pub(super) fn to_ptr(self) -> Pointer {
+ pub(super) fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
let (ptr, extra) = self.to_ptr_and_extra();
assert_eq!(extra, LvalueExtra::None);
- ptr
+ ptr.to_ptr()
}
pub(super) fn elem_ty_and_len(self, ty: Ty<'tcx>) -> (Ty<'tcx>, u64) {
match lvalue {
Lvalue::Ptr { ptr, extra } => {
assert_eq!(extra, LvalueExtra::None);
- Ok(Value::ByRef(ptr))
+ Ok(Value::ByRef(ptr.to_ptr()?))
}
Lvalue::Local { frame, local, field } => {
self.stack[frame].get_local(local, field.map(|(i, _)| i))
pub(super) fn eval_lvalue(&mut self, mir_lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
use rustc::mir::Lvalue::*;
let lvalue = match *mir_lvalue {
- Local(mir::RETURN_POINTER) => self.frame().return_lvalue,
+ Local(mir::RETURN_POINTER) => self.frame().return_lvalue.expect("diverging function returned"),
Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None },
Static(ref static_) => {
Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i))? {
Value::ByRef(ptr) => {
assert!(field.is_none(), "local can't be ByRef and have a field offset");
- (ptr, LvalueExtra::None)
+ (PrimVal::Ptr(ptr), LvalueExtra::None)
},
Value::ByVal(PrimVal::Undef) => {
// FIXME: allocate in fewer cases
if self.ty_to_primval_kind(base_ty).is_ok() {
return Ok(base);
} else {
- (self.force_allocation(base)?.to_ptr(), LvalueExtra::None)
+ (PrimVal::Ptr(self.force_allocation(base)?.to_ptr()?), LvalueExtra::None)
}
},
Value::ByVal(_) => {
let offset = match base_extra {
LvalueExtra::Vtable(tab) => {
- let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?;
+ let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(base_ptr, PrimVal::Ptr(tab)))?;
offset.abi_align(Align::from_bytes(align, align).unwrap()).bytes()
}
_ => offset.bytes(),
if packed {
let size = self.type_size(field_ty)?.expect("packed struct must be sized");
- self.memory.mark_packed(ptr, size);
+ self.memory.mark_packed(ptr.to_ptr()?, size);
}
let extra = if self.type_is_sized(field_ty) {
use rustc::ty::layout::{self, TargetDataLayout};
use error::{EvalError, EvalResult};
-use value::PrimVal;
+use value::{PrimVal, self};
////////////////////////////////////////////////////////////////////////////////
// Allocations and pointers
}
pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self {
- Pointer::new(self.alloc_id, (self.offset.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64)
+ Pointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout))
}
pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
- // FIXME: is it possible to over/underflow here?
- if i < 0 {
- // trickery to ensure that i64::min_value() works fine
- // this formula only works for true negative values, it panics for zero!
- let n = u64::max_value() - (i as u64) + 1;
- if let Some(res) = self.offset.checked_sub(n) {
- Ok(Pointer::new(self.alloc_id, res))
- } else {
- Err(EvalError::OverflowingMath)
- }
- } else {
- self.offset(i as u64, layout)
- }
+ Ok(Pointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?))
}
pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
- if let Some(res) = self.offset.checked_add(i) {
- if res as u128 >= (1u128 << layout.pointer_size.bits()) {
- Err(EvalError::OverflowingMath)
- } else {
- Ok(Pointer::new(self.alloc_id, res))
- }
- } else {
- Err(EvalError::OverflowingMath)
- }
+ Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?))
}
pub fn points_to_zst(&self) -> bool {
self.alloc_id == ZST_ALLOC_ID
}
- pub fn to_int<'tcx>(&self) -> EvalResult<'tcx, u64> {
- match self.alloc_id {
- NEVER_ALLOC_ID => Ok(self.offset),
- _ => Err(EvalError::ReadPointerAsBytes),
- }
- }
-
- pub fn from_int(i: u64) -> Self {
- Pointer::new(NEVER_ALLOC_ID, i)
- }
-
pub fn zst_ptr() -> Self {
Pointer::new(ZST_ALLOC_ID, 0)
}
-
- pub fn never_ptr() -> Self {
- Pointer::new(NEVER_ALLOC_ID, 0)
- }
-
- pub fn is_null_ptr(&self) -> bool {
- return *self == Pointer::from_int(0)
- }
}
pub type TlsKey = usize;
#[derive(Copy, Clone, Debug)]
pub struct TlsEntry<'tcx> {
- data: Pointer, // Will eventually become a map from thread IDs to pointers, if we ever support more than one thread.
+ data: PrimVal, // Will eventually become a map from thread IDs to `PrimVal`s, if we ever support more than one thread.
dtor: Option<ty::Instance<'tcx>>,
}
}
const ZST_ALLOC_ID: AllocId = AllocId(0);
-const NEVER_ALLOC_ID: AllocId = AllocId(1);
impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self {
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: Pointer::from_int(0), dtor });
+ self.thread_local.insert(new_key, TlsEntry { data: PrimVal::Bytes(0), dtor });
trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
return new_key;
}
}
}
- pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> {
+ pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, PrimVal> {
return match self.thread_local.get(&key) {
Some(&TlsEntry { data, .. }) => {
trace!("TLS key {} loaded: {:?}", key, data);
}
}
- pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> {
+ pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: PrimVal) -> EvalResult<'tcx> {
return match self.thread_local.get_mut(&key) {
Some(&mut TlsEntry { ref mut data, .. }) => {
trace!("TLS key {} stored: {:?}", key, new_data);
}
}
- // Returns a dtor and its argument, if one is supposed to run
- pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, Pointer)> {
+ /// Returns a dtor and its argument, if one is supposed to run
+ pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, PrimVal)> {
for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() {
- if !data.is_null_ptr() {
+ if *data != PrimVal::Bytes(0) {
if let Some(dtor) = dtor {
- let old_data = *data;
- *data = Pointer::from_int(0);
- return Some((dtor, old_data));
+ let ret = Some((dtor, *data));
+ *data = PrimVal::Bytes(0);
+ return ret;
}
}
}
Some(alloc) => Ok(alloc),
None => match self.functions.get(&id) {
Some(_) => Err(EvalError::DerefFunctionPointer),
- None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
+ None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
None => Err(EvalError::DanglingPointerDeref),
}
}
},
None => match self.functions.get(&id) {
Some(_) => Err(EvalError::DerefFunctionPointer),
- None if id == NEVER_ALLOC_ID || id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
+ None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
None => Err(EvalError::DanglingPointerDeref),
}
}
let mut allocs_seen = HashSet::new();
while let Some(id) = allocs_to_print.pop_front() {
- if id == ZST_ALLOC_ID || id == NEVER_ALLOC_ID { continue; }
+ if id == ZST_ALLOC_ID { continue; }
let mut msg = format!("Alloc {:<5} ", format!("{}:", id));
let prefix_len = msg.len();
let mut relocations = vec![];
write!(msg, "{:1$}", "", ((i - pos) * 3) as usize).unwrap();
let target = match target_id {
ZST_ALLOC_ID => String::from("zst"),
- NEVER_ALLOC_ID => String::from("int ptr"),
_ => format!("({})", target_id),
};
// this `as usize` is fine, since we can't print more chars than `usize::MAX`
/// mark an allocation as being the entry point to a static (see `static_alloc` field)
pub fn mark_static(&mut self, alloc_id: AllocId) {
trace!("mark_static: {:?}", alloc_id);
- if alloc_id != NEVER_ALLOC_ID && alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) {
+ if alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) {
bug!("tried to mark an allocation ({:?}) as static twice", alloc_id);
}
}
// mark recursively
mem::replace(relocations, Default::default())
},
- None if alloc_id == NEVER_ALLOC_ID || alloc_id == ZST_ALLOC_ID => return Ok(()),
+ None if alloc_id == ZST_ALLOC_ID => return Ok(()),
None if !self.functions.contains_key(&alloc_id) => return Err(EvalError::DanglingPointerDeref),
_ => return Ok(()),
};
Ok(())
}
- pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, Pointer> {
+ pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<'tcx, PrimVal> {
let size = self.pointer_size();
- self.check_defined(ptr, size)?;
+ if self.check_defined(ptr, size).is_err() {
+ return Ok(PrimVal::Undef);
+ }
let endianess = self.endianess();
let bytes = self.get_bytes_unchecked(ptr, size, size)?;
let offset = read_target_uint(endianess, bytes).unwrap();
let offset = offset as u64;
let alloc = self.get(ptr.alloc_id)?;
match alloc.relocations.get(&ptr.offset) {
- Some(&alloc_id) => Ok(Pointer::new(alloc_id, offset)),
- None => Ok(Pointer::from_int(offset)),
+ Some(&alloc_id) => Ok(PrimVal::Ptr(Pointer::new(alloc_id, offset))),
+ None => Ok(PrimVal::Bytes(offset as u128)),
}
}
pub fn write_ptr(&mut self, dest: Pointer, ptr: Pointer) -> EvalResult<'tcx> {
self.write_usize(dest, ptr.offset as u64)?;
- if ptr.alloc_id != NEVER_ALLOC_ID {
- self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id);
- }
+ self.get_mut(dest.alloc_id)?.relocations.insert(dest.offset, ptr.alloc_id);
Ok(())
}
use error::{EvalError, EvalResult};
use eval_context::EvalContext;
use lvalue::Lvalue;
-use memory::Pointer;
use value::{
PrimVal,
PrimValKind,
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
+ use value::PrimValKind::*;
match $kind {
I8 => overflow!($int_op, l as i8, r as i8),
I16 => overflow!($int_op, l as i16, r as i16),
use rustc::mir::BinOp::*;
use value::PrimValKind::*;
- // FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store
- // plain bytes, and leave that to PrimVal::Bytes.
- fn normalize(val: PrimVal) -> PrimVal {
- if let PrimVal::Ptr(ptr) = val {
- if let Ok(bytes) = ptr.to_int() {
- return PrimVal::Bytes(bytes as u128);
- }
- }
- val
- }
- let (left, right) = (normalize(left), normalize(right));
-
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
if bin_op == Offset {
if left_kind == Ptr && right_kind == PrimValKind::from_uint_size(self.memory.pointer_size()) {
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.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?;
- return Ok((PrimVal::Ptr(ptr), false));
+ let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?;
+ return Ok((ptr, false));
} else {
bug!("Offset used with wrong type");
}
}
- let (l, r) = match (left, right) {
- (PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes),
-
- // One argument is a pointer value -- this is handled separately
- (PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => {
- return self.ptr_ops(bin_op, left_ptr, left_kind, right_ptr, right_kind);
- }
- (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) => {
- return self.ptr_ops(bin_op, ptr, left_kind, Pointer::from_int(bytes as u64), right_kind);
+ // unrelated pointer ops
+ let op: Option<fn(&PrimVal, &PrimVal) -> bool> = match bin_op {
+ Eq => Some(PrimVal::eq),
+ Ne => Some(PrimVal::ne),
+ _ => None,
+ };
+ if let Some(op) = op {
+ // only floats can't be binary compared
+ let ok = left_kind != F32 && left_kind != F64;
+ let ok = ok && right_kind != F32 && right_kind != F64;
+ if ok {
+ return Ok((PrimVal::from_bool(op(&left, &right)), false));
}
- (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => {
- return self.ptr_ops(bin_op, Pointer::from_int(bytes as u64), left_kind, ptr, right_kind);
+ }
+
+
+ if let (Ok(left), Ok(right)) = (left.to_ptr(), right.to_ptr()) {
+ if left.alloc_id == right.alloc_id {
+ return self.ptr_ops(
+ bin_op,
+ left.offset,
+ right.offset,
+ );
+ } else {
+ return Err(EvalError::InvalidPointerMath);
}
+ }
- (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
- };
+ let l = left.to_bytes()?;
+ let r = right.to_bytes()?;
// These ops can have an RHS with a different numeric type.
if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
fn ptr_ops(
&self,
bin_op: mir::BinOp,
- left: Pointer,
- left_kind: PrimValKind,
- right: Pointer,
- right_kind: PrimValKind,
+ left: u64,
+ right: u64,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
- use value::PrimValKind::*;
-
- if left_kind != right_kind || !(left_kind.is_ptr() || left_kind == PrimValKind::from_uint_size(self.memory.pointer_size())) {
- let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
- return Err(EvalError::Unimplemented(msg));
- }
let val = match bin_op {
Eq => PrimVal::from_bool(left == right),
Ne => PrimVal::from_bool(left != right),
Lt | Le | Gt | Ge => {
- if left.alloc_id == right.alloc_id {
- PrimVal::from_bool(match bin_op {
- Lt => left.offset < right.offset,
- Le => left.offset <= right.offset,
- Gt => left.offset > right.offset,
- Ge => left.offset >= right.offset,
- _ => bug!("We already established it has to be a comparison operator."),
- })
- } else {
- return Err(EvalError::InvalidPointerMath);
- }
+ PrimVal::from_bool(match bin_op {
+ Lt => left < right,
+ Le => left <= right,
+ Gt => left > right,
+ Ge => left >= right,
+ _ => bug!("We already established it has to be a comparison operator."),
+ })
}
Sub => {
- if left.alloc_id == right.alloc_id {
- return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset);
- } else {
- return Err(EvalError::InvalidPointerMath);
- }
+ let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
+ return int_arithmetic!(usize, overflowing_sub, left, right);
}
_ => {
return Err(EvalError::ReadPointerAsBytes);
use eval_context::{EvalContext, StackPopCleanup};
use lvalue::{Global, GlobalId, Lvalue};
use value::{Value, PrimVal};
-use memory::Pointer;
use syntax::codemap::Span;
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
instance,
mir.span,
mir,
- Lvalue::from_ptr(Pointer::zst_ptr()),
+ Some(Lvalue::zst()),
StackPopCleanup::None,
)?;
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_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?;
+ self.write_primval(dest, ptr, ty)?;
return Ok(true);
}
return Ok(false);
}
if self.ecx.tcx.has_attr(def_id, "linkage") {
trace!("Initializing an extern global with NULL");
- self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Ptr(Pointer::from_int(0))), !shared));
+ self.ecx.globals.insert(cid, Global::initialized(self.ecx.tcx.type_of(def_id), Value::ByVal(PrimVal::Bytes(0)), !shared));
return;
}
self.try(|this| {
instance,
span,
mir,
- Lvalue::Global(cid),
+ Some(Lvalue::Global(cid)),
cleanup,
)
});
this.ecx.push_stack_frame(this.instance,
constant.span,
mir,
- Lvalue::Global(cid),
+ Some(Lvalue::Global(cid)),
StackPopCleanup::MarkStatic(false),
)
});
use error::EvalResult;
use eval_context::{EvalContext, StackPopCleanup};
use lvalue::{Lvalue, LvalueExtra};
-use memory::Pointer;
use value::PrimVal;
use value::Value;
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(PrimVal::Ptr(ptr), PrimVal::Ptr(vtable)),
- Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len) } => Value::ByValPair(PrimVal::Ptr(ptr), PrimVal::Bytes(len as u128)),
- Lvalue::Ptr { ptr, extra: LvalueExtra::None } => Value::ByVal(PrimVal::Ptr(ptr)),
+ 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),
_ => bug!("force_allocation broken"),
};
self.drop(val, instance, ty, span)
instance,
span,
mir,
- Lvalue::from_ptr(Pointer::zst_ptr()),
+ Some(Lvalue::zst()),
StackPopCleanup::None,
)?;
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, PrimVal::Ptr(result_ptr), dest_ty)?;
+ self.write_primval(dest, result_ptr, dest_ty)?;
}
"assume" => {
"atomic_load_acq" |
"volatile_load" => {
let ty = substs.type_at(0);
- let ptr = arg_vals[0].read_ptr(&self.memory)?;
+ let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
self.write_value(Value::ByRef(ptr), dest, ty)?;
}
"atomic_store_rel" |
"volatile_store" => {
let ty = substs.type_at(0);
- let dest = arg_vals[0].read_ptr(&self.memory)?;
+ let dest = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
self.write_value_to_ptr(arg_vals[1], dest, ty)?;
}
_ if intrinsic_name.starts_with("atomic_xchg") => {
let ty = substs.type_at(0);
- let ptr = arg_vals[0].read_ptr(&self.memory)?;
+ let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
let change = self.value_to_primval(arg_vals[1], ty)?;
let old = self.read_value(ptr, ty)?;
let old = match old {
_ if intrinsic_name.starts_with("atomic_cxchg") => {
let ty = substs.type_at(0);
- let ptr = arg_vals[0].read_ptr(&self.memory)?;
+ let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
let expect_old = self.value_to_primval(arg_vals[1], ty)?;
let change = self.value_to_primval(arg_vals[2], ty)?;
let old = self.read_value(ptr, ty)?;
Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"),
};
let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?;
- let dest = self.force_allocation(dest)?.to_ptr();
+ let dest = self.force_allocation(dest)?.to_ptr()?;
self.write_pair_to_ptr(old, val, dest, dest_ty)?;
self.write_primval(Lvalue::from_ptr(ptr), change, ty)?;
}
"atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" |
"atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => {
let ty = substs.type_at(0);
- let ptr = arg_vals[0].read_ptr(&self.memory)?;
+ let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
let change = self.value_to_primval(arg_vals[1], ty)?;
let old = self.read_value(ptr, ty)?;
let old = match old {
// FIXME: check whether overlapping occurs
let elem_ty = substs.type_at(0);
let elem_size = self.type_size(elem_ty)?.expect("cannot copy unsized value");
- let elem_align = self.type_align(elem_ty)?;
- let src = arg_vals[0].read_ptr(&self.memory)?;
- let dest = arg_vals[1].read_ptr(&self.memory)?;
- let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
- self.memory.copy(src, dest, count * elem_size, elem_align)?;
+ if elem_size != 0 {
+ let elem_align = self.type_align(elem_ty)?;
+ let src = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
+ let dest = arg_vals[1].read_ptr(&self.memory)?.to_ptr()?;
+ let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
+ self.memory.copy(src, dest, count * elem_size, elem_align)?;
+ }
}
"ctpop" |
"discriminant_value" => {
let ty = substs.type_at(0);
- let adt_ptr = arg_vals[0].read_ptr(&self.memory)?;
+ let adt_ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?;
}
};
match dest {
Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), init)?,
- Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?,
+ Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr.to_ptr()?, 0, size)?,
Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"),
Lvalue::Global(cid) => self.modify_global(cid, init)?,
}
"move_val_init" => {
let ty = substs.type_at(0);
- let ptr = arg_vals[0].read_ptr(&self.memory)?;
+ let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
self.write_value_to_ptr(arg_vals[1], ptr, ty)?;
}
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, PrimVal::Ptr(result_ptr), dest_ty)?;
+ self.write_primval(dest, result_ptr, dest_ty)?;
}
"overflowing_sub" => {
let dest_align = self.type_align(dest_ty)?;
let size = self.type_size(dest_ty)?.expect("transmute() type must be sized");
if dest_align < src_align {
- let ptr = self.force_allocation(dest)?.to_ptr();
+ let ptr = self.force_allocation(dest)?.to_ptr()?;
self.memory.mark_packed(ptr, size);
self.write_value_to_ptr(arg_vals[0], ptr, dest_ty)?;
} else {
match dest {
Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?,
Lvalue::Ptr { ptr, extra: LvalueExtra::None } =>
- self.memory.mark_definedness(ptr, size, false)?,
+ self.memory.mark_definedness(ptr.to_ptr()?, size, false)?,
Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"),
Lvalue::Global(cid) => self.modify_global(cid, uninit)?,
}
let ty_align = self.type_align(ty)?;
let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8;
let size = self.type_size(ty)?.expect("write_bytes() type must be sized");
- let ptr = arg_vals[0].read_ptr(&self.memory)?;
+ let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
if count > 0 {
self.memory.check_align(ptr, ty_align, size * count)?;
use rustc::mir::TerminatorKind::*;
match terminator.kind {
Return => {
- self.dump_local(self.frame().return_lvalue);
+ self.dump_local(self.frame().return_lvalue.expect("diverging function returned"));
self.pop_stack_frame()?
}
let ptr_size = self.memory.pointer_size();
let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?;
let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?;
- let instance = self.memory.get_fn(fn_ptr.alloc_id)?;
+ let instance = self.memory.get_fn(fn_ptr.to_ptr()?.alloc_id)?;
let mut arg_operands = arg_operands.to_vec();
let ty = self.operand_ty(&arg_operands[0]);
let ty = self.get_field_ty(ty, 0)?;
Err(other) => return Err(other),
};
let (return_lvalue, return_to_block) = match destination {
- Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
- None => {
- // FIXME(solson)
- let lvalue = Lvalue::from_ptr(Pointer::never_ptr());
- (lvalue, StackPopCleanup::None)
- }
+ Some((lvalue, block)) => (Some(lvalue), StackPopCleanup::Goto(block)),
+ None => (None, StackPopCleanup::None),
};
self.push_stack_frame(
}
"__rust_deallocate" => {
- let ptr = args[0].read_ptr(&self.memory)?;
+ let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
// FIXME: insert sanity check for size and align?
let _old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
let _align = self.value_to_primval(args[2], usize)?.to_u64()?;
},
"__rust_reallocate" => {
- let ptr = args[0].read_ptr(&self.memory)?;
+ let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
let size = self.value_to_primval(args[2], usize)?.to_u64()?;
let align = self.value_to_primval(args[3], usize)?.to_u64()?;
let new_ptr = self.memory.reallocate(ptr, size, align)?;
// fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32
// We abort on panic, so not much is going on here, but we still have to call the closure
let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
- let f = args[0].read_ptr(&self.memory)?;
+ let f = args[0].read_ptr(&self.memory)?.to_ptr()?;
let data = args[1].read_ptr(&self.memory)?;
let f_instance = self.memory.get_fn(f.alloc_id)?;
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
f_instance,
mir.span,
mir,
- Lvalue::from_ptr(Pointer::zst_ptr()),
+ Some(Lvalue::zst()),
StackPopCleanup::Goto(dest_block),
)?;
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_value(Value::ByVal(PrimVal::Ptr(data)), arg_dest, u8_ptr_ty)?;
+ self.write_primval(arg_dest, data, u8_ptr_ty)?;
// We ourselbes return 0
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
}
"memcmp" => {
- let left = args[0].read_ptr(&self.memory)?;
- let right = args[1].read_ptr(&self.memory)?;
+ let left = args[0].read_ptr(&self.memory)?.to_ptr()?;
+ let right = args[1].read_ptr(&self.memory)?.to_ptr()?;
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
let result = {
}
"memrchr" => {
- let ptr = args[0].read_ptr(&self.memory)?;
+ let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
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) {
}
"memchr" => {
- let ptr = args[0].read_ptr(&self.memory)?;
+ let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
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) {
"getenv" => {
{
- let name_ptr = args[0].read_ptr(&self.memory)?;
+ let name_ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
let name = self.memory.read_c_str(name_ptr)?;
info!("ignored env var request for `{:?}`", ::std::str::from_utf8(name));
}
"write" => {
let fd = self.value_to_primval(args[0], usize)?.to_u64()?;
- let buf = args[1].read_ptr(&self.memory)?;
+ let buf = args[1].read_ptr(&self.memory)?.to_ptr()?;
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
let result = if fd == 1 || fd == 2 { // stdout/stderr
"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, PrimVal::Ptr(addr), dest_ty)?;
+ self.write_primval(dest, addr, dest_ty)?;
}
// Hook pthread calls that go to the thread-local storage memory subsystem
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_ptr = args[1].read_ptr(&self.memory)?;
- let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) };
+ let dtor = match args[1].read_ptr(&self.memory)? {
+ PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr.alloc_id)?),
+ PrimVal::Bytes(0) => None,
+ PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer),
+ PrimVal::Undef => return Err(EvalError::ReadUndefBytes),
+ };
// Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
let key_type = self.operand_ty(&arg_operands[0]).builtin_deref(true, ty::LvaluePreference::NoPreference)
return Err(EvalError::OutOfTls);
}
// TODO: Does this need checking for alignment?
- self.memory.write_uint(key_ptr, key, key_size.bytes())?;
+ self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?;
// Return success (0)
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
// 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, PrimVal::Ptr(ptr), dest_ty)?;
+ self.write_primval(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
use eval_context::EvalContext;
use memory::Pointer;
+use value::{Value, PrimVal};
use rustc::hir::def_id::DefId;
use rustc::ty::subst::Substs;
use syntax::codemap::DUMMY_SP;
use syntax::ast;
-use error::EvalResult;
+use error::{EvalResult, EvalError};
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
pub fn read_drop_type_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, Option<ty::Instance<'tcx>>> {
- let drop_fn = self.memory.read_ptr(vtable)?;
-
- // just a sanity check
- assert_eq!(drop_fn.offset, 0);
-
- // some values don't need to call a drop impl, so the value is null
- if drop_fn == Pointer::from_int(0) {
- Ok(None)
- } else {
- self.memory.get_fn(drop_fn.alloc_id).map(Some)
+ // we don't care about the pointee type, we just want a pointer
+ match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? {
+ // some values don't need to call a drop impl, so the value is null
+ Value::ByVal(PrimVal::Bytes(0)) => Ok(None),
+ Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn.alloc_id).map(Some),
+ _ => Err(EvalError::ReadBytesAsPointer),
}
}
#![allow(float_cmp)]
use std::mem::transmute;
+use rustc::ty::layout::TargetDataLayout;
use error::{EvalError, EvalResult};
use memory::{Memory, Pointer};
/// `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
/// of a simple value, a pointer into another `Allocation`, or be undefined.
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PrimVal {
/// The raw bytes of a simple value.
Bytes(u128),
}
impl<'a, 'tcx: 'a> Value {
- pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
+ pub(super) fn read_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, PrimVal> {
use self::Value::*;
match *self {
ByRef(ptr) => mem.read_ptr(ptr),
- ByVal(ptr) | ByValPair(ptr, _) => ptr.to_ptr(),
+ ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr),
}
}
pub(super) fn expect_ptr_vtable_pair(
&self,
mem: &Memory<'a, 'tcx>
- ) -> EvalResult<'tcx, (Pointer, Pointer)> {
+ ) -> EvalResult<'tcx, (PrimVal, Pointer)> {
use self::Value::*;
match *self {
ByRef(ref_ptr) => {
let ptr = mem.read_ptr(ref_ptr)?;
let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?;
- Ok((ptr, vtable))
+ Ok((ptr, vtable.to_ptr()?))
}
- ByValPair(ptr, vtable) => Ok((ptr.to_ptr()?, vtable.to_ptr()?)),
+ ByValPair(ptr, vtable) => Ok((ptr, vtable.to_ptr()?)),
_ => bug!("expected ptr and vtable, got {:?}", self),
}
}
- pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> {
+ pub(super) fn expect_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (PrimVal, u64)> {
use self::Value::*;
match *self {
ByRef(ref_ptr) => {
ByValPair(ptr, val) => {
let len = val.to_u128()?;
assert_eq!(len as u64 as u128, len);
- Ok((ptr.to_ptr()?, len as u64))
+ Ok((ptr, len as u64))
},
_ => unimplemented!(),
}
pub fn to_bytes(self) -> EvalResult<'tcx, u128> {
match self {
PrimVal::Bytes(b) => Ok(b),
- PrimVal::Ptr(p) => p.to_int().map(|b| b as u128),
+ PrimVal::Ptr(_) => Err(EvalError::ReadPointerAsBytes),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
}
}
pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> {
match self {
- PrimVal::Bytes(b) => Ok(Pointer::from_int(b as u64)),
+ PrimVal::Bytes(_) => Err(EvalError::ReadBytesAsPointer),
PrimVal::Ptr(p) => Ok(p),
PrimVal::Undef => Err(EvalError::ReadUndefBytes),
}
_ => Err(EvalError::InvalidBool),
}
}
+
+ pub fn signed_offset(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
+ match self {
+ PrimVal::Bytes(b) => {
+ assert_eq!(b as u64 as u128, b);
+ Ok(PrimVal::Bytes(signed_offset(b as u64, i, layout)? as u128))
+ },
+ PrimVal::Ptr(ptr) => ptr.signed_offset(i, layout).map(PrimVal::Ptr),
+ PrimVal::Undef => Err(EvalError::ReadUndefBytes),
+ }
+ }
+
+ pub fn offset(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
+ match self {
+ PrimVal::Bytes(b) => {
+ assert_eq!(b as u64 as u128, b);
+ Ok(PrimVal::Bytes(offset(b as u64, i, layout)? as u128))
+ },
+ PrimVal::Ptr(ptr) => ptr.offset(i, layout).map(PrimVal::Ptr),
+ PrimVal::Undef => Err(EvalError::ReadUndefBytes),
+ }
+ }
+
+ pub 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);
+ Ok(PrimVal::Bytes(wrapping_signed_offset(b as u64, i, layout) as u128))
+ },
+ PrimVal::Ptr(ptr) => Ok(PrimVal::Ptr(ptr.wrapping_signed_offset(i, layout))),
+ PrimVal::Undef => Err(EvalError::ReadUndefBytes),
+ }
+ }
+}
+
+pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> {
+ // FIXME: is it possible to over/underflow here?
+ if i < 0 {
+ // trickery to ensure that i64::min_value() works fine
+ // this formula only works for true negative values, it panics for zero!
+ let n = u64::max_value() - (i as u64) + 1;
+ val.checked_sub(n).ok_or(EvalError::OverflowingMath)
+ } else {
+ offset(val, i as u64, layout)
+ }
+}
+
+pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> {
+ if let Some(res) = val.checked_add(i) {
+ if res as u128 >= (1u128 << layout.pointer_size.bits()) {
+ Err(EvalError::OverflowingMath)
+ } else {
+ Ok(res)
+ }
+ } else {
+ Err(EvalError::OverflowingMath)
+ }
+}
+
+pub fn wrapping_signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> u64 {
+ (val.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64
}
impl PrimValKind {
std::mem::transmute::<&usize, &fn(i32)>(&b)
};
- (*g)(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer
+ (*g)(42) //~ ERROR a memory access tried to interpret some bytes as a pointer
}
std::mem::transmute::<usize, fn(i32)>(42)
};
- g(42) //~ ERROR tried to use an integer pointer or a dangling pointer as a function pointer
+ g(42) //~ ERROR a memory access tried to interpret some bytes as a pointer
}
fn main() {
- let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: tried to access memory through an invalid pointer
+ let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: a memory access tried to interpret some bytes as a pointer
panic!("this should never print: {}", x);
}
fn main() {
let p = 42 as *const i32;
- let x = unsafe { *p }; //~ ERROR: tried to access memory through an invalid pointer
+ let x = unsafe { *p }; //~ ERROR: a memory access tried to interpret some bytes as a pointer
panic!("this should never print: {}", x);
}