diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 331ae7e248b1..5913ff168fc7 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -609,7 +609,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let operand_ty = self.operand_ty(operand); assert_eq!(self.type_size(operand_ty)?, Some(0)); } - let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; + let (offset, ty, _packed) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; + // TODO: The packed flag is ignored // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr()?; @@ -702,7 +703,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { LvalueExtra::DowncastVariant(..) => bug!("attempted to take a reference to an enum downcast lvalue"), }; - self.write_value(val, dest, dest_ty)?; } @@ -826,7 +826,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty: Ty<'tcx>, nndiscr: u64, discrfield: &[u32], - ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { + ) -> EvalResult<'tcx, (Size, Ty<'tcx>, bool)> { // Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant let path = discrfield.iter().skip(2).map(|&i| i as usize); @@ -849,16 +849,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { mut offset: Size, mut ty: Ty<'tcx>, path: I, - ) -> EvalResult<'tcx, (Size, Ty<'tcx>)> { + ) -> EvalResult<'tcx, (Size, Ty<'tcx>, bool)> { // Skip the initial 0 intended for LLVM GEP. + let mut packed = false; for field_index in path { let field_offset = self.get_field_offset(ty, field_index)?; trace!("field_path_offset_and_ty: {}, {}, {:?}, {:?}", field_index, ty, field_offset, offset); - ty = self.get_field_ty(ty, field_index)?; + let field_ty = self.get_field_ty(ty, field_index)?; + ty = field_ty.0; + packed = packed || field_ty.1; offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap(); } - Ok((offset, ty)) + Ok((offset, ty, packed)) } fn get_fat_field(&self, pointee_ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { match (field_index, &self.tcx.struct_tail(pointee_ty).sty) { @@ -870,33 +873,46 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> { + /// Returns the field type and whether the field is packed + pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, (Ty<'tcx>, bool)> { match ty.sty { - ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index), + ty::TyAdt(adt_def, _) if adt_def.is_box() => + Ok((self.get_fat_field(ty.boxed_ty(), field_index)?, false)), ty::TyAdt(adt_def, substs) if adt_def.is_enum() => { use rustc::ty::layout::Layout::*; match *self.type_layout(ty)? { - RawNullablePointer { nndiscr, .. } | - StructWrappedNullablePointer { nndiscr, .. } => Ok(adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs)), + RawNullablePointer { nndiscr, .. } => + Ok((adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs), false)), + StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => { + let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs); + Ok((ty, nonnull.packed)) + }, _ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))), } } ty::TyAdt(adt_def, substs) => { - Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs)) + let variant_def = adt_def.struct_variant(); + use rustc::ty::layout::Layout::*; + match *self.type_layout(ty)? { + Univariant { ref variant, .. } => + Ok((variant_def.fields[field_index].ty(self.tcx, substs), variant.packed)), + _ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))), + } } - ty::TyTuple(fields, _) => Ok(fields[field_index]), + ty::TyTuple(fields, _) => Ok((fields[field_index], false)), ty::TyRef(_, ref tam) | - ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index), + ty::TyRawPtr(ref tam) => Ok((self.get_fat_field(tam.ty, field_index)?, false)), - ty::TyArray(ref inner, _) => Ok(inner), + ty::TyArray(ref inner, _) => Ok((inner, false)), _ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))), } } fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> { + // Also see lvalue_field in lvalue.rs, which handles more cases but needs an actual value at the given type let layout = self.type_layout(ty)?; use rustc::ty::layout::Layout::*; @@ -1236,20 +1252,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ptr: MemoryPointer, mut ty: Ty<'tcx> ) -> EvalResult<'tcx> { + let mut packed = false; while self.get_field_count(ty)? == 1 { - ty = self.get_field_ty(ty, 0)?; + let field = self.get_field_ty(ty, 0)?; + ty = field.0; + packed = packed || field.1; } assert_eq!(self.get_field_count(ty)?, 2); - let field_0 = self.get_field_offset(ty, 0)?.bytes(); - let field_1 = self.get_field_offset(ty, 1)?.bytes(); + let field_0 = self.get_field_offset(ty, 0)?; + let field_1 = self.get_field_offset(ty, 1)?; let field_0_ty = self.get_field_ty(ty, 0)?; let field_1_ty = self.get_field_ty(ty, 1)?; - let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); - let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - let field_0_ptr = ptr.offset(field_0, &self)?.into(); - let field_1_ptr = ptr.offset(field_1, &self)?.into(); - self.memory.write_primval(field_0_ptr, a, field_0_size)?; - self.memory.write_primval(field_1_ptr, b, field_1_size)?; + // The .1 components say whether the field is packed + assert_eq!(field_0_ty.1, field_1_ty.1, "the two fields must agree on being packed"); + packed = packed || field_0_ty.1; + let field_0_size = self.type_size(field_0_ty.0)?.expect("pair element type must be sized"); + let field_1_size = self.type_size(field_1_ty.0)?.expect("pair element type must be sized"); + let field_0_ptr = ptr.offset(field_0.bytes(), &self)?.into(); + let field_1_ptr = ptr.offset(field_1.bytes(), &self)?.into(); + self.write_maybe_aligned(!packed, + |ectx| ectx.memory.write_primval(field_0_ptr, a, field_0_size))?; + self.write_maybe_aligned(!packed, + |ectx| ectx.memory.write_primval(field_1_ptr, b, field_1_size))?; Ok(()) } @@ -1529,8 +1553,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty()); } if self.ty_to_primval_kind(src_ty).is_ok() { - let sty = self.get_field_ty(src_ty, 0)?; - let dty = self.get_field_ty(dest_ty, 0)?; + // TODO: We ignore the packed flag here + let sty = self.get_field_ty(src_ty, 0)?.0; + let dty = self.get_field_ty(dest_ty, 0)?.0; return self.unsize_into(src, sty, dest, dty); } // unsizing of generic struct with pointer fields diff --git a/src/librustc_mir/interpret/lvalue.rs b/src/librustc_mir/interpret/lvalue.rs index e4ab3d90a5c1..da357a6d1e71 100644 --- a/src/librustc_mir/interpret/lvalue.rs +++ b/src/librustc_mir/interpret/lvalue.rs @@ -295,6 +295,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; + //trace!("Field {} of {:?} is at offset {}{}", field_index, base_ty, offset.bytes(), + // if packed { " (packed)" } else { "" }); // Do not allocate in trivial cases let (base_ptr, base_extra, aligned) = match base { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 461fced3c609..591f5dc7fe80 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -1402,20 +1402,20 @@ pub(crate) trait HasMemory<'a, 'tcx> { fn read_maybe_aligned(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T> where F: FnOnce(&mut Self) -> EvalResult<'tcx, T> { - assert!(self.memory_mut().reads_are_aligned, "Unaligned reads must not be nested"); - self.memory_mut().reads_are_aligned = aligned; + let old = self.memory_mut().reads_are_aligned; + self.memory_mut().reads_are_aligned = old && aligned; let t = f(self); - self.memory_mut().reads_are_aligned = true; + self.memory_mut().reads_are_aligned = old; t } fn write_maybe_aligned(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T> where F: FnOnce(&mut Self) -> EvalResult<'tcx, T> { - assert!(self.memory_mut().writes_are_aligned, "Unaligned writes must not be nested"); - self.memory_mut().writes_are_aligned = aligned; + let old = self.memory_mut().writes_are_aligned; + self.memory_mut().writes_are_aligned = old && aligned; let t = f(self); - self.memory_mut().writes_are_aligned = true; + self.memory_mut().writes_are_aligned = old; t } } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 86e123233061..075fab36f64f 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -11,12 +11,14 @@ use rustc::ty; use rustc::ty::layout::Layout; use rustc::ty::subst::Substs; +use syntax::codemap::Span; +use syntax::ast::Mutability; + use error::{EvalResult, EvalError}; use eval_context::{EvalContext, StackPopCleanup}; use lvalue::{Global, GlobalId, Lvalue}; use value::{Value, PrimVal}; -use syntax::codemap::Span; -use syntax::ast::Mutability; +use memory::HasMemory; impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> { @@ -101,12 +103,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { if variant_index as u64 != nndiscr { - let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; + let (offset, ty, packed) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?; let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?; trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); - self.memory.write_uint(nonnull, 0, discr_size)?; + self.write_maybe_aligned(!packed, |ectx| ectx.memory.write_uint(nonnull, 0, discr_size))?; } }, diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 3ee6bab77e05..e5b6d3713812 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -9,7 +9,7 @@ use syntax::abi::Abi; use error::{EvalError, EvalResult}; use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited, self}; use lvalue::Lvalue; -use memory::{MemoryPointer, TlsKey, Kind}; +use memory::{MemoryPointer, TlsKey, Kind, HasMemory}; use value::{PrimVal, Value}; use rustc_data_structures::indexed_vec::Idx; use const_eval; @@ -402,7 +402,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); - let ty = self.get_field_ty(ty, 0)?; + let ty = self.get_field_ty(ty, 0)?.0; // TODO: packed flag is ignored match arg_operands[0] { mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty), _ => bug!("virtual call first arg cannot be a constant"), @@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(false) } - pub fn read_discriminant_value(&self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + pub fn read_discriminant_value(&mut self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { use rustc::ty::layout::Layout::*; let adt_layout = self.type_layout(adt_ty)?; //trace!("read_discriminant_value {:#?}", adt_layout); @@ -487,12 +487,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { - let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; - let nonnull = adt_ptr.offset(offset.bytes(), self)?; + let (offset, ty, packed) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; + let nonnull = adt_ptr.offset(offset.bytes(), &*self)?; trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); - self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)? + self.read_maybe_aligned(!packed, + |ectx| ectx.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size))? } // The discriminant_value intrinsic returns 0 for non-sum types. diff --git a/tests/run-pass/packed_struct.rs b/tests/run-pass/packed_struct.rs index 7219649e728c..0c4781198282 100644 --- a/tests/run-pass/packed_struct.rs +++ b/tests/run-pass/packed_struct.rs @@ -40,7 +40,8 @@ fn test_unsizing() { let arr = [1, 2, 3]; let arr_unaligned: UnalignedPtr<[i32; 3]> = UnalignedPtr { data: &arr }; - let _uns: UnalignedPtr<[i32]> = arr_unaligned; + let arr_unaligned: UnalignedPtr<[i32]> = arr_unaligned; + let _unused = &arr_unaligned; // forcing an allocation, which could also yield "unaligned write"-errors } fn main() {