diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index a7e4407ce59..b590905264e 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -621,6 +621,7 @@ 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 -> Arc assert!(self.type_is_fat_ptr(dest_ty)); let (ptr, extra) = self.get_fat_ptr(dest); self.move_(src, ptr, src_ty)?; @@ -883,6 +884,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let offset = variant.field_offset(field.index()).bytes(); 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, diff --git a/src/interpreter/terminator/intrinsics.rs b/src/interpreter/terminator/intrinsics.rs index e2381d59e2c..f7fa1588b56 100644 --- a/src/interpreter/terminator/intrinsics.rs +++ b/src/interpreter/terminator/intrinsics.rs @@ -188,22 +188,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 +234,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)) + } } diff --git a/src/memory.rs b/src/memory.rs index 0108f7e5d9c..980f9ca728b 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -120,7 +120,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { bytes: Vec::new(), relocations: BTreeMap::new(), undef_mask: UndefMask::new(0), - align: 1, + align: 8, // should be infinity? immutable: false, // must be mutable, because sometimes we "move out" of a ZST }; mem.alloc_map.insert(ZST_ALLOC_ID, alloc); diff --git a/tests/run-pass/cast-rfc0401-vtable-kinds.rs b/tests/run-pass/cast-rfc0401-vtable-kinds.rs new file mode 100644 index 00000000000..3a9f24ad4cc --- /dev/null +++ b/tests/run-pass/cast-rfc0401-vtable-kinds.rs @@ -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 or the MIT license +// , 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 { + fn foo(&self, _: T) -> u32 { 42 } +} + +trait Bar { + fn bar(&self) { println!("Bar!"); } +} + +impl Foo for () {} +impl Foo for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } +impl Bar for () {} + +unsafe fn round_trip_and_call<'a>(t: *const (Foo+'a)) -> u32 { + let foo_e : *const Foo = t as *const _; + let r_1 = foo_e as *mut Foo; + + (&*r_1).foo(0) +} + +#[repr(C)] +struct FooS(T); +#[repr(C)] +struct BarS(T); + +fn foo_to_bar(u: *const FooS) -> *const BarS { + u as *const BarS +} + +fn main() { + let x = 4u32; + let y : &Foo = &x; + let fl = unsafe { round_trip_and_call(y as *const Foo) }; + 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]); +} diff --git a/tests/run-pass/dst-irrefutable-bind.rs b/tests/run-pass/dst-irrefutable-bind.rs new file mode 100644 index 00000000000..9f8067f372a --- /dev/null +++ b/tests/run-pass/dst-irrefutable-bind.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Test(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; +} diff --git a/tests/run-pass/dst-raw.rs b/tests/run-pass/dst-raw.rs new file mode 100644 index 00000000000..3a74626b029 --- /dev/null +++ b/tests/run-pass/dst-raw.rs @@ -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 or the MIT license +// , 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 { + 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 = &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 = &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); + } +} diff --git a/tests/run-pass/dst-struct-sole.rs b/tests/run-pass/dst-struct-sole.rs new file mode 100644 index 00000000000..58d7b35a527 --- /dev/null +++ b/tests/run-pass/dst-struct-sole.rs @@ -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 or the MIT license +// , 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 { + 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(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()); +} diff --git a/tests/run-pass/issue-23261.rs b/tests/run-pass/issue-23261.rs new file mode 100644 index 00000000000..fc806f5429a --- /dev/null +++ b/tests/run-pass/issue-23261.rs @@ -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 or the MIT license +// , 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 { + 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) { + 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 = &Foo { a: 32, inner: 32 }; + check_trait_obj(foo); +} diff --git a/tests/run-pass/issue-36278-prefix-nesting.rs b/tests/run-pass/issue-36278-prefix-nesting.rs new file mode 100644 index 00000000000..95269d0569d --- /dev/null +++ b/tests/run-pass/issue-36278-prefix-nesting.rs @@ -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 or the MIT license +// , 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([u8; SZ], T); + +type Ack = P>; + +fn main() { + let size_of_sized; let size_of_unsized; + let x: Box> = Box::new(P([0; SZ], P([0; SZ], [0; 0]))); + size_of_sized = mem::size_of_val::>(&x); + let y: Box> = x; + size_of_unsized = mem::size_of_val::>(&y); + assert_eq!(size_of_sized, size_of_unsized); +} diff --git a/tests/run-pass/mir_fat_ptr.rs b/tests/run-pass/mir_fat_ptr.rs new file mode 100644 index 00000000000..e5c9e3577d1 --- /dev/null +++ b/tests/run-pass/mir_fat_ptr.rs @@ -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 or the MIT license +// , 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(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"); +}