Merge pull request #274 from RalfJung/packed2

make force_allocation handle packed ByValPair
This commit is contained in:
Oliver Schneider 2017-07-31 12:41:29 +02:00 committed by GitHub
commit 7c6befe69f
9 changed files with 189 additions and 141 deletions

View file

@ -142,6 +142,12 @@ impl Default for ResourceLimits {
}
}
#[derive(Copy, Clone, Debug)]
pub struct TyAndPacked<'tcx> {
pub ty: Ty<'tcx>,
pub packed: bool,
}
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
EvalContext {
@ -381,7 +387,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
.expect("global should have been cached (static)");
match global_value.value {
// FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions
Value::ByRef(ptr, _aligned) =>
Value::ByRef { ptr, aligned: _aligned } =>
// Alignment does not matter for this call
self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, mutable)?,
Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val {
@ -439,7 +445,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
pub fn deallocate_local(&mut self, local: Option<Value>) -> EvalResult<'tcx> {
if let Some(Value::ByRef(ptr, _aligned)) = local {
if let Some(Value::ByRef { ptr, aligned: _ }) = local {
trace!("deallocating local");
let ptr = ptr.to_ptr()?;
self.memory.dump_alloc(ptr.alloc_id);
@ -609,7 +615,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let operand_ty = self.operand_ty(operand);
assert_eq!(self.type_size(operand_ty)?, Some(0));
}
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
let (offset, TyAndPacked { ty, packed: _}) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
// TODO: The packed flag is ignored
// FIXME(solson)
let dest = self.force_allocation(dest)?.to_ptr()?;
@ -702,7 +709,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
LvalueExtra::DowncastVariant(..) =>
bug!("attempted to take a reference to an enum downcast lvalue"),
};
self.write_value(val, dest, dest_ty)?;
}
@ -745,7 +751,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let src_ty = self.operand_ty(operand);
if self.type_is_fat_ptr(src_ty) {
match (src, self.type_is_fat_ptr(dest_ty)) {
(Value::ByRef(..), _) |
(Value::ByRef{..}, _) |
(Value::ByValPair(..), true) => {
self.write_value(src, dest, dest_ty)?;
},
@ -826,7 +832,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
ty: Ty<'tcx>,
nndiscr: u64,
discrfield: &[u32],
) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
) -> EvalResult<'tcx, (Size, TyAndPacked<'tcx>)> {
// Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant
let path = discrfield.iter().skip(2).map(|&i| i as usize);
@ -849,16 +855,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
mut offset: Size,
mut ty: Ty<'tcx>,
path: I,
) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
) -> EvalResult<'tcx, (Size, TyAndPacked<'tcx>)> {
// Skip the initial 0 intended for LLVM GEP.
let mut packed = false;
for field_index in path {
let field_offset = self.get_field_offset(ty, field_index)?;
trace!("field_path_offset_and_ty: {}, {}, {:?}, {:?}", field_index, ty, field_offset, offset);
ty = self.get_field_ty(ty, field_index)?;
let field_ty = self.get_field_ty(ty, field_index)?;
ty = field_ty.ty;
packed = packed || field_ty.packed;
offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap();
}
Ok((offset, ty))
Ok((offset, TyAndPacked { ty, packed }))
}
fn get_fat_field(&self, pointee_ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
match (field_index, &self.tcx.struct_tail(pointee_ty).sty) {
@ -870,33 +879,46 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
/// Returns the field type and whether the field is packed
pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, TyAndPacked<'tcx>> {
match ty.sty {
ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index),
ty::TyAdt(adt_def, _) if adt_def.is_box() =>
Ok(TyAndPacked { ty: self.get_fat_field(ty.boxed_ty(), field_index)?, packed: false }),
ty::TyAdt(adt_def, substs) if adt_def.is_enum() => {
use rustc::ty::layout::Layout::*;
match *self.type_layout(ty)? {
RawNullablePointer { nndiscr, .. } |
StructWrappedNullablePointer { nndiscr, .. } => Ok(adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs)),
RawNullablePointer { nndiscr, .. } =>
Ok(TyAndPacked { ty: adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs), packed: false }),
StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs);
Ok(TyAndPacked { ty, packed: nonnull.packed })
},
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
}
}
ty::TyAdt(adt_def, substs) => {
Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
let variant_def = adt_def.struct_variant();
use rustc::ty::layout::Layout::*;
match *self.type_layout(ty)? {
Univariant { ref variant, .. } =>
Ok(TyAndPacked { ty: variant_def.fields[field_index].ty(self.tcx, substs), packed: variant.packed }),
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))),
}
}
ty::TyTuple(fields, _) => Ok(fields[field_index]),
ty::TyTuple(fields, _) => Ok(TyAndPacked { ty: fields[field_index], packed: false }),
ty::TyRef(_, ref tam) |
ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index),
ty::TyRawPtr(ref tam) => Ok(TyAndPacked { ty: self.get_fat_field(tam.ty, field_index)?, packed: false }),
ty::TyArray(ref inner, _) => Ok(inner),
ty::TyArray(ref inner, _) => Ok(TyAndPacked { ty: inner, packed: false }),
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
}
}
fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> {
// Also see lvalue_field in lvalue.rs, which handles more cases but needs an actual value at the given type
let layout = self.type_layout(ty)?;
use rustc::ty::layout::Layout::*;
@ -1026,7 +1048,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// -1 since we don't store the return value
match self.stack[frame].locals[local.index() - 1] {
None => return Err(EvalError::DeadLocal),
Some(Value::ByRef(ptr, aligned)) => {
Some(Value::ByRef { ptr, aligned }) => {
Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None }
},
Some(val) => {
@ -1044,7 +1066,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Lvalue::Global(cid) => {
let global_val = self.globals.get(&cid).expect("global not cached").clone();
match global_val.value {
Value::ByRef(ptr, aligned) =>
Value::ByRef { ptr, aligned } =>
Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None },
_ => {
let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?;
@ -1070,7 +1092,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
/// ensures this Value is not a ByRef
pub(super) fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
match value {
Value::ByRef(ptr, aligned) => {
Value::ByRef { ptr, aligned } => {
self.read_maybe_aligned(aligned, |ectx| ectx.read_value(ptr, ty))
}
other => Ok(other),
@ -1079,7 +1101,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
match self.follow_by_ref_value(value, ty)? {
Value::ByRef(..) => bug!("follow_by_ref_value can't result in `ByRef`"),
Value::ByRef{..} => bug!("follow_by_ref_value can't result in `ByRef`"),
Value::ByVal(primval) => {
self.ensure_valid_value(primval, ty)?;
@ -1145,7 +1167,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Lvalue::Ptr { ptr, extra, aligned } => {
assert_eq!(extra, LvalueExtra::None);
self.write_maybe_aligned(aligned,
self.write_maybe_aligned_mut(aligned,
|ectx| ectx.write_value_to_ptr(src_val, ptr, dest_ty))
}
@ -1169,7 +1191,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
old_dest_val: Value,
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx> {
if let Value::ByRef(dest_ptr, aligned) = old_dest_val {
if let Value::ByRef { ptr: dest_ptr, aligned } = old_dest_val {
// If the value is already `ByRef` (that is, backed by an `Allocation`),
// then we must write the new value into this allocation, because there may be
// other pointers into the allocation. These other pointers are logically
@ -1177,10 +1199,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
//
// Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we
// knew for certain that there were no outstanding pointers to this allocation.
self.write_maybe_aligned(aligned,
self.write_maybe_aligned_mut(aligned,
|ectx| ectx.write_value_to_ptr(src_val, dest_ptr, dest_ty))?;
} else if let Value::ByRef(src_ptr, aligned) = src_val {
} else if let Value::ByRef { ptr: src_ptr, aligned } = src_val {
// If the value is not `ByRef`, then we know there are no pointers to it
// and we can simply overwrite the `Value` in the locals array directly.
//
@ -1192,7 +1214,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// It is a valid optimization to attempt reading a primitive value out of the
// source and write that into the destination without making an allocation, so
// we do so here.
self.read_maybe_aligned(aligned, |ectx| {
self.read_maybe_aligned_mut(aligned, |ectx| {
if let Ok(Some(src_val)) = ectx.try_read_value(src_ptr, dest_ty) {
write_dest(ectx, src_val)?;
} else {
@ -1218,8 +1240,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx> {
match value {
Value::ByRef(ptr, aligned) => {
self.read_maybe_aligned(aligned, |ectx| ectx.copy(ptr, dest, dest_ty))
Value::ByRef { ptr, aligned } => {
self.read_maybe_aligned_mut(aligned, |ectx| ectx.copy(ptr, dest, dest_ty))
},
Value::ByVal(primval) => {
let size = self.type_size(dest_ty)?.expect("dest type must be sized");
@ -1236,20 +1258,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
ptr: MemoryPointer,
mut ty: Ty<'tcx>
) -> EvalResult<'tcx> {
let mut packed = false;
while self.get_field_count(ty)? == 1 {
ty = self.get_field_ty(ty, 0)?;
let field = self.get_field_ty(ty, 0)?;
ty = field.ty;
packed = packed || field.packed;
}
assert_eq!(self.get_field_count(ty)?, 2);
let field_0 = self.get_field_offset(ty, 0)?.bytes();
let field_1 = self.get_field_offset(ty, 1)?.bytes();
let field_0 = self.get_field_offset(ty, 0)?;
let field_1 = self.get_field_offset(ty, 1)?;
let field_0_ty = self.get_field_ty(ty, 0)?;
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");
let field_0_ptr = ptr.offset(field_0, &self)?.into();
let field_1_ptr = ptr.offset(field_1, &self)?.into();
self.memory.write_primval(field_0_ptr, a, field_0_size)?;
self.memory.write_primval(field_1_ptr, b, field_1_size)?;
assert_eq!(field_0_ty.packed, field_1_ty.packed, "the two fields must agree on being packed");
packed = packed || field_0_ty.packed;
let field_0_size = self.type_size(field_0_ty.ty)?.expect("pair element type must be sized");
let field_1_size = self.type_size(field_1_ty.ty)?.expect("pair element type must be sized");
let field_0_ptr = ptr.offset(field_0.bytes(), &self)?.into();
let field_1_ptr = ptr.offset(field_1.bytes(), &self)?.into();
self.write_maybe_aligned_mut(!packed,
|ectx| ectx.memory.write_primval(field_0_ptr, a, field_0_size))?;
self.write_maybe_aligned_mut(!packed,
|ectx| ectx.memory.write_primval(field_1_ptr, b, field_1_size))?;
Ok(())
}
@ -1352,7 +1381,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
pub(super) fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
pub(super) fn read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
if let Some(val) = self.try_read_value(ptr, ty)? {
Ok(val)
} else {
@ -1376,7 +1405,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
}
fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
fn try_read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
use syntax::ast::FloatTy;
let val = match ty.sty {
@ -1488,7 +1517,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
let ptr = src.into_ptr(&mut self.memory)?;
let ptr = src.into_ptr(&self.memory)?;
// u64 cast is from usize to u64, which is always good
self.write_value(ptr.to_value_with_len(length as u64), dest, dest_ty)
}
@ -1502,7 +1531,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty);
let trait_ref = self.tcx.erase_regions(&trait_ref);
let vtable = self.get_vtable(src_pointee_ty, trait_ref)?;
let ptr = src.into_ptr(&mut self.memory)?;
let ptr = src.into_ptr(&self.memory)?;
self.write_value(ptr.to_value_with_vtable(vtable), dest, dest_ty)
},
@ -1529,8 +1558,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty());
}
if self.ty_to_primval_kind(src_ty).is_ok() {
let sty = self.get_field_ty(src_ty, 0)?;
let dty = self.get_field_ty(dest_ty, 0)?;
// TODO: We ignore the packed flag here
let sty = self.get_field_ty(src_ty, 0)?.ty;
let dty = self.get_field_ty(dest_ty, 0)?.ty;
return self.unsize_into(src, sty, dest, dty);
}
// unsizing of generic struct with pointer fields
@ -1545,7 +1575,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
//let src = adt::MaybeSizedValue::sized(src);
//let dst = adt::MaybeSizedValue::sized(dst);
let src_ptr = match src {
Value::ByRef(ptr, true) => ptr,
Value::ByRef { ptr, aligned: true } => ptr,
// TODO: Is it possible for unaligned pointers to occur here?
_ => bug!("expected aligned pointer, got {:?}", src),
};
@ -1592,7 +1622,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Err(err) => {
panic!("Failed to access local: {:?}", err);
}
Ok(Value::ByRef(ptr, aligned)) => match ptr.into_inner_primval() {
Ok(Value::ByRef { ptr, aligned }) => match ptr.into_inner_primval() {
PrimVal::Ptr(ptr) => {
write!(msg, " by {}ref:", if aligned { "" } else { "unaligned " }).unwrap();
allocs.push(ptr.alloc_id);

View file

@ -196,7 +196,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match lvalue {
Lvalue::Ptr { ptr, extra, aligned } => {
assert_eq!(extra, LvalueExtra::None);
Ok(Value::ByRef(ptr, aligned))
Ok(Value::ByRef { ptr, aligned })
}
Lvalue::Local { frame, local } => {
self.stack[frame].get_local(local)
@ -305,7 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
return Ok(base);
},
Value::ByRef(..) |
Value::ByRef{..} |
Value::ByValPair(..) |
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
},
@ -315,7 +315,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
return Ok(base);
},
Value::ByRef(..) |
Value::ByRef{..} |
Value::ByValPair(..) |
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
},
@ -349,17 +349,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(Lvalue::Ptr { ptr, extra, aligned: aligned && !packed })
}
pub(super) fn val_to_lvalue(&mut self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
pub(super) fn val_to_lvalue(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
Ok(match self.tcx.struct_tail(ty).sty {
ty::TyDynamic(..) => {
let (ptr, vtable) = val.into_ptr_vtable_pair(&mut self.memory)?;
let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?;
Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: true }
},
ty::TyStr | ty::TySlice(_) => {
let (ptr, len) = val.into_slice(&mut self.memory)?;
let (ptr, len) = val.into_slice(&self.memory)?;
Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: true }
},
_ => Lvalue::Ptr { ptr: val.into_ptr(&mut self.memory)?, extra: LvalueExtra::None, aligned: true },
_ => Lvalue::Ptr { ptr: val.into_ptr(&self.memory)?, extra: LvalueExtra::None, aligned: true },
})
}

View file

@ -1,6 +1,7 @@
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
use std::{fmt, iter, ptr, mem, io, ops};
use std::cell::Cell;
use rustc::ty;
use rustc::ty::layout::{self, TargetDataLayout, HasDataLayout};
@ -266,8 +267,8 @@ pub struct Memory<'a, 'tcx> {
/// To avoid having to pass flags to every single memory access, we have some global state saying whether
/// alignment checking is currently enforced for read and/or write accesses.
reads_are_aligned: bool,
writes_are_aligned: bool,
reads_are_aligned: Cell<bool>,
writes_are_aligned: Cell<bool>,
/// The current stack frame. Used to check accesses against locks.
cur_frame: usize,
@ -287,8 +288,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
literal_alloc_cache: HashMap::new(),
thread_local: BTreeMap::new(),
next_thread_local: 0,
reads_are_aligned: true,
writes_are_aligned: true,
reads_are_aligned: Cell::new(true),
writes_are_aligned: Cell::new(true),
cur_frame: usize::max_value(),
}
}
@ -796,7 +797,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
impl<'a, 'tcx> Memory<'a, 'tcx> {
fn get_bytes_unchecked(&self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> {
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
if self.reads_are_aligned {
if self.reads_are_aligned.get() {
self.check_align(ptr.into(), align)?;
}
if size == 0 {
@ -813,7 +814,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
fn get_bytes_unchecked_mut(&mut self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> {
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
if self.writes_are_aligned {
if self.writes_are_aligned.get() {
self.check_align(ptr.into(), align)?;
}
if size == 0 {
@ -909,10 +910,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> {
if size == 0 {
// Empty accesses don't need to be valid pointers, but they should still be aligned
if self.reads_are_aligned {
if self.reads_are_aligned.get() {
self.check_align(src, align)?;
}
if self.writes_are_aligned {
if self.writes_are_aligned.get() {
self.check_align(dest, align)?;
}
return Ok(());
@ -968,7 +969,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> {
if size == 0 {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
if self.reads_are_aligned {
if self.reads_are_aligned.get() {
self.check_align(ptr, 1)?;
}
return Ok(&[]);
@ -979,7 +980,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx> {
if src.is_empty() {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
if self.writes_are_aligned {
if self.writes_are_aligned.get() {
self.check_align(ptr, 1)?;
}
return Ok(());
@ -992,7 +993,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> {
if count == 0 {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
if self.writes_are_aligned {
if self.writes_are_aligned.get() {
self.check_align(ptr, 1)?;
}
return Ok(());
@ -1399,23 +1400,36 @@ pub(crate) trait HasMemory<'a, 'tcx> {
fn memory(&self) -> &Memory<'a, 'tcx>;
// These are not supposed to be overriden.
fn read_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
fn read_maybe_aligned<F, T>(&self, aligned: bool, f: F) -> EvalResult<'tcx, T>
where F: FnOnce(&Self) -> EvalResult<'tcx, T>
{
assert!(self.memory_mut().reads_are_aligned, "Unaligned reads must not be nested");
self.memory_mut().reads_are_aligned = aligned;
let old = self.memory().reads_are_aligned.get();
// Do alignment checking if *all* nested calls say it has to be aligned.
self.memory().reads_are_aligned.set(old && aligned);
let t = f(self);
self.memory_mut().reads_are_aligned = true;
self.memory().reads_are_aligned.set(old);
t
}
fn write_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
fn read_maybe_aligned_mut<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
{
assert!(self.memory_mut().writes_are_aligned, "Unaligned writes must not be nested");
self.memory_mut().writes_are_aligned = aligned;
let old = self.memory().reads_are_aligned.get();
// Do alignment checking if *all* nested calls say it has to be aligned.
self.memory().reads_are_aligned.set(old && aligned);
let t = f(self);
self.memory_mut().writes_are_aligned = true;
self.memory().reads_are_aligned.set(old);
t
}
fn write_maybe_aligned_mut<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
{
let old = self.memory().writes_are_aligned.get();
// Do alignment checking if *all* nested calls say it has to be aligned.
self.memory().writes_are_aligned.set(old && aligned);
let t = f(self);
self.memory().writes_are_aligned.set(old);
t
}
}

View file

@ -11,13 +11,15 @@ use rustc::ty;
use rustc::ty::layout::Layout;
use rustc::ty::subst::Substs;
use error::{EvalResult, EvalError};
use eval_context::{EvalContext, StackPopCleanup};
use lvalue::{Global, GlobalId, Lvalue};
use value::{Value, PrimVal};
use syntax::codemap::Span;
use syntax::ast::Mutability;
use error::{EvalResult, EvalError};
use eval_context::{EvalContext, StackPopCleanup, TyAndPacked};
use lvalue::{Global, GlobalId, Lvalue};
use value::{Value, PrimVal};
use memory::HasMemory;
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
self.steps_remaining = self.steps_remaining.saturating_sub(n);
@ -101,12 +103,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
if variant_index as u64 != nndiscr {
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
trace!("struct wrapped nullable pointer type: {}", ty);
// only the pointer part of a fat pointer is used for this space optimization
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
self.memory.write_uint(nonnull, 0, discr_size)?;
self.write_maybe_aligned_mut(!packed, |ectx| ectx.memory.write_uint(nonnull, 0, discr_size))?;
}
},

View file

@ -45,7 +45,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"arith_offset" => {
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?;
self.write_ptr(dest, result_ptr, dest_ty)?;
}
@ -61,7 +61,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"atomic_load_acq" |
"volatile_load" => {
let ty = substs.type_at(0);
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
self.write_value(Value::by_ref(ptr), dest, ty)?;
}
@ -70,7 +70,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"atomic_store_rel" |
"volatile_store" => {
let ty = substs.type_at(0);
let dest = arg_vals[0].into_ptr(&mut self.memory)?;
let dest = arg_vals[0].into_ptr(&self.memory)?;
self.write_value_to_ptr(arg_vals[1], dest, ty)?;
}
@ -80,12 +80,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
_ if intrinsic_name.starts_with("atomic_xchg") => {
let ty = substs.type_at(0);
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
let change = self.value_to_primval(arg_vals[1], ty)?;
let old = self.read_value(ptr, ty)?;
let old = match old {
Value::ByVal(val) => val,
Value::ByRef(..) => bug!("just read the value, can't be byref"),
Value::ByRef { .. } => bug!("just read the value, can't be byref"),
Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"),
};
self.write_primval(dest, old, ty)?;
@ -94,13 +94,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
_ if intrinsic_name.starts_with("atomic_cxchg") => {
let ty = substs.type_at(0);
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
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)?;
let old = match old {
Value::ByVal(val) => val,
Value::ByRef(..) => bug!("just read the value, can't be byref"),
Value::ByRef { .. } => bug!("just read the value, can't be byref"),
Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"),
};
let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?;
@ -115,12 +115,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"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].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
let change = self.value_to_primval(arg_vals[1], ty)?;
let old = self.read_value(ptr, ty)?;
let old = match old {
Value::ByVal(val) => val,
Value::ByRef(..) => bug!("just read the value, can't be byref"),
Value::ByRef { .. } => bug!("just read the value, can't be byref"),
Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"),
};
self.write_primval(dest, old, ty)?;
@ -148,8 +148,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// TODO: We do not even validate alignment for the 0-bytes case. libstd relies on this in vec::IntoIter::next.
// Also see the write_bytes intrinsic.
let elem_align = self.type_align(elem_ty)?;
let src = arg_vals[0].into_ptr(&mut self.memory)?;
let dest = arg_vals[1].into_ptr(&mut self.memory)?;
let src = arg_vals[0].into_ptr(&self.memory)?;
let dest = arg_vals[1].into_ptr(&self.memory)?;
self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?;
}
}
@ -176,7 +176,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"discriminant_value" => {
let ty = substs.type_at(0);
let adt_ptr = arg_vals[0].into_ptr(&mut self.memory)?.to_ptr()?;
let adt_ptr = arg_vals[0].into_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)?;
}
@ -251,10 +251,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let size = self.type_size(dest_ty)?.expect("cannot zero unsized value");
let init = |this: &mut Self, val: Value| {
let zero_val = match val {
Value::ByRef(ptr, aligned) => {
Value::ByRef { ptr, aligned } => {
// These writes have no alignment restriction anyway.
this.memory.write_repeat(ptr, 0, size)?;
Value::ByRef(ptr, aligned)
Value::ByRef { ptr, aligned }
},
// TODO(solson): Revisit this, it's fishy to check for Undef here.
Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) {
@ -297,7 +297,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"move_val_init" => {
let ty = substs.type_at(0);
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
self.write_value_to_ptr(arg_vals[1], ptr, ty)?;
}
@ -310,7 +310,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"offset" => {
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?;
self.write_ptr(dest, result_ptr, dest_ty)?;
}
@ -399,7 +399,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"transmute" => {
let src_ty = substs.type_at(0);
let ptr = self.force_allocation(dest)?.to_ptr()?;
self.write_maybe_aligned(/*aligned*/false, |ectx| {
self.write_maybe_aligned_mut(/*aligned*/false, |ectx| {
ectx.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)
})?;
}
@ -442,9 +442,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let size = dest_layout.size(&self.tcx.data_layout).bytes();
let uninit = |this: &mut Self, val: Value| {
match val {
Value::ByRef(ptr, aligned) => {
Value::ByRef { ptr, aligned } => {
this.memory.mark_definedness(ptr, size, false)?;
Ok(Value::ByRef(ptr, aligned))
Ok(Value::ByRef { ptr, aligned })
},
_ => Ok(Value::ByVal(PrimVal::Undef)),
}
@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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].into_ptr(&mut self.memory)?;
let ptr = arg_vals[0].into_ptr(&self.memory)?;
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
if count > 0 {
// HashMap relies on write_bytes on a NULL ptr with count == 0 to work
@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok((size, align.abi()))
}
ty::TyDynamic(..) => {
let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?;
let (_, vtable) = value.into_ptr_vtable_pair(&self.memory)?;
// the second entry in the vtable is the dynamic size of the object.
self.read_size_and_align_from_vtable(vtable)
}
@ -558,7 +558,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
ty::TySlice(_) | ty::TyStr => {
let elem_ty = ty.sequence_element_type(self.tcx);
let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64;
let (_, len) = value.into_slice(&mut self.memory)?;
let (_, len) = value.into_slice(&self.memory)?;
let align = self.type_align(elem_ty)?;
Ok((len * elem_size, align as u64))
}

View file

@ -7,9 +7,9 @@ use syntax::attr;
use syntax::abi::Abi;
use error::{EvalError, EvalResult};
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited, self};
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, TyAndPacked, is_inhabited, self};
use lvalue::Lvalue;
use memory::{MemoryPointer, TlsKey, Kind};
use memory::{MemoryPointer, TlsKey, Kind, HasMemory};
use value::{PrimVal, Value};
use rustc_data_structures::indexed_vec::Idx;
use const_eval;
@ -313,10 +313,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
if self.frame().mir.args_iter().count() == fields.len() + 1 {
let offsets = variant.offsets.iter().map(|s| s.bytes());
match arg_val {
Value::ByRef(ptr, aligned) => {
Value::ByRef { ptr, aligned } => {
assert!(aligned, "Unaligned ByRef-values cannot occur as function arguments");
for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) {
let arg = Value::ByRef(ptr.offset(offset, &self)?, true);
let arg = Value::ByRef { ptr: ptr.offset(offset, &self)?, aligned: true};
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty);
self.write_value(arg, dest, ty)?;
@ -397,12 +397,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
},
ty::InstanceDef::Virtual(_, idx) => {
let ptr_size = self.memory.pointer_size();
let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&mut self.memory)?;
let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&self.memory)?;
let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), &self)?)?;
let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?;
let mut arg_operands = arg_operands.to_vec();
let ty = self.operand_ty(&arg_operands[0]);
let ty = self.get_field_ty(ty, 0)?;
let ty = self.get_field_ty(ty, 0)?.ty; // TODO: packed flag is ignored
match arg_operands[0] {
mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty),
_ => bug!("virtual call first arg cannot be a constant"),
@ -487,12 +487,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?;
let nonnull = adt_ptr.offset(offset.bytes(), self)?;
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?;
let nonnull = adt_ptr.offset(offset.bytes(), &*self)?;
trace!("struct wrapped nullable pointer type: {}", ty);
// only the pointer part of a fat pointer is used for this space optimization
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)?
self.read_maybe_aligned(!packed,
|ectx| ectx.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size))?
}
// The discriminant_value intrinsic returns 0 for non-sum types.
@ -576,7 +577,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
"alloc::heap::::__rust_dealloc" => {
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
let align = self.value_to_primval(args[2], usize)?.to_u64()?;
if old_size == 0 {
@ -588,7 +589,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust)?;
}
"alloc::heap::::__rust_realloc" => {
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
let old_align = self.value_to_primval(args[2], usize)?.to_u64()?;
let new_size = self.value_to_primval(args[3], usize)?.to_u64()?;
@ -664,7 +665,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"free" => {
let ptr = args[0].into_ptr(&mut self.memory)?;
let ptr = args[0].into_ptr(&self.memory)?;
if !ptr.is_null()? {
self.memory.deallocate(ptr.to_ptr()?, None, Kind::C)?;
}
@ -678,8 +679,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"dlsym" => {
let _handle = args[0].into_ptr(&mut self.memory)?;
let symbol = args[1].into_ptr(&mut self.memory)?.to_ptr()?;
let _handle = args[0].into_ptr(&self.memory)?;
let symbol = args[1].into_ptr(&self.memory)?.to_ptr()?;
let symbol_name = self.memory.read_c_str(symbol)?;
let err = format!("bad c unicode symbol: {:?}", symbol_name);
let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
@ -690,8 +691,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// 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].into_ptr(&mut self.memory)?.to_ptr()?;
let data = args[1].into_ptr(&mut self.memory)?;
let f = args[0].into_ptr(&self.memory)?.to_ptr()?;
let data = args[1].into_ptr(&self.memory)?;
let f_instance = self.memory.get_fn(f)?;
self.write_null(dest, dest_ty)?;
@ -722,8 +723,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"memcmp" => {
let left = args[0].into_ptr(&mut self.memory)?;
let right = args[1].into_ptr(&mut self.memory)?;
let left = args[0].into_ptr(&self.memory)?;
let right = args[1].into_ptr(&self.memory)?;
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
let result = {
@ -742,7 +743,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"memrchr" => {
let ptr = args[0].into_ptr(&mut self.memory)?;
let ptr = args[0].into_ptr(&self.memory)?;
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) {
@ -754,7 +755,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"memchr" => {
let ptr = args[0].into_ptr(&mut self.memory)?;
let ptr = args[0].into_ptr(&self.memory)?;
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) {
@ -767,7 +768,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"getenv" => {
let result = {
let name_ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
let name_ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
let name = self.memory.read_c_str(name_ptr)?;
match self.env_vars.get(name) {
Some(&var) => PrimVal::Ptr(var),
@ -780,7 +781,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"unsetenv" => {
let mut success = None;
{
let name_ptr = args[0].into_ptr(&mut self.memory)?;
let name_ptr = args[0].into_ptr(&self.memory)?;
if !name_ptr.is_null()? {
let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
if !name.is_empty() && !name.contains(&b'=') {
@ -801,8 +802,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"setenv" => {
let mut new = None;
{
let name_ptr = args[0].into_ptr(&mut self.memory)?;
let value_ptr = args[1].into_ptr(&mut self.memory)?.to_ptr()?;
let name_ptr = args[0].into_ptr(&self.memory)?;
let value_ptr = args[1].into_ptr(&self.memory)?.to_ptr()?;
let value = self.memory.read_c_str(value_ptr)?;
if !name_ptr.is_null()? {
let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
@ -828,7 +829,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"write" => {
let fd = self.value_to_primval(args[0], usize)?.to_u64()?;
let buf = args[1].into_ptr(&mut self.memory)?;
let buf = args[1].into_ptr(&self.memory)?;
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
@ -845,7 +846,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
"strlen" => {
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
let n = self.memory.read_c_str(ptr)?.len();
self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?;
}
@ -888,10 +889,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// Hook pthread calls that go to the thread-local storage memory subsystem
"pthread_key_create" => {
let key_ptr = args[0].into_ptr(&mut self.memory)?;
let key_ptr = args[0].into_ptr(&self.memory)?;
// Extract the function type out of the signature (that seems easier than constructing it ourselves...)
let dtor = match args[1].into_ptr(&mut self.memory)?.into_inner_primval() {
let dtor = match args[1].into_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),
@ -933,7 +934,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"pthread_setspecific" => {
// 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 new_ptr = args[1].into_ptr(&mut self.memory)?;
let new_ptr = args[1].into_ptr(&self.memory)?;
self.memory.store_tls(key, new_ptr)?;
// Return success (0)

View file

@ -151,7 +151,7 @@ std::sync::atomic::AtomicBool::get_mut$|\
fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, re: Option<CodeExtent>, mutbl: Mutability, mode: ValidationMode) -> EvalResult<'tcx> {
// Check alignment and non-NULLness
let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
let ptr = val.into_ptr(&mut self.memory)?;
let ptr = val.into_ptr(&self.memory)?;
self.memory.check_align(ptr, align)?;
// Recurse
@ -309,7 +309,7 @@ std::sync::atomic::AtomicBool::get_mut$|\
self.validate_ptr(val, query.ty.boxed_ty(), query.re, query.mutbl, mode)
}
TyFnPtr(_sig) => {
let ptr = self.read_lvalue(query.lval)?.into_ptr(&mut self.memory)?.to_ptr()?;
let ptr = self.read_lvalue(query.lval)?.into_ptr(&self.memory)?.to_ptr()?;
self.memory.get_fn(ptr)?;
// TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
Ok(())

View file

@ -32,7 +32,7 @@ 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(Pointer, bool),
ByRef { ptr: Pointer, aligned: bool},
ByVal(PrimVal),
ByValPair(PrimVal, PrimVal),
}
@ -162,15 +162,15 @@ pub enum PrimValKind {
impl<'a, 'tcx: 'a> Value {
#[inline]
pub(super) fn by_ref(ptr: Pointer) -> Self {
Value::ByRef(ptr, true)
Value::ByRef { ptr, aligned: true }
}
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
/// this may have to perform a load.
pub(super) fn into_ptr(&self, mem: &mut Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
pub(super) fn into_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
use self::Value::*;
match *self {
ByRef(ptr, aligned) => {
ByRef { ptr, aligned } => {
mem.read_maybe_aligned(aligned, |mem| mem.read_ptr(ptr.to_ptr()?) )
},
ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.into()),
@ -179,11 +179,11 @@ impl<'a, 'tcx: 'a> Value {
pub(super) fn into_ptr_vtable_pair(
&self,
mem: &mut Memory<'a, 'tcx>
mem: &Memory<'a, 'tcx>
) -> EvalResult<'tcx, (Pointer, MemoryPointer)> {
use self::Value::*;
match *self {
ByRef(ref_ptr, aligned) => {
ByRef { ptr: ref_ptr, aligned } => {
mem.read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?;
let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?;
@ -197,11 +197,11 @@ impl<'a, 'tcx: 'a> Value {
}
}
pub(super) fn into_slice(&self, mem: &mut Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> {
pub(super) fn into_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> {
use self::Value::*;
match *self {
ByRef(ref_ptr, aligned) => {
mem.write_maybe_aligned(aligned, |mem| {
ByRef { ptr: ref_ptr, aligned } => {
mem.read_maybe_aligned(aligned, |mem| {
let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?;
let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?;
Ok((ptr, len))

View file

@ -40,7 +40,8 @@ fn test_unsizing() {
let arr = [1, 2, 3];
let arr_unaligned: UnalignedPtr<[i32; 3]> = UnalignedPtr { data: &arr };
let _uns: UnalignedPtr<[i32]> = arr_unaligned;
let arr_unaligned: UnalignedPtr<[i32]> = arr_unaligned;
let _unused = &arr_unaligned; // forcing an allocation, which could also yield "unaligned write"-errors
}
fn main() {