Skip to content

Commit 1e3659e

Browse files
authored
Merge pull request rust-lang#59 from oli-obk/master
enable A<Struct> -> A<Trait> downcasting where `A<Trait>` is a fat pointer
2 parents 5b012ed + 38748fa commit 1e3659e

18 files changed

+634
-85
lines changed

Diff for: src/error.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use syntax::codemap::Span;
1010
pub enum EvalError<'tcx> {
1111
FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>),
1212
DanglingPointerDeref,
13+
InvalidMemoryAccess,
1314
InvalidFunctionPointer,
1415
InvalidBool,
1516
InvalidDiscriminant,
@@ -19,7 +20,6 @@ pub enum EvalError<'tcx> {
1920
allocation_size: usize,
2021
},
2122
ReadPointerAsBytes,
22-
ReadBytesAsPointer,
2323
InvalidPointerMath,
2424
ReadUndefBytes,
2525
InvalidBoolOp(mir::BinOp),
@@ -53,6 +53,8 @@ impl<'tcx> Error for EvalError<'tcx> {
5353
match *self {
5454
EvalError::FunctionPointerTyMismatch(..) =>
5555
"tried to call a function through a function pointer of a different type",
56+
EvalError::InvalidMemoryAccess =>
57+
"tried to access memory through an invalid pointer",
5658
EvalError::DanglingPointerDeref =>
5759
"dangling pointer was dereferenced",
5860
EvalError::InvalidFunctionPointer =>
@@ -65,8 +67,6 @@ impl<'tcx> Error for EvalError<'tcx> {
6567
"pointer offset outside bounds of allocation",
6668
EvalError::ReadPointerAsBytes =>
6769
"a raw memory access tried to access part of a pointer value as raw bytes",
68-
EvalError::ReadBytesAsPointer =>
69-
"attempted to interpret some raw bytes as a pointer address",
7070
EvalError::InvalidPointerMath =>
7171
"attempted to do math or a comparison on pointers into different allocations",
7272
EvalError::ReadUndefBytes =>

Diff for: src/interpreter/cast.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
2525
U16(u) => self.cast_const_int(u as u64, ty, false),
2626
U32(u) => self.cast_const_int(u as u64, ty, false),
2727
Char(c) => self.cast_const_int(c as u64, ty, false),
28-
U64(u) |
29-
IntegerPtr(u) => self.cast_const_int(u, ty, false),
28+
U64(u) => self.cast_const_int(u, ty, false),
3029
FnPtr(ptr) |
3130
Ptr(ptr) => self.cast_ptr(ptr, ty),
3231
}
@@ -74,7 +73,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
7473
ty::TyFloat(ast::FloatTy::F64) => Ok(F64(v as f64)),
7574
ty::TyFloat(ast::FloatTy::F32) if negative => Ok(F32(v as i64 as f32)),
7675
ty::TyFloat(ast::FloatTy::F32) => Ok(F32(v as f32)),
77-
ty::TyRawPtr(_) => Ok(IntegerPtr(v)),
76+
ty::TyRawPtr(_) => Ok(Ptr(Pointer::from_int(v as usize))),
7877
ty::TyChar if v as u8 as u64 == v => Ok(Char(v as u8 as char)),
7978
ty::TyChar => Err(EvalError::InvalidChar(v)),
8079
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),

Diff for: src/interpreter/mod.rs

+18-9
Original file line numberDiff line numberDiff line change
@@ -621,12 +621,16 @@ 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)?;
627628
let src_pointee_ty = pointee_type(src_ty).unwrap();
628629
let dest_pointee_ty = pointee_type(dest_ty).unwrap();
629630

631+
// A<Struct> -> A<Trait> conversion
632+
let (src_pointee_ty, dest_pointee_ty) = self.tcx.struct_lockstep_tails(src_pointee_ty, dest_pointee_ty);
633+
630634
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
631635
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
632636
self.memory.write_usize(extra, length as u64)?;
@@ -858,7 +862,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
858862

859863
use rustc::mir::repr::ProjectionElem::*;
860864
match proj.elem {
861-
Field(field, _) => {
865+
Field(field, field_ty) => {
862866
use rustc::ty::layout::Layout::*;
863867
let variant = match *base_layout {
864868
Univariant { ref variant, .. } => variant,
@@ -878,7 +882,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
878882
};
879883

880884
let offset = variant.field_offset(field.index()).bytes();
881-
base.ptr.offset(offset as isize)
885+
let ptr = base.ptr.offset(offset as isize);
886+
match (&field_ty.sty, base.extra) {
887+
(&ty::TyStr, extra @ LvalueExtra::Length(_)) |
888+
(&ty::TySlice(_), extra @ LvalueExtra::Length(_)) |
889+
(&ty::TyTrait(_), extra @ LvalueExtra::Vtable(_)) => return Ok(Lvalue {
890+
ptr: ptr,
891+
extra: extra,
892+
}),
893+
(&ty::TyTrait(_), _) => bug!("trait field without vtable"),
894+
_ => ptr,
895+
}
882896
},
883897

884898
Downcast(_, variant) => {
@@ -899,6 +913,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
899913

900914
Deref => {
901915
let pointee_ty = pointee_type(base_ty).expect("Deref of non-pointer");
916+
let pointee_ty = self.tcx.struct_tail(pointee_ty);
902917
let ptr = self.memory.read_ptr(base.ptr)?;
903918
let extra = match pointee_ty.sty {
904919
ty::TySlice(_) | ty::TyStr => {
@@ -1049,13 +1064,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
10491064
&ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
10501065
&ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => {
10511066
if self.type_is_sized(ty) {
1052-
match self.memory.read_ptr(ptr) {
1053-
Ok(p) => PrimVal::Ptr(p),
1054-
Err(EvalError::ReadBytesAsPointer) => {
1055-
PrimVal::IntegerPtr(self.memory.read_usize(ptr)?)
1056-
}
1057-
Err(e) => return Err(e),
1058-
}
1067+
PrimVal::Ptr(self.memory.read_ptr(ptr)?)
10591068
} else {
10601069
bug!("primitive read of fat pointer type: {:?}", ty);
10611070
}

Diff for: src/interpreter/terminator/intrinsics.rs

+115-28
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
132132
let ptr_arg = args_ptrs[0];
133133
let offset = self.memory.read_isize(args_ptrs[1])?;
134134

135-
match self.memory.read_ptr(ptr_arg) {
136-
Ok(ptr) => {
137-
let result_ptr = ptr.offset(offset as isize * pointee_size);
138-
self.memory.write_ptr(dest, result_ptr)?;
139-
}
140-
Err(EvalError::ReadBytesAsPointer) => {
141-
let addr = self.memory.read_isize(ptr_arg)?;
142-
let result_addr = addr + offset * pointee_size as i64;
143-
self.memory.write_isize(dest, result_addr)?;
144-
}
145-
Err(e) => return Err(e),
146-
}
135+
let ptr = self.memory.read_ptr(ptr_arg)?;
136+
let result_ptr = ptr.offset(offset as isize * pointee_size);
137+
self.memory.write_ptr(dest, result_ptr)?;
147138
}
148139

149140
"overflowing_sub" => {
@@ -188,22 +179,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
188179

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

0 commit comments

Comments
 (0)