From 36b97cd76c478d56820e1e18647841e1d144d852 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 15 Nov 2018 14:25:23 +0100 Subject: [PATCH] Factor out common top-level code from escape-to-raw and retag --- src/stacked_borrows.rs | 81 ++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs index 44fa605fd6a2..b088a6e845f5 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows.rs @@ -303,6 +303,9 @@ impl<'tcx> Stacks { trace!("{} access of tag {:?}: {:?}, size {}", if is_write { "read" } else { "write" }, ptr.tag, ptr, size.bytes()); + // Even reads can have a side-effect, by invalidating other references. + // This is fundamentally necessary since `&mut` asserts that there + // are no accesses through other references, not even reads. let mut stacks = self.stacks.borrow_mut(); for stack in stacks.iter_mut(ptr.offset, size) { stack.access(ptr.tag, is_write)?; @@ -311,6 +314,7 @@ impl<'tcx> Stacks { } /// Reborrow the given pointer to the new tag for the given kind of reference. + /// This works on `&self` because we might encounter references to constant memory. fn reborrow( &self, ptr: Pointer, @@ -414,8 +418,16 @@ pub trait EvalContextExt<'tcx> { kind: MemoryKind, ) -> Borrow; - /// Retag an indidual pointer, returning the retagged version. + /// Reborrow the given place, returning the newly tagged ptr to it. fn reborrow( + &mut self, + place: MPlaceTy<'tcx, Borrow>, + size: Size, + new_bor: Borrow + ) -> EvalResult<'tcx, Pointer>; + + /// Retag an indidual pointer, returning the retagged version. + fn retag_reference( &mut self, ptr: ImmTy<'tcx, Borrow>, mutbl: Mutability, @@ -536,22 +548,46 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> { } /// The given place may henceforth be accessed through raw pointers. + #[inline(always)] fn escape_to_raw( &mut self, place: MPlaceTy<'tcx, Borrow>, size: Size, ) -> EvalResult<'tcx> { - trace!("escape_to_raw: {:?} is now accessible by raw pointers", *place); - // Get the allocation - let ptr = place.ptr.to_ptr()?; - self.memory().check_bounds(ptr, size, false)?; // `ptr_dereference` wouldn't do any checks if this is a raw ptr - let alloc = self.memory().get(ptr.alloc_id).expect("We checked that the ptr is fine!"); - // Re-borrow to raw. This is a NOP for shared borrows, but we do not know the borrow - // type here and that's also okay. Freezing does not matter here. - alloc.extra.reborrow(ptr, size, Borrow::default(), RefKind::Raw) + self.reborrow(place, size, Borrow::default())?; + Ok(()) } fn reborrow( + &mut self, + place: MPlaceTy<'tcx, Borrow>, + size: Size, + new_bor: Borrow + ) -> EvalResult<'tcx, Pointer> { + let ptr = place.ptr.to_ptr()?; + let new_ptr = Pointer::new_with_tag(ptr.alloc_id, ptr.offset, new_bor); + trace!("reborrow: Creating new reference for {:?} (pointee {}): {:?}", + ptr, place.layout.ty, new_bor); + + // Get the allocation. It might not be mutable, so we cannot use `get_mut`. + self.memory().check_bounds(ptr, size, false)?; + let alloc = self.memory().get(ptr.alloc_id).expect("We checked that the ptr is fine!"); + // Update the stacks. + if let Borrow::Shr(Some(_)) = new_bor { + // Reference that cares about freezing. We need a frozen-sensitive reborrow. + self.visit_freeze_sensitive(place, size, |cur_ptr, size, frozen| { + let kind = if frozen { RefKind::Frozen } else { RefKind::Raw }; + alloc.extra.reborrow(cur_ptr, size, new_bor, kind) + })?; + } else { + // Just treat this as one big chunk. + let kind = if new_bor.is_unique() { RefKind::Unique } else { RefKind::Raw }; + alloc.extra.reborrow(ptr, size, new_bor, kind)?; + } + Ok(new_ptr) + } + + fn retag_reference( &mut self, val: ImmTy<'tcx, Borrow>, mutbl: Mutability, @@ -566,33 +602,17 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> { return Ok(*val); } - // Prepare to re-borrow this place. - let ptr = place.ptr.to_ptr()?; + // Compute new borrow. let time = self.machine.stacked_borrows.increment_clock(); let new_bor = match mutbl { MutMutable => Borrow::Uniq(time), MutImmutable => Borrow::Shr(Some(time)), }; - trace!("reborrow: Creating new {:?} reference for {:?} (pointee {}): {:?}", - mutbl, ptr, place.layout.ty, new_bor); - // Get the allocation. It might not be mutable, so we cannot use `get_mut`. - self.memory().check_bounds(ptr, size, false)?; - let alloc = self.memory().get(ptr.alloc_id).expect("We checked that the ptr is fine!"); - // Update the stacks. - if mutbl == MutImmutable { - // Shared reference. We need a frozen-sensitive reborrow. - self.visit_freeze_sensitive(place, size, |cur_ptr, size, frozen| { - let kind = if frozen { RefKind::Frozen } else { RefKind::Raw }; - alloc.extra.reborrow(cur_ptr, size, new_bor, kind) - })?; - } else { - // Mutable reference. Just treat this as one big chunk. - alloc.extra.reborrow(ptr, size, new_bor, RefKind::Unique)?; - } + // Reborrow. + let new_ptr = self.reborrow(place, size, new_bor)?; // Return new ptr - let new_ptr = Pointer::new_with_tag(ptr.alloc_id, ptr.offset, new_bor); let new_place = MemPlace { ptr: Scalar::Ptr(new_ptr), ..*place }; Ok(new_place.to_ref()) } @@ -611,8 +631,9 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> { ty::Ref(_, _, mutbl) => { // fast path let val = self.read_immediate(self.place_to_op(place)?)?; - let val = self.reborrow(val, mutbl)?; + let val = self.retag_reference(val, mutbl)?; self.write_immediate(val, place)?; + return Ok(()); } _ => {}, // handled with the general case below }; @@ -643,7 +664,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> { match place.layout.ty.sty { ty::Ref(_, _, mutbl) => { let val = self.ecx.read_immediate(place.into())?; - let val = self.ecx.reborrow(val, mutbl)?; + let val = self.ecx.retag_reference(val, mutbl)?; self.ecx.write_immediate(val, place.into())?; } _ => {}, // nothing to do