1
Fork 0

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:
Scott Olson 2016-09-23 18:07:02 -06:00 committed by GitHub
commit 1e3659eabb
18 changed files with 634 additions and 85 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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
}

View 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]);
}

View 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
View 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);
}
}

View 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());
}

View 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);
}

View 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);
}

View 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");
}

View file

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

View file

@ -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 = (); }
}