X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc_mir%2Finterpret%2Fplace.rs;h=758230e2b7dcb8c271ed90531de5fc9908ba5784;hb=80ff07f30d01da3bed8316af81d3836254ea1769;hp=6381d3888c29399727b0129614ca7ce779044922;hpb=83b74f2aad1191ca93265aece416f8641e49524a;p=rust.git diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 6381d3888c2..758230e2b7d 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -5,7 +5,6 @@ use std::convert::TryFrom; use std::hash::Hash; -use rustc::hir; use rustc::mir; use rustc::mir::interpret::truncate; use rustc::ty::{self, Ty}; @@ -13,7 +12,7 @@ use rustc::ty::TypeFoldable; use super::{ - GlobalId, AllocId, Allocation, Scalar, EvalResult, Pointer, PointerArithmetic, + GlobalId, AllocId, Allocation, Scalar, InterpResult, Pointer, PointerArithmetic, InterpretCx, Machine, AllocMap, AllocationExtra, RawConst, Immediate, ImmTy, ScalarMaybeUndef, Operand, OpTy, MemoryKind, LocalValue }; @@ -131,7 +130,7 @@ pub fn to_scalar_ptr_align(self) -> (Scalar, Align) { /// metact the ptr part of the mplace #[inline(always)] - pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { + pub fn to_ptr(self) -> InterpResult<'tcx, Pointer> { // At this point, we forget about the alignment information -- // the place has been turned into a reference, and no matter where it came from, // it now must be aligned. @@ -153,7 +152,7 @@ pub fn offset( offset: Size, meta: Option>, cx: &impl HasDataLayout, - ) -> EvalResult<'tcx, Self> { + ) -> InterpResult<'tcx, Self> { Ok(MemPlace { ptr: self.ptr.ptr_offset(offset, cx)?, align: self.align.restrict_for_offset(offset), @@ -191,7 +190,7 @@ pub fn offset( meta: Option>, layout: TyLayout<'tcx>, cx: &impl HasDataLayout, - ) -> EvalResult<'tcx, Self> { + ) -> InterpResult<'tcx, Self> { Ok(MPlaceTy { mplace: self.mplace.offset(offset, meta, cx)?, layout, @@ -204,7 +203,7 @@ fn from_aligned_ptr(ptr: Pointer, layout: TyLayout<'tcx>) -> Self { } #[inline] - pub(super) fn len(self, cx: &impl HasDataLayout) -> EvalResult<'tcx, u64> { + pub(super) fn len(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> { if self.layout.is_unsized() { // We need to consult `meta` metadata match self.layout.ty.sty { @@ -223,7 +222,7 @@ pub(super) fn len(self, cx: &impl HasDataLayout) -> EvalResult<'tcx, u64> { } #[inline] - pub(super) fn vtable(self) -> EvalResult<'tcx, Pointer> { + pub(super) fn vtable(self) -> InterpResult<'tcx, Pointer> { match self.layout.ty.sty { ty::Dynamic(..) => self.mplace.meta.unwrap().to_ptr(), _ => bug!("vtable not supported on type {:?}", self.layout.ty), @@ -278,7 +277,7 @@ pub fn to_scalar_ptr_align(self) -> (Scalar, Align) { } #[inline] - pub fn to_ptr(self) -> EvalResult<'tcx, Pointer> { + pub fn to_ptr(self) -> InterpResult<'tcx, Pointer> { self.to_mem_place().to_ptr() } } @@ -294,7 +293,7 @@ pub fn to_mem_place(self) -> MPlaceTy<'tcx, Tag> { impl<'a, 'mir, 'tcx, Tag, M> InterpretCx<'a, 'mir, 'tcx, M> where // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 - Tag: ::std::fmt::Debug+Default+Copy+Eq+Hash+'static, + Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static, M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>, // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 M::MemoryMap: AllocMap, Allocation)>, @@ -307,7 +306,7 @@ impl<'a, 'mir, 'tcx, Tag, M> InterpretCx<'a, 'mir, 'tcx, M> pub fn ref_to_mplace( &self, val: ImmTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty; let layout = self.layout_of(pointee_type)?; @@ -325,25 +324,13 @@ pub fn ref_to_mplace( // Take an operand, representing a pointer, and dereference it to a place -- that // will always be a MemPlace. Lives in `place.rs` because it creates a place. - // This calls the "deref" machine hook, and counts as a deref as far as - // Stacked Borrows is concerned. pub fn deref_operand( &self, src: OpTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let val = self.read_immediate(src)?; trace!("deref to {} on {:?}", val.layout.ty, *val); - let mut place = self.ref_to_mplace(val)?; - // Pointer tag tracking might want to adjust the tag. - let mutbl = match val.layout.ty.sty { - // `builtin_deref` considers boxes immutable, that's useless for our purposes - ty::Ref(_, _, mutbl) => Some(mutbl), - ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable), - ty::RawPtr(_) => None, - _ => bug!("Unexpected pointer type {}", val.layout.ty), - }; - place.mplace.ptr = M::tag_dereference(self, place, mutbl)?; - Ok(place) + self.ref_to_mplace(val) } /// Offset a pointer to project to a field. Unlike `place_field`, this is always @@ -354,7 +341,7 @@ pub fn mplace_field( &self, base: MPlaceTy<'tcx, M::PointerTag>, field: u64, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { // Not using the layout method because we want to compute on u64 let offset = match base.layout.fields { layout::FieldPlacement::Arbitrary { ref offsets, .. } => @@ -410,7 +397,7 @@ pub fn mplace_array_fields( &self, base: MPlaceTy<'tcx, Tag>, ) -> - EvalResult<'tcx, impl Iterator>> + 'a> + InterpResult<'tcx, impl Iterator>> + 'a> { let len = base.len(self)?; // also asserts that we have a type where this makes sense let stride = match base.layout.fields { @@ -427,7 +414,7 @@ pub fn mplace_subslice( base: MPlaceTy<'tcx, M::PointerTag>, from: u64, to: u64, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { let len = base.len(self)?; // also asserts that we have a type where this makes sense assert!(from <= len - to); @@ -461,7 +448,7 @@ pub fn mplace_downcast( &self, base: MPlaceTy<'tcx, M::PointerTag>, variant: VariantIdx, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { // Downcasts only change the layout assert!(base.meta.is_none()); Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base }) @@ -472,7 +459,7 @@ pub fn mplace_projection( &self, base: MPlaceTy<'tcx, M::PointerTag>, proj_elem: &mir::PlaceElem<'tcx>, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { use rustc::mir::ProjectionElem::*; Ok(match *proj_elem { Field(field, _) => self.mplace_field(base, field.index() as u64)?, @@ -517,7 +504,7 @@ pub fn place_field( &mut self, base: PlaceTy<'tcx, M::PointerTag>, field: u64, - ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { // FIXME: We could try to be smarter and avoid allocation for fields that span the // entire place. let mplace = self.force_allocation(base)?; @@ -528,7 +515,7 @@ pub fn place_downcast( &self, base: PlaceTy<'tcx, M::PointerTag>, variant: VariantIdx, - ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { // Downcast just changes the layout Ok(match base.place { Place::Ptr(mplace) => @@ -545,7 +532,7 @@ pub fn place_projection( &mut self, base: PlaceTy<'tcx, M::PointerTag>, proj_elem: &mir::ProjectionElem>, - ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { use rustc::mir::ProjectionElem::*; Ok(match *proj_elem { Field(field, _) => self.place_field(base, field.index() as u64)?, @@ -565,7 +552,7 @@ pub fn place_projection( pub(super) fn eval_static_to_mplace( &self, place_static: &mir::Static<'tcx> - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { use rustc::mir::StaticKind; Ok(match place_static.kind { @@ -587,18 +574,23 @@ pub(super) fn eval_static_to_mplace( promoted: None }; // Just create a lazy reference, so we can support recursive statics. - // tcx takes are of assigning every static one and only one unique AllocId. + // tcx takes care of assigning every static one and only one unique AllocId. // When the data here is ever actually used, memory will notice, // and it knows how to deal with alloc_id that are present in the // global table but not in its local memory: It calls back into tcx through // a query, triggering the CTFE machinery to actually turn this lazy reference // into a bunch of bytes. IOW, statics are evaluated with CTFE even when // this InterpretCx uses another Machine (e.g., in miri). This is what we - // want! This way, computing statics works concistently between codegen + // want! This way, computing statics works consistently between codegen // and miri: They use the same query to eventually obtain a `ty::Const` // and use that for further computation. - let alloc = self.tcx.alloc_map.lock().create_static_alloc(cid.instance.def_id()); - MPlaceTy::from_aligned_ptr(Pointer::from(alloc).with_default_tag(), layout) + // + // Notice that statics have *two* AllocIds: the lazy one, and the resolved + // one. Here we make sure that the interpreted program never sees the + // resolved ID. Also see the doc comment of `Memory::get_static_alloc`. + let alloc_id = self.tcx.alloc_map.lock().create_static_alloc(cid.instance.def_id()); + let ptr = self.tag_static_base_pointer(Pointer::from(alloc_id)); + MPlaceTy::from_aligned_ptr(ptr, layout) } }) } @@ -608,7 +600,7 @@ pub(super) fn eval_static_to_mplace( pub fn eval_place( &mut self, mir_place: &mir::Place<'tcx>, - ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { use rustc::mir::PlaceBase; mir_place.iterate(|place_base, place_projection| { @@ -620,7 +612,7 @@ pub fn eval_place( PlaceTy { place: *return_place, layout: self - .layout_of(self.monomorphize(self.frame().mir.return_ty())?)?, + .layout_of(self.monomorphize(self.frame().body.return_ty())?)?, } } None => return err!(InvalidNullPointerUsage), @@ -650,7 +642,7 @@ pub fn write_scalar( &mut self, val: impl Into>, dest: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { self.write_immediate(Immediate::Scalar(val.into()), dest) } @@ -660,7 +652,7 @@ pub fn write_immediate( &mut self, src: Immediate, dest: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { self.write_immediate_no_validate(src, dest)?; if M::enforce_validity(self) { @@ -671,6 +663,23 @@ pub fn write_immediate( Ok(()) } + /// Write an `Immediate` to memory. + #[inline(always)] + pub fn write_immediate_to_mplace( + &mut self, + src: Immediate, + dest: MPlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + self.write_immediate_to_mplace_no_validate(src, dest)?; + + if M::enforce_validity(self) { + // Data got changed, better make sure it matches the type! + self.validate_operand(dest.into(), vec![], None, /*const_mode*/ false)?; + } + + Ok(()) + } + /// Write an immediate to a place. /// If you use this you are responsible for validating that things got copied at the /// right type. @@ -678,7 +687,7 @@ fn write_immediate_no_validate( &mut self, src: Immediate, dest: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { if cfg!(debug_assertions) { // This is a very common path, avoid some checks in release mode assert!(!dest.layout.is_unsized(), "Cannot write unsized data"); @@ -728,7 +737,7 @@ fn write_immediate_to_mplace_no_validate( &mut self, value: Immediate, dest: MPlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { let (ptr, ptr_align) = dest.to_scalar_ptr_align(); // Note that it is really important that the type here is the right one, and matches the // type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here @@ -792,7 +801,7 @@ pub fn copy_op( &mut self, src: OpTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { self.copy_op_no_validate(src, dest)?; if M::enforce_validity(self) { @@ -811,7 +820,7 @@ fn copy_op_no_validate( &mut self, src: OpTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { // We do NOT compare the types for equality, because well-typed code can // actually "transmute" `&mut T` to `&T` in an assignment without a cast. assert!(src.layout.details == dest.layout.details, @@ -856,7 +865,7 @@ pub fn copy_op_transmute( &mut self, src: OpTy<'tcx, M::PointerTag>, dest: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { if src.layout.details == dest.layout.details { // Fast path: Just use normal `copy_op` return self.copy_op(src, dest); @@ -903,7 +912,7 @@ pub fn force_allocation_maybe_sized( &mut self, place: PlaceTy<'tcx, M::PointerTag>, meta: Option>, - ) -> EvalResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option)> { + ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option)> { let (mplace, size) = match place.place { Place::Local { frame, local } => { match self.stack[frame].locals[local].access_mut()? { @@ -956,7 +965,7 @@ pub fn force_allocation_maybe_sized( pub fn force_allocation( &mut self, place: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { Ok(self.force_allocation_maybe_sized(place, None)?.0) } @@ -973,7 +982,7 @@ pub fn write_discriminant_index( &mut self, variant_index: VariantIdx, dest: PlaceTy<'tcx, M::PointerTag>, - ) -> EvalResult<'tcx> { + ) -> InterpResult<'tcx> { match dest.layout.variants { layout::Variants::Single { index } => { assert_eq!(index, variant_index); @@ -1029,20 +1038,18 @@ pub fn write_discriminant_index( pub fn raw_const_to_mplace( &self, raw: RawConst<'tcx>, - ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { // This must be an allocation in `tcx` assert!(self.tcx.alloc_map.lock().get(raw.alloc_id).is_some()); + let ptr = self.tag_static_base_pointer(Pointer::from(raw.alloc_id)); let layout = self.layout_of(raw.ty)?; - Ok(MPlaceTy::from_aligned_ptr( - Pointer::new(raw.alloc_id, Size::ZERO).with_default_tag(), - layout, - )) + Ok(MPlaceTy::from_aligned_ptr(ptr, layout)) } /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. /// Also return some more information so drop doesn't have to run the same code twice. pub(super) fn unpack_dyn_trait(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) - -> EvalResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { + -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> { let vtable = mplace.vtable()?; // also sanity checks the type let (instance, ty) = self.read_drop_type_from_vtable(vtable)?; let layout = self.layout_of(ty)?;