Skip to content

Commit 0f578f0

Browse files
committed
fully implement size_of_val and add various tests that now succeed
1 parent 17e336c commit 0f578f0

10 files changed

+551
-17
lines changed

src/interpreter/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
621621
let src = self.eval_operand_to_ptr(operand)?;
622622
let src_ty = self.operand_ty(operand);
623623
let dest_ty = self.monomorphize(dest_ty, self.substs());
624+
// FIXME: cases where dest_ty is not a fat pointer. e.g. Arc<Struct> -> Arc<Trait>
624625
assert!(self.type_is_fat_ptr(dest_ty));
625626
let (ptr, extra) = self.get_fat_ptr(dest);
626627
self.move_(src, ptr, src_ty)?;
@@ -883,6 +884,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
883884
let offset = variant.field_offset(field.index()).bytes();
884885
let ptr = base.ptr.offset(offset as isize);
885886
match (&field_ty.sty, base.extra) {
887+
(&ty::TyStr, extra @ LvalueExtra::Length(_)) |
888+
(&ty::TySlice(_), extra @ LvalueExtra::Length(_)) |
886889
(&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue {
887890
ptr: ptr,
888891
extra: extra,

src/interpreter/terminator/intrinsics.rs

+112-16
Original file line numberDiff line numberDiff line change
@@ -188,22 +188,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
188188

189189
"size_of_val" => {
190190
let ty = substs.type_at(0);
191-
if self.type_is_sized(ty) {
192-
let size = self.type_size(ty) as u64;
193-
self.memory.write_uint(dest, size, pointer_size)?;
194-
} else {
195-
match ty.sty {
196-
ty::TySlice(_) | ty::TyStr => {
197-
let elem_ty = ty.sequence_element_type(self.tcx);
198-
let elem_size = self.type_size(elem_ty) as u64;
199-
let ptr_size = self.memory.pointer_size() as isize;
200-
let n = self.memory.read_usize(args_ptrs[0].offset(ptr_size))?;
201-
self.memory.write_uint(dest, n * elem_size, pointer_size)?;
202-
}
203-
204-
_ => return Err(EvalError::Unimplemented(format!("unimplemented: size_of_val::<{:?}>", ty))),
205-
}
206-
}
191+
let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?;
192+
self.memory.write_uint(dest, size, pointer_size)?;
207193
}
208194
// FIXME: wait for eval_operand_to_ptr to be gone
209195
/*
@@ -248,4 +234,114 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
248234
// current frame.
249235
Ok(())
250236
}
237+
238+
fn size_and_align_of_dst(
239+
&self,
240+
ty: ty::Ty<'tcx>,
241+
value: Pointer,
242+
) -> EvalResult<'tcx, (u64, u64)> {
243+
let pointer_size = self.memory.pointer_size();
244+
if self.type_is_sized(ty) {
245+
Ok((self.type_size(ty) as u64, self.type_align(ty) as u64))
246+
} else {
247+
match ty.sty {
248+
ty::TyAdt(def, substs) => {
249+
// First get the size of all statically known fields.
250+
// Don't use type_of::sizing_type_of because that expects t to be sized,
251+
// and it also rounds up to alignment, which we want to avoid,
252+
// as the unsized field's alignment could be smaller.
253+
assert!(!ty.is_simd());
254+
let layout = self.type_layout(ty);
255+
debug!("DST {} layout: {:?}", ty, layout);
256+
257+
// Returns size in bytes of all fields except the last one
258+
// (we will be recursing on the last one).
259+
fn local_prefix_bytes(variant: &ty::layout::Struct) -> u64 {
260+
let fields = variant.offset_after_field.len();
261+
if fields > 1 {
262+
variant.offset_after_field[fields - 2].bytes()
263+
} else {
264+
0
265+
}
266+
}
267+
268+
let (sized_size, sized_align) = match *layout {
269+
ty::layout::Layout::Univariant { ref variant, .. } => {
270+
(local_prefix_bytes(variant), variant.align.abi())
271+
}
272+
_ => {
273+
bug!("size_and_align_of_dst: expcted Univariant for `{}`, found {:#?}",
274+
ty, layout);
275+
}
276+
};
277+
debug!("DST {} statically sized prefix size: {} align: {}",
278+
ty, sized_size, sized_align);
279+
280+
// Recurse to get the size of the dynamically sized field (must be
281+
// the last field).
282+
let last_field = def.struct_variant().fields.last().unwrap();
283+
let field_ty = self.field_ty(substs, last_field);
284+
let (unsized_size, unsized_align) = self.size_and_align_of_dst(field_ty, value)?;
285+
286+
// FIXME (#26403, #27023): We should be adding padding
287+
// to `sized_size` (to accommodate the `unsized_align`
288+
// required of the unsized field that follows) before
289+
// summing it with `sized_size`. (Note that since #26403
290+
// is unfixed, we do not yet add the necessary padding
291+
// here. But this is where the add would go.)
292+
293+
// Return the sum of sizes and max of aligns.
294+
let size = sized_size + unsized_size;
295+
296+
// Choose max of two known alignments (combined value must
297+
// be aligned according to more restrictive of the two).
298+
let align = ::std::cmp::max(sized_align, unsized_align);
299+
300+
// Issue #27023: must add any necessary padding to `size`
301+
// (to make it a multiple of `align`) before returning it.
302+
//
303+
// Namely, the returned size should be, in C notation:
304+
//
305+
// `size + ((size & (align-1)) ? align : 0)`
306+
//
307+
// emulated via the semi-standard fast bit trick:
308+
//
309+
// `(size + (align-1)) & -align`
310+
311+
if size & (align - 1) != 0 {
312+
Ok((size + align, align))
313+
} else {
314+
Ok((size, align))
315+
}
316+
}
317+
ty::TyTrait(..) => {
318+
let (_, vtable) = self.get_fat_ptr(value);
319+
let vtable = self.memory.read_ptr(vtable)?;
320+
// the second entry in the vtable is the dynamic size of the object.
321+
let size = self.memory.read_usize(vtable.offset(pointer_size as isize))?;
322+
let align = self.memory.read_usize(vtable.offset(pointer_size as isize * 2))?;
323+
Ok((size, align))
324+
}
325+
326+
ty::TySlice(_) | ty::TyStr => {
327+
let elem_ty = ty.sequence_element_type(self.tcx);
328+
let elem_size = self.type_size(elem_ty) as u64;
329+
let (_, len_ptr) = self.get_fat_ptr(value);
330+
let n = self.memory.read_usize(len_ptr)?;
331+
let align = self.type_align(elem_ty);
332+
Ok((n * elem_size, align as u64))
333+
}
334+
335+
_ => bug!("size_of_val::<{:?}>", ty),
336+
}
337+
}
338+
}
339+
/// Returns the normalized type of a struct field
340+
fn field_ty(
341+
&self,
342+
param_substs: &Substs<'tcx>,
343+
f: ty::FieldDef<'tcx>,
344+
)-> ty::Ty<'tcx> {
345+
self.tcx.normalize_associated_type(&f.ty(self.tcx, param_substs))
346+
}
251347
}

src/memory.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
120120
bytes: Vec::new(),
121121
relocations: BTreeMap::new(),
122122
undef_mask: UndefMask::new(0),
123-
align: 1,
123+
align: 8, // should be infinity?
124124
immutable: false, // must be mutable, because sometimes we "move out" of a ZST
125125
};
126126
mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that you can cast between different pointers to trait objects
12+
// whose vtable have the same kind (both lengths, or both trait pointers).
13+
14+
trait Foo<T> {
15+
fn foo(&self, _: T) -> u32 { 42 }
16+
}
17+
18+
trait Bar {
19+
fn bar(&self) { println!("Bar!"); }
20+
}
21+
22+
impl<T> Foo<T> for () {}
23+
impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
24+
impl Bar for () {}
25+
26+
unsafe fn round_trip_and_call<'a>(t: *const (Foo<u32>+'a)) -> u32 {
27+
let foo_e : *const Foo<u16> = t as *const _;
28+
let r_1 = foo_e as *mut Foo<u32>;
29+
30+
(&*r_1).foo(0)
31+
}
32+
33+
#[repr(C)]
34+
struct FooS<T:?Sized>(T);
35+
#[repr(C)]
36+
struct BarS<T:?Sized>(T);
37+
38+
fn foo_to_bar<T:?Sized>(u: *const FooS<T>) -> *const BarS<T> {
39+
u as *const BarS<T>
40+
}
41+
42+
fn main() {
43+
let x = 4u32;
44+
let y : &Foo<u32> = &x;
45+
let fl = unsafe { round_trip_and_call(y as *const Foo<u32>) };
46+
assert_eq!(fl, (43+4));
47+
48+
let s = FooS([0,1,2]);
49+
let u: &FooS<[u32]> = &s;
50+
let u: *const FooS<[u32]> = u;
51+
let bar_ref : *const BarS<[u32]> = foo_to_bar(u);
52+
let z : &BarS<[u32]> = unsafe{&*bar_ref};
53+
assert_eq!(&z.0, &[0,1,2]);
54+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Test<T: ?Sized>(T);
12+
13+
fn main() {
14+
let x = Test([1,2,3]);
15+
let x : &Test<[i32]> = &x;
16+
17+
let & ref _y = x;
18+
19+
// Make sure binding to a fat pointer behind a reference
20+
// still works
21+
let slice = &[1,2,3];
22+
let x = Test(&slice);
23+
let Test(&_slice) = x;
24+
}

tests/run-pass/dst-raw.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test DST raw pointers
12+
13+
14+
trait Trait {
15+
fn foo(&self) -> isize;
16+
}
17+
18+
struct A {
19+
f: isize
20+
}
21+
impl Trait for A {
22+
fn foo(&self) -> isize {
23+
self.f
24+
}
25+
}
26+
27+
struct Foo<T: ?Sized> {
28+
f: T
29+
}
30+
31+
pub fn main() {
32+
// raw trait object
33+
let x = A { f: 42 };
34+
let z: *const Trait = &x;
35+
let r = unsafe {
36+
(&*z).foo()
37+
};
38+
assert_eq!(r, 42);
39+
40+
// raw DST struct
41+
let p = Foo {f: A { f: 42 }};
42+
let o: *const Foo<Trait> = &p;
43+
let r = unsafe {
44+
(&*o).f.foo()
45+
};
46+
assert_eq!(r, 42);
47+
48+
// raw slice
49+
let a: *const [_] = &[1, 2, 3];
50+
unsafe {
51+
let b = (*a)[2];
52+
assert_eq!(b, 3);
53+
let len = (*a).len();
54+
assert_eq!(len, 3);
55+
}
56+
57+
// raw slice with explicit cast
58+
let a = &[1, 2, 3] as *const [i32];
59+
unsafe {
60+
let b = (*a)[2];
61+
assert_eq!(b, 3);
62+
let len = (*a).len();
63+
assert_eq!(len, 3);
64+
}
65+
66+
// raw DST struct with slice
67+
let c: *const Foo<[_]> = &Foo {f: [1, 2, 3]};
68+
unsafe {
69+
let b = (&*c).f[0];
70+
assert_eq!(b, 1);
71+
let len = (&*c).f.len();
72+
assert_eq!(len, 3);
73+
}
74+
75+
// all of the above with *mut
76+
let mut x = A { f: 42 };
77+
let z: *mut Trait = &mut x;
78+
let r = unsafe {
79+
(&*z).foo()
80+
};
81+
assert_eq!(r, 42);
82+
83+
let mut p = Foo {f: A { f: 42 }};
84+
let o: *mut Foo<Trait> = &mut p;
85+
let r = unsafe {
86+
(&*o).f.foo()
87+
};
88+
assert_eq!(r, 42);
89+
90+
let a: *mut [_] = &mut [1, 2, 3];
91+
unsafe {
92+
let b = (*a)[2];
93+
assert_eq!(b, 3);
94+
let len = (*a).len();
95+
assert_eq!(len, 3);
96+
}
97+
98+
let a = &mut [1, 2, 3] as *mut [i32];
99+
unsafe {
100+
let b = (*a)[2];
101+
assert_eq!(b, 3);
102+
let len = (*a).len();
103+
assert_eq!(len, 3);
104+
}
105+
106+
let c: *mut Foo<[_]> = &mut Foo {f: [1, 2, 3]};
107+
unsafe {
108+
let b = (&*c).f[0];
109+
assert_eq!(b, 1);
110+
let len = (&*c).f.len();
111+
assert_eq!(len, 3);
112+
}
113+
}

0 commit comments

Comments
 (0)