Merge pull request #59 from oli-obk/master
enable A<Struct> -> A<Trait> downcasting where `A<Trait>` is a fat pointer
This commit is contained in:
commit
1e3659eabb
18 changed files with 634 additions and 85 deletions
|
@ -10,6 +10,7 @@ use syntax::codemap::Span;
|
|||
pub enum EvalError<'tcx> {
|
||||
FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>),
|
||||
DanglingPointerDeref,
|
||||
InvalidMemoryAccess,
|
||||
InvalidFunctionPointer,
|
||||
InvalidBool,
|
||||
InvalidDiscriminant,
|
||||
|
@ -19,7 +20,6 @@ pub enum EvalError<'tcx> {
|
|||
allocation_size: usize,
|
||||
},
|
||||
ReadPointerAsBytes,
|
||||
ReadBytesAsPointer,
|
||||
InvalidPointerMath,
|
||||
ReadUndefBytes,
|
||||
InvalidBoolOp(mir::BinOp),
|
||||
|
@ -53,6 +53,8 @@ impl<'tcx> Error for EvalError<'tcx> {
|
|||
match *self {
|
||||
EvalError::FunctionPointerTyMismatch(..) =>
|
||||
"tried to call a function through a function pointer of a different type",
|
||||
EvalError::InvalidMemoryAccess =>
|
||||
"tried to access memory through an invalid pointer",
|
||||
EvalError::DanglingPointerDeref =>
|
||||
"dangling pointer was dereferenced",
|
||||
EvalError::InvalidFunctionPointer =>
|
||||
|
@ -65,8 +67,6 @@ impl<'tcx> Error for EvalError<'tcx> {
|
|||
"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 =>
|
||||
"attempted to interpret some raw bytes as a pointer address",
|
||||
EvalError::InvalidPointerMath =>
|
||||
"attempted to do math or a comparison on pointers into different allocations",
|
||||
EvalError::ReadUndefBytes =>
|
||||
|
|
|
@ -25,8 +25,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
U16(u) => self.cast_const_int(u as u64, ty, false),
|
||||
U32(u) => self.cast_const_int(u as u64, ty, false),
|
||||
Char(c) => self.cast_const_int(c as u64, ty, false),
|
||||
U64(u) |
|
||||
IntegerPtr(u) => self.cast_const_int(u, ty, false),
|
||||
U64(u) => self.cast_const_int(u, ty, false),
|
||||
FnPtr(ptr) |
|
||||
Ptr(ptr) => self.cast_ptr(ptr, ty),
|
||||
}
|
||||
|
@ -74,7 +73,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)),
|
||||
ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)),
|
||||
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)),
|
||||
ty::TyRawPtr(_) => Ok(IntegerPtr(v)),
|
||||
ty::TyRawPtr(_) => Ok(Ptr(Pointer::from_int(v as usize))),
|
||||
ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)),
|
||||
ty::TyChar => Err(EvalError::InvalidChar(v)),
|
||||
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
|
||||
|
|
|
@ -621,12 +621,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let src = self.eval_operand_to_ptr(operand)?;
|
||||
let src_ty = self.operand_ty(operand);
|
||||
let dest_ty = self.monomorphize(dest_ty, self.substs());
|
||||
// FIXME: cases where dest_ty is not a fat pointer. e.g. Arc<Struct> -> Arc<Trait>
|
||||
assert!(self.type_is_fat_ptr(dest_ty));
|
||||
let (ptr, extra) = self.get_fat_ptr(dest);
|
||||
self.move_(src, ptr, src_ty)?;
|
||||
let src_pointee_ty = pointee_type(src_ty).unwrap();
|
||||
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
|
||||
|
||||
// A<Struct> -> A<Trait> conversion
|
||||
let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(src_pointee_ty, dest_pointee_ty);
|
||||
|
||||
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
|
||||
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
|
||||
self.memory.write_usize(extra, length as u64)?;
|
||||
|
@ -858,7 +862,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
use rustc::mir::repr::ProjectionElem::*;
|
||||
match proj.elem {
|
||||
Field(field, _) => {
|
||||
Field(field, field_ty) => {
|
||||
use rustc::ty::layout::Layout::*;
|
||||
let variant = match *base_layout {
|
||||
Univariant { ref variant, .. } => variant,
|
||||
|
@ -878,7 +882,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
};
|
||||
|
||||
let offset = variant.field_offset(field.index()).bytes();
|
||||
base.ptr.offset(offset as isize)
|
||||
let ptr = base.ptr.offset(offset as isize);
|
||||
match (&field_ty.sty, base.extra) {
|
||||
(&ty::TyStr, extra @ LvalueExtra::Length(_)) |
|
||||
(&ty::TySlice(_), extra @ LvalueExtra::Length(_)) |
|
||||
(&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue {
|
||||
ptr: ptr,
|
||||
extra: extra,
|
||||
}),
|
||||
(&ty::TyTrait(_), _) => bug!("trait field without vtable"),
|
||||
_ => ptr,
|
||||
}
|
||||
},
|
||||
|
||||
Downcast(_, variant) => {
|
||||
|
@ -899,6 +913,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
Deref => {
|
||||
let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer");
|
||||
let pointee_ty = self.tcx.struct_tail(pointee_ty);
|
||||
let ptr = self.memory.read_ptr(base.ptr)?;
|
||||
let extra = match pointee_ty.sty {
|
||||
ty::TySlice(_) | ty::TyStr => {
|
||||
|
@ -1049,13 +1064,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
&ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
|
||||
&ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
|
||||
if self.type_is_sized(ty) {
|
||||
match self.memory.read_ptr(ptr) {
|
||||
Ok(p) => PrimVal::Ptr(p),
|
||||
Err(EvalError::ReadBytesAsPointer) => {
|
||||
PrimVal::IntegerPtr(self.memory.read_usize(ptr)?)
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
PrimVal::Ptr(self.memory.read_ptr(ptr)?)
|
||||
} else {
|
||||
bug!("primitive read of fat pointer type: {:?}", ty);
|
||||
}
|
||||
|
|
|
@ -132,18 +132,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let ptr_arg = args_ptrs[0];
|
||||
let offset = self.memory.read_isize(args_ptrs[1])?;
|
||||
|
||||
match self.memory.read_ptr(ptr_arg) {
|
||||
Ok(ptr) => {
|
||||
let result_ptr = ptr.offset(offset as isize * pointee_size);
|
||||
self.memory.write_ptr(dest, result_ptr)?;
|
||||
}
|
||||
Err(EvalError::ReadBytesAsPointer) => {
|
||||
let addr = self.memory.read_isize(ptr_arg)?;
|
||||
let result_addr = addr + offset * pointee_size as i64;
|
||||
self.memory.write_isize(dest, result_addr)?;
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
let ptr = self.memory.read_ptr(ptr_arg)?;
|
||||
let result_ptr = ptr.offset(offset as isize * pointee_size);
|
||||
self.memory.write_ptr(dest, result_ptr)?;
|
||||
}
|
||||
|
||||
"overflowing_sub" => {
|
||||
|
@ -188,22 +179,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"size_of_val" => {
|
||||
let ty = substs.type_at(0);
|
||||
if self.type_is_sized(ty) {
|
||||
let size = self.type_size(ty) as u64;
|
||||
self.memory.write_uint(dest, size, pointer_size)?;
|
||||
} else {
|
||||
match ty.sty {
|
||||
ty::TySlice(_) | ty::TyStr => {
|
||||
let elem_ty = ty.sequence_element_type(self.tcx);
|
||||
let elem_size = self.type_size(elem_ty) as u64;
|
||||
let ptr_size = self.memory.pointer_size() as isize;
|
||||
let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?;
|
||||
self.memory.write_uint(dest, n * elem_size, pointer_size)?;
|
||||
}
|
||||
|
||||
_ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))),
|
||||
}
|
||||
}
|
||||
let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?;
|
||||
self.memory.write_uint(dest, size, pointer_size)?;
|
||||
}
|
||||
// FIXME: wait for eval_operand_to_ptr to be gone
|
||||
/*
|
||||
|
@ -248,4 +225,114 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// current frame.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn size_and_align_of_dst(
|
||||
&self,
|
||||
ty: ty::Ty<'tcx>,
|
||||
value: Pointer,
|
||||
) -> EvalResult<'tcx, (u64, u64)> {
|
||||
let pointer_size = self.memory.pointer_size();
|
||||
if self.type_is_sized(ty) {
|
||||
Ok((self.type_size(ty) as u64, self.type_align(ty) as u64))
|
||||
} else {
|
||||
match ty.sty {
|
||||
ty::TyAdt(def, substs) => {
|
||||
// First get the size of all statically known fields.
|
||||
// Don't use type_of::sizing_type_of because that expects t to be sized,
|
||||
// and it also rounds up to alignment, which we want to avoid,
|
||||
// as the unsized field's alignment could be smaller.
|
||||
assert!(!ty.is_simd());
|
||||
let layout = self.type_layout(ty);
|
||||
debug!("DST {} layout: {:?}", ty, layout);
|
||||
|
||||
// Returns size in bytes of all fields except the last one
|
||||
// (we will be recursing on the last one).
|
||||
fn local_prefix_bytes(variant: &ty::layout::Struct) -> u64 {
|
||||
let fields = variant.offset_after_field.len();
|
||||
if fields > 1 {
|
||||
variant.offset_after_field[fields - 2].bytes()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
let (sized_size, sized_align) = match *layout {
|
||||
ty::layout::Layout::Univariant { ref variant, .. } => {
|
||||
(local_prefix_bytes(variant), variant.align.abi())
|
||||
}
|
||||
_ => {
|
||||
bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}",
|
||||
ty, layout);
|
||||
}
|
||||
};
|
||||
debug!("DST {} statically sized prefix size: {} align: {}",
|
||||
ty, sized_size, sized_align);
|
||||
|
||||
// Recurse to get the size of the dynamically sized field (must be
|
||||
// the last field).
|
||||
let last_field = def.struct_variant().fields.last().unwrap();
|
||||
let field_ty = self.field_ty(substs, last_field);
|
||||
let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?;
|
||||
|
||||
// FIXME (#26403, #27023): We should be adding padding
|
||||
// to `sized_size` (to accommodate the `unsized_align`
|
||||
// required of the unsized field that follows) before
|
||||
// summing it with `sized_size`. (Note that since #26403
|
||||
// is unfixed, we do not yet add the necessary padding
|
||||
// here. But this is where the add would go.)
|
||||
|
||||
// Return the sum of sizes and max of aligns.
|
||||
let size = sized_size + unsized_size;
|
||||
|
||||
// Choose max of two known alignments (combined value must
|
||||
// be aligned according to more restrictive of the two).
|
||||
let align = ::std::cmp::max(sized_align, unsized_align);
|
||||
|
||||
// Issue #27023: must add any necessary padding to `size`
|
||||
// (to make it a multiple of `align`) before returning it.
|
||||
//
|
||||
// Namely, the returned size should be, in C notation:
|
||||
//
|
||||
// `size + ((size & (align-1)) ? align : 0)`
|
||||
//
|
||||
// emulated via the semi-standard fast bit trick:
|
||||
//
|
||||
// `(size + (align-1)) & -align`
|
||||
|
||||
if size & (align - 1) != 0 {
|
||||
Ok((size + align, align))
|
||||
} else {
|
||||
Ok((size, align))
|
||||
}
|
||||
}
|
||||
ty::TyTrait(..) => {
|
||||
let (_, vtable) = self.get_fat_ptr(value);
|
||||
let vtable = self.memory.read_ptr(vtable)?;
|
||||
// the second entry in the vtable is the dynamic size of the object.
|
||||
let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?;
|
||||
let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?;
|
||||
Ok((size, align))
|
||||
}
|
||||
|
||||
ty::TySlice(_) | ty::TyStr => {
|
||||
let elem_ty = ty.sequence_element_type(self.tcx);
|
||||
let elem_size = self.type_size(elem_ty) as u64;
|
||||
let (_, len_ptr) = self.get_fat_ptr(value);
|
||||
let n = self.memory.read_usize(len_ptr)?;
|
||||
let align = self.type_align(elem_ty);
|
||||
Ok((n * elem_size, align as u64))
|
||||
}
|
||||
|
||||
_ => bug!("size_of_val::<{:?}>", ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Returns the normalized type of a struct field
|
||||
fn field_ty(
|
||||
&self,
|
||||
param_substs: &Substs<'tcx>,
|
||||
f: ty::FieldDef<'tcx>,
|
||||
)-> ty::Ty<'tcx> {
|
||||
self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,12 @@ impl Pointer {
|
|||
pub fn points_to_zst(&self) -> bool {
|
||||
self.alloc_id == ZST_ALLOC_ID
|
||||
}
|
||||
pub fn from_int(i: usize) -> Self {
|
||||
Pointer {
|
||||
alloc_id: ZST_ALLOC_ID,
|
||||
offset: i,
|
||||
}
|
||||
}
|
||||
fn zst_ptr() -> Self {
|
||||
Pointer {
|
||||
alloc_id: ZST_ALLOC_ID,
|
||||
|
@ -105,7 +111,7 @@ const ZST_ALLOC_ID: AllocId = AllocId(0);
|
|||
|
||||
impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||
pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self {
|
||||
let mut mem = Memory {
|
||||
Memory {
|
||||
alloc_map: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
function_alloc_cache: HashMap::new(),
|
||||
|
@ -113,21 +119,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
layout: layout,
|
||||
memory_size: max_memory,
|
||||
memory_usage: 0,
|
||||
};
|
||||
// alloc id 0 is reserved for ZSTs, this is an optimization to prevent ZST
|
||||
// (e.g. function items, (), [], ...) from requiring memory
|
||||
let alloc = Allocation {
|
||||
bytes: Vec::new(),
|
||||
relocations: BTreeMap::new(),
|
||||
undef_mask: UndefMask::new(0),
|
||||
align: 1,
|
||||
immutable: false, // must be mutable, because sometimes we "move out" of a ZST
|
||||
};
|
||||
mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
|
||||
// check that additional zst allocs work
|
||||
debug_assert!(mem.allocate(0, 1).unwrap().points_to_zst());
|
||||
debug_assert!(mem.get(ZST_ALLOC_ID).is_ok());
|
||||
mem
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation> {
|
||||
|
@ -293,6 +285,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
Some(alloc) => Ok(alloc),
|
||||
None => match self.functions.get(&id) {
|
||||
Some(_) => Err(EvalError::DerefFunctionPointer),
|
||||
None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
|
||||
None => Err(EvalError::DanglingPointerDeref),
|
||||
}
|
||||
}
|
||||
|
@ -304,6 +297,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
Some(alloc) => Ok(alloc),
|
||||
None => match self.functions.get(&id) {
|
||||
Some(_) => Err(EvalError::DerefFunctionPointer),
|
||||
None if id == ZST_ALLOC_ID => Err(EvalError::InvalidMemoryAccess),
|
||||
None => Err(EvalError::DanglingPointerDeref),
|
||||
}
|
||||
}
|
||||
|
@ -345,25 +339,30 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
|
||||
/// Print an allocation and all allocations it points to, recursively.
|
||||
pub fn dump(&self, id: AllocId) {
|
||||
use std::fmt::Write;
|
||||
let mut allocs_seen = HashSet::new();
|
||||
let mut allocs_to_print = VecDeque::new();
|
||||
allocs_to_print.push_back(id);
|
||||
|
||||
while let Some(id) = allocs_to_print.pop_front() {
|
||||
allocs_seen.insert(id);
|
||||
let prefix = format!("Alloc {:<5} ", format!("{}:", id));
|
||||
print!("{}", prefix);
|
||||
let mut msg = format!("Alloc {:<5} ", format!("{}:", id));
|
||||
if id == ZST_ALLOC_ID {
|
||||
trace!("{} zst allocation", msg);
|
||||
continue;
|
||||
}
|
||||
let prefix_len = msg.len();
|
||||
let mut relocations = vec![];
|
||||
|
||||
let alloc = match (self.alloc_map.get(&id), self.functions.get(&id)) {
|
||||
(Some(a), None) => a,
|
||||
(None, Some(_)) => {
|
||||
// FIXME: print function name
|
||||
println!("function pointer");
|
||||
trace!("{} function pointer", msg);
|
||||
continue;
|
||||
},
|
||||
(None, None) => {
|
||||
println!("(deallocated)");
|
||||
trace!("{} (deallocated)", msg);
|
||||
continue;
|
||||
},
|
||||
(Some(_), Some(_)) => bug!("miri invariant broken: an allocation id exists that points to both a function and a memory location"),
|
||||
|
@ -377,25 +376,26 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
relocations.push((i, target_id));
|
||||
}
|
||||
if alloc.undef_mask.is_range_defined(i, i + 1) {
|
||||
print!("{:02x} ", alloc.bytes[i]);
|
||||
write!(msg, "{:02x} ", alloc.bytes[i]).unwrap();
|
||||
} else {
|
||||
print!("__ ");
|
||||
msg.push_str("__ ");
|
||||
}
|
||||
}
|
||||
|
||||
let immutable = if alloc.immutable { " (immutable)" } else { "" };
|
||||
println!("({} bytes){}", alloc.bytes.len(), immutable);
|
||||
trace!("{}({} bytes){}", msg, alloc.bytes.len(), immutable);
|
||||
|
||||
if !relocations.is_empty() {
|
||||
print!("{:1$}", "", prefix.len()); // Print spaces.
|
||||
msg.clear();
|
||||
write!(msg, "{:1$}", "", prefix_len).unwrap(); // Print spaces.
|
||||
let mut pos = 0;
|
||||
let relocation_width = (self.pointer_size() - 1) * 3;
|
||||
for (i, target_id) in relocations {
|
||||
print!("{:1$}", "", (i - pos) * 3);
|
||||
print!("└{0:─^1$}┘ ", format!("({})", target_id), relocation_width);
|
||||
write!(msg, "{:1$}", "", (i - pos) * 3).unwrap();
|
||||
write!(msg, "└{0:─^1$}┘ ", format!("({})", target_id), relocation_width).unwrap();
|
||||
pos = i + self.pointer_size();
|
||||
}
|
||||
println!("");
|
||||
trace!("{}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -404,6 +404,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
/// Byte accessors
|
||||
impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||
fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> {
|
||||
if size == 0 {
|
||||
return Ok(&[]);
|
||||
}
|
||||
let alloc = self.get(ptr.alloc_id)?;
|
||||
if ptr.offset + size > alloc.bytes.len() {
|
||||
return Err(EvalError::PointerOutOfBounds {
|
||||
|
@ -416,6 +419,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> {
|
||||
if size == 0 {
|
||||
return Ok(&mut []);
|
||||
}
|
||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||
if ptr.offset + size > alloc.bytes.len() {
|
||||
return Err(EvalError::PointerOutOfBounds {
|
||||
|
@ -428,6 +434,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> {
|
||||
if size == 0 {
|
||||
return Ok(&[]);
|
||||
}
|
||||
self.check_align(ptr, align)?;
|
||||
if self.relocations(ptr, size)?.count() != 0 {
|
||||
return Err(EvalError::ReadPointerAsBytes);
|
||||
|
@ -437,6 +446,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> {
|
||||
if size == 0 {
|
||||
return Ok(&mut []);
|
||||
}
|
||||
self.check_align(ptr, align)?;
|
||||
self.clear_relocations(ptr, size)?;
|
||||
self.mark_definedness(ptr, size, true)?;
|
||||
|
@ -447,8 +459,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
/// Reading and writing
|
||||
impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||
pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> {
|
||||
// Never freeze the zero-sized allocation. If you do that, then getting a mutable handle to
|
||||
// _any_ ZST becomes an error, since they all share the same allocation.
|
||||
// It's not possible to freeze the zero-sized allocation, because it doesn't exist.
|
||||
if alloc_id != ZST_ALLOC_ID {
|
||||
self.get_mut(alloc_id)?.immutable = true;
|
||||
}
|
||||
|
@ -456,6 +467,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
}
|
||||
|
||||
pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> {
|
||||
if size == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
self.check_relocation_edges(src, size)?;
|
||||
|
||||
let src_bytes = self.get_bytes_unchecked(src, size)?.as_ptr();
|
||||
|
@ -503,7 +517,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
let alloc = self.get(ptr.alloc_id)?;
|
||||
match alloc.relocations.get(&ptr.offset) {
|
||||
Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }),
|
||||
None => Err(EvalError::ReadBytesAsPointer),
|
||||
None => Ok(Pointer::from_int(offset)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,7 +528,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
}
|
||||
|
||||
pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> {
|
||||
let pointer_size = self.pointer_size();
|
||||
match val {
|
||||
PrimVal::Bool(b) => self.write_bool(ptr, b),
|
||||
PrimVal::I8(n) => self.write_int(ptr, n as i64, 1),
|
||||
|
@ -526,7 +539,6 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
PrimVal::U32(n) => self.write_uint(ptr, n as u64, 4),
|
||||
PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8),
|
||||
PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4),
|
||||
PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size),
|
||||
PrimVal::F32(f) => self.write_f32(ptr, f),
|
||||
PrimVal::F64(f) => self.write_f64(ptr, f),
|
||||
PrimVal::FnPtr(p) |
|
||||
|
@ -712,6 +724,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
pub fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool)
|
||||
-> EvalResult<'tcx, ()>
|
||||
{
|
||||
if size == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
let mut alloc = self.get_mut(ptr.alloc_id)?;
|
||||
alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state);
|
||||
Ok(())
|
||||
|
|
|
@ -14,7 +14,6 @@ pub enum PrimVal {
|
|||
|
||||
Ptr(Pointer),
|
||||
FnPtr(Pointer),
|
||||
IntegerPtr(u64),
|
||||
Char(char),
|
||||
|
||||
F32(f32), F64(f64),
|
||||
|
@ -209,14 +208,8 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
|
|||
})
|
||||
}
|
||||
|
||||
(IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r),
|
||||
|
||||
(Ptr(_), IntegerPtr(_)) |
|
||||
(IntegerPtr(_), Ptr(_)) |
|
||||
(FnPtr(_), Ptr(_)) |
|
||||
(Ptr(_), FnPtr(_)) |
|
||||
(FnPtr(_), IntegerPtr(_)) |
|
||||
(IntegerPtr(_), FnPtr(_)) =>
|
||||
(Ptr(_), FnPtr(_)) =>
|
||||
unrelated_ptr_ops(bin_op)?,
|
||||
|
||||
(FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
fn main() {
|
||||
let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: attempted to interpret some raw bytes as a pointer address
|
||||
let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR: tried to access memory through an invalid pointer
|
||||
panic!("this should never print: {}", x);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
fn main() {
|
||||
let p = 42 as *const i32;
|
||||
let x = unsafe { *p }; //~ ERROR: attempted to interpret some raw bytes as a pointer address
|
||||
let x = unsafe { *p }; //~ ERROR: tried to access memory through an invalid pointer
|
||||
panic!("this should never print: {}", x);
|
||||
}
|
||||
|
|
4
tests/compile-fail/zst.rs
Normal file
4
tests/compile-fail/zst.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
let x = &() as *const () as *const i32;
|
||||
let _ = unsafe { *x }; //~ ERROR: tried to access memory through an invalid pointer
|
||||
}
|
54
tests/run-pass/cast-rfc0401-vtable-kinds.rs
Normal file
54
tests/run-pass/cast-rfc0401-vtable-kinds.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Check that you can cast between different pointers to trait objects
|
||||
// whose vtable have the same kind (both lengths, or both trait pointers).
|
||||
|
||||
trait Foo<T> {
|
||||
fn foo(&self, _: T) -> u32 { 42 }
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
fn bar(&self) { println!("Bar!"); }
|
||||
}
|
||||
|
||||
impl<T> Foo<T> for () {}
|
||||
impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
|
||||
impl Bar for () {}
|
||||
|
||||
unsafe fn round_trip_and_call<'a>(t: *const (Foo<u32>+'a)) -> u32 {
|
||||
let foo_e : *const Foo<u16> = t as *const _;
|
||||
let r_1 = foo_e as *mut Foo<u32>;
|
||||
|
||||
(&*r_1).foo(0)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct FooS<T:?Sized>(T);
|
||||
#[repr(C)]
|
||||
struct BarS<T:?Sized>(T);
|
||||
|
||||
fn foo_to_bar<T:?Sized>(u: *const FooS<T>) -> *const BarS<T> {
|
||||
u as *const BarS<T>
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 4u32;
|
||||
let y : &Foo<u32> = &x;
|
||||
let fl = unsafe { round_trip_and_call(y as *const Foo<u32>) };
|
||||
assert_eq!(fl, (43+4));
|
||||
|
||||
let s = FooS([0,1,2]);
|
||||
let u: &FooS<[u32]> = &s;
|
||||
let u: *const FooS<[u32]> = u;
|
||||
let bar_ref : *const BarS<[u32]> = foo_to_bar(u);
|
||||
let z : &BarS<[u32]> = unsafe{&*bar_ref};
|
||||
assert_eq!(&z.0, &[0,1,2]);
|
||||
}
|
24
tests/run-pass/dst-irrefutable-bind.rs
Normal file
24
tests/run-pass/dst-irrefutable-bind.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
struct Test<T: ?Sized>(T);
|
||||
|
||||
fn main() {
|
||||
let x = Test([1,2,3]);
|
||||
let x : &Test<[i32]> = &x;
|
||||
|
||||
let & ref _y = x;
|
||||
|
||||
// Make sure binding to a fat pointer behind a reference
|
||||
// still works
|
||||
let slice = &[1,2,3];
|
||||
let x = Test(&slice);
|
||||
let Test(&_slice) = x;
|
||||
}
|
113
tests/run-pass/dst-raw.rs
Normal file
113
tests/run-pass/dst-raw.rs
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test DST raw pointers
|
||||
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> isize;
|
||||
}
|
||||
|
||||
struct A {
|
||||
f: isize
|
||||
}
|
||||
impl Trait for A {
|
||||
fn foo(&self) -> isize {
|
||||
self.f
|
||||
}
|
||||
}
|
||||
|
||||
struct Foo<T: ?Sized> {
|
||||
f: T
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// raw trait object
|
||||
let x = A { f: 42 };
|
||||
let z: *const Trait = &x;
|
||||
let r = unsafe {
|
||||
(&*z).foo()
|
||||
};
|
||||
assert_eq!(r, 42);
|
||||
|
||||
// raw DST struct
|
||||
let p = Foo {f: A { f: 42 }};
|
||||
let o: *const Foo<Trait> = &p;
|
||||
let r = unsafe {
|
||||
(&*o).f.foo()
|
||||
};
|
||||
assert_eq!(r, 42);
|
||||
|
||||
// raw slice
|
||||
let a: *const [_] = &[1, 2, 3];
|
||||
unsafe {
|
||||
let b = (*a)[2];
|
||||
assert_eq!(b, 3);
|
||||
let len = (*a).len();
|
||||
assert_eq!(len, 3);
|
||||
}
|
||||
|
||||
// raw slice with explicit cast
|
||||
let a = &[1, 2, 3] as *const [i32];
|
||||
unsafe {
|
||||
let b = (*a)[2];
|
||||
assert_eq!(b, 3);
|
||||
let len = (*a).len();
|
||||
assert_eq!(len, 3);
|
||||
}
|
||||
|
||||
// raw DST struct with slice
|
||||
let c: *const Foo<[_]> = &Foo {f: [1, 2, 3]};
|
||||
unsafe {
|
||||
let b = (&*c).f[0];
|
||||
assert_eq!(b, 1);
|
||||
let len = (&*c).f.len();
|
||||
assert_eq!(len, 3);
|
||||
}
|
||||
|
||||
// all of the above with *mut
|
||||
let mut x = A { f: 42 };
|
||||
let z: *mut Trait = &mut x;
|
||||
let r = unsafe {
|
||||
(&*z).foo()
|
||||
};
|
||||
assert_eq!(r, 42);
|
||||
|
||||
let mut p = Foo {f: A { f: 42 }};
|
||||
let o: *mut Foo<Trait> = &mut p;
|
||||
let r = unsafe {
|
||||
(&*o).f.foo()
|
||||
};
|
||||
assert_eq!(r, 42);
|
||||
|
||||
let a: *mut [_] = &mut [1, 2, 3];
|
||||
unsafe {
|
||||
let b = (*a)[2];
|
||||
assert_eq!(b, 3);
|
||||
let len = (*a).len();
|
||||
assert_eq!(len, 3);
|
||||
}
|
||||
|
||||
let a = &mut [1, 2, 3] as *mut [i32];
|
||||
unsafe {
|
||||
let b = (*a)[2];
|
||||
assert_eq!(b, 3);
|
||||
let len = (*a).len();
|
||||
assert_eq!(len, 3);
|
||||
}
|
||||
|
||||
let c: *mut Foo<[_]> = &mut Foo {f: [1, 2, 3]};
|
||||
unsafe {
|
||||
let b = (&*c).f[0];
|
||||
assert_eq!(b, 1);
|
||||
let len = (&*c).f.len();
|
||||
assert_eq!(len, 3);
|
||||
}
|
||||
}
|
85
tests/run-pass/dst-struct-sole.rs
Normal file
85
tests/run-pass/dst-struct-sole.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// As dst-struct.rs, but the unsized field is the only field in the struct.
|
||||
|
||||
|
||||
struct Fat<T: ?Sized> {
|
||||
ptr: T
|
||||
}
|
||||
|
||||
// x is a fat pointer
|
||||
fn foo(x: &Fat<[isize]>) {
|
||||
let y = &x.ptr;
|
||||
assert_eq!(x.ptr.len(), 3);
|
||||
assert_eq!(y[0], 1);
|
||||
assert_eq!(x.ptr[1], 2);
|
||||
}
|
||||
|
||||
fn foo2<T:ToBar>(x: &Fat<[T]>) {
|
||||
let y = &x.ptr;
|
||||
let bar = Bar;
|
||||
assert_eq!(x.ptr.len(), 3);
|
||||
assert_eq!(y[0].to_bar(), bar);
|
||||
assert_eq!(x.ptr[1].to_bar(), bar);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
struct Bar;
|
||||
|
||||
trait ToBar {
|
||||
fn to_bar(&self) -> Bar;
|
||||
}
|
||||
|
||||
impl ToBar for Bar {
|
||||
fn to_bar(&self) -> Bar {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// With a vec of ints.
|
||||
let f1 = Fat { ptr: [1, 2, 3] };
|
||||
foo(&f1);
|
||||
let f2 = &f1;
|
||||
foo(f2);
|
||||
let f3: &Fat<[isize]> = f2;
|
||||
foo(f3);
|
||||
let f4: &Fat<[isize]> = &f1;
|
||||
foo(f4);
|
||||
let f5: &Fat<[isize]> = &Fat { ptr: [1, 2, 3] };
|
||||
foo(f5);
|
||||
|
||||
// With a vec of Bars.
|
||||
let bar = Bar;
|
||||
let f1 = Fat { ptr: [bar, bar, bar] };
|
||||
foo2(&f1);
|
||||
let f2 = &f1;
|
||||
foo2(f2);
|
||||
let f3: &Fat<[Bar]> = f2;
|
||||
foo2(f3);
|
||||
let f4: &Fat<[Bar]> = &f1;
|
||||
foo2(f4);
|
||||
let f5: &Fat<[Bar]> = &Fat { ptr: [bar, bar, bar] };
|
||||
foo2(f5);
|
||||
|
||||
// Assignment.
|
||||
let f5: &mut Fat<[isize]> = &mut Fat { ptr: [1, 2, 3] };
|
||||
f5.ptr[1] = 34;
|
||||
assert_eq!(f5.ptr[0], 1);
|
||||
assert_eq!(f5.ptr[1], 34);
|
||||
assert_eq!(f5.ptr[2], 3);
|
||||
|
||||
// Zero size vec.
|
||||
let f5: &Fat<[isize]> = &Fat { ptr: [] };
|
||||
assert!(f5.ptr.is_empty());
|
||||
let f5: &Fat<[Bar]> = &Fat { ptr: [] };
|
||||
assert!(f5.ptr.is_empty());
|
||||
}
|
70
tests/run-pass/issue-23261.rs
Normal file
70
tests/run-pass/issue-23261.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Matching on a DST struct should not trigger an LLVM assertion.
|
||||
|
||||
struct Foo<T: ?Sized> {
|
||||
a: i32,
|
||||
inner: T
|
||||
}
|
||||
|
||||
trait Get {
|
||||
fn get(&self) -> i32;
|
||||
}
|
||||
|
||||
impl Get for i32 {
|
||||
fn get(&self) -> i32 {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
fn check_val(val: &Foo<[u8]>) {
|
||||
match *val {
|
||||
Foo { a, .. } => {
|
||||
assert_eq!(a, 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_dst_val(val: &Foo<[u8]>) {
|
||||
match *val {
|
||||
Foo { ref inner, .. } => {
|
||||
assert_eq!(inner, [1, 2, 3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_both(val: &Foo<[u8]>) {
|
||||
match *val {
|
||||
Foo { a, ref inner } => {
|
||||
assert_eq!(a, 32);
|
||||
assert_eq!(inner, [1, 2, 3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_obj(val: &Foo<Get>) {
|
||||
match *val {
|
||||
Foo { a, ref inner } => {
|
||||
assert_eq!(a, 32);
|
||||
assert_eq!(inner.get(), 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo: &Foo<[u8]> = &Foo { a: 32, inner: [1, 2, 3] };
|
||||
check_val(foo);
|
||||
check_dst_val(foo);
|
||||
check_both(foo);
|
||||
|
||||
let foo: &Foo<Get> = &Foo { a: 32, inner: 32 };
|
||||
check_trait_obj(foo);
|
||||
}
|
28
tests/run-pass/issue-36278-prefix-nesting.rs
Normal file
28
tests/run-pass/issue-36278-prefix-nesting.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Issue 36278: On an unsized struct with >1 level of nontrivial
|
||||
// nesting, ensure we are computing dynamic size of prefix correctly.
|
||||
|
||||
use std::mem;
|
||||
|
||||
const SZ: usize = 100;
|
||||
struct P<T: ?Sized>([u8; SZ], T);
|
||||
|
||||
type Ack<T> = P<P<T>>;
|
||||
|
||||
fn main() {
|
||||
let size_of_sized; let size_of_unsized;
|
||||
let x: Box<Ack<[u8; 0]>> = Box::new(P([0; SZ], P([0; SZ], [0; 0])));
|
||||
size_of_sized = mem::size_of_val::<Ack<_>>(&x);
|
||||
let y: Box<Ack<[u8 ]>> = x;
|
||||
size_of_unsized = mem::size_of_val::<Ack<_>>(&y);
|
||||
assert_eq!(size_of_sized, size_of_unsized);
|
||||
}
|
61
tests/run-pass/mir_fat_ptr.rs
Normal file
61
tests/run-pass/mir_fat_ptr.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// test that ordinary fat pointer operations work.
|
||||
|
||||
struct Wrapper<T: ?Sized>(u32, T);
|
||||
|
||||
struct FatPtrContainer<'a> {
|
||||
ptr: &'a [u8]
|
||||
}
|
||||
|
||||
fn fat_ptr_project(a: &Wrapper<[u8]>) -> &[u8] {
|
||||
&a.1
|
||||
}
|
||||
|
||||
fn fat_ptr_simple(a: &[u8]) -> &[u8] {
|
||||
a
|
||||
}
|
||||
|
||||
fn fat_ptr_via_local(a: &[u8]) -> &[u8] {
|
||||
let x = a;
|
||||
x
|
||||
}
|
||||
|
||||
fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] {
|
||||
s.ptr
|
||||
}
|
||||
|
||||
fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer {
|
||||
FatPtrContainer { ptr: a }
|
||||
}
|
||||
|
||||
fn fat_ptr_store_to<'a>(a: &'a [u8], b: &mut &'a [u8]) {
|
||||
*b = a;
|
||||
}
|
||||
|
||||
fn fat_ptr_constant() -> &'static str {
|
||||
"HELLO"
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = Wrapper(4, [7,6,5]);
|
||||
|
||||
let p = fat_ptr_project(&a);
|
||||
let p = fat_ptr_simple(p);
|
||||
let p = fat_ptr_via_local(p);
|
||||
let p = fat_ptr_from_struct(fat_ptr_to_struct(p));
|
||||
|
||||
let mut target : &[u8] = &[42];
|
||||
fat_ptr_store_to(p, &mut target);
|
||||
assert_eq!(target, &a.1);
|
||||
|
||||
assert_eq!(fat_ptr_constant(), "HELLO");
|
||||
}
|
|
@ -10,9 +10,14 @@ impl Trait for Struct {
|
|||
}
|
||||
}
|
||||
|
||||
struct Foo<T: ?Sized>(T);
|
||||
|
||||
fn main() {
|
||||
let y: &Trait = &Struct(42);
|
||||
y.method();
|
||||
let x: Foo<Struct> = Foo(Struct(42));
|
||||
let y: &Foo<Trait> = &x;
|
||||
y.0.method();
|
||||
/*
|
||||
let x: Box<Fn(i32) -> i32> = Box::new(|x| x * 2);
|
||||
assert_eq!(x(21), 42);
|
||||
|
|
|
@ -21,4 +21,6 @@ fn main() {
|
|||
assert_eq!(use_zst(), A);
|
||||
assert_eq!(&A as *const A as *const (), &() as *const _);
|
||||
assert_eq!(&A as *const A, &A as *const A);
|
||||
let x = 42 as *mut ();
|
||||
unsafe { *x = (); }
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue