1 use std::cell::RefCell;
3 use rustc::ty::{Ty, layout::Size};
11 pub type Timestamp = u64;
13 /// Information about a potentially mutable borrow
14 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
16 /// A unique, mutable reference
18 /// Any raw pointer, or a shared borrow with interior mutability
24 fn is_raw(self) -> bool {
32 fn is_uniq(self) -> bool {
40 /// Information about any kind of borrow
41 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
43 /// A mutable borrow, a raw pointer, or a shared borrow with interior mutability
45 /// A shared borrow without interior mutability
51 fn is_mut(self) -> bool {
53 Borrow::Mut(_) => true,
59 fn is_uniq(self) -> bool {
61 Borrow::Mut(Mut::Uniq(_)) => true,
67 /// An item in the borrow stack
68 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
69 pub enum BorStackItem {
70 /// Defines which references are permitted to mutate *if* the location is not frozen
72 /// A barrier, tracking the function it belongs to by its index on the call stack
76 impl Default for Borrow {
77 fn default() -> Self {
82 /// Extra global machine state
83 #[derive(Clone, Debug)]
89 pub fn new() -> State {
94 /// Extra per-location state
95 #[derive(Clone, Debug)]
97 borrows: Vec<BorStackItem>, // used as a stack
98 frozen_since: Option<Timestamp>,
101 impl Default for Stack {
102 fn default() -> Self {
110 /// Extra per-allocation state
111 #[derive(Clone, Debug, Default)]
113 stacks: RefCell<RangeMap<Stack>>,
118 fn check(&self, bor: Borrow) -> bool {
120 Borrow::Frz(acc_t) =>
121 // Must be frozen at least as long as the `acc_t` says.
122 self.frozen_since.map_or(false, |loc_t| loc_t <= acc_t),
123 Borrow::Mut(acc_m) =>
124 // Raw pointers are fine with frozen locations. This is important because &Cell is raw!
125 if self.frozen_since.is_some() {
128 self.borrows.last().map_or(false, |&loc_itm| loc_itm == BorStackItem::Mut(acc_m))
133 /// Reactive `bor` for this stack. If `force_mut` is set, we want to aggressively
134 /// unfreeze this location (because we are about to push a `Uniq`).
135 fn reactivate(&mut self, bor: Borrow, force_mut: bool) -> EvalResult<'tcx> {
136 assert!(!force_mut || bor.is_mut()); // if `force_mut` is set, this must be a mutable borrow
137 // Do NOT change anything if `bor` is already active -- in particular, if
138 // it is a `Mut(Raw)` and we are frozen.
139 if !force_mut && self.check(bor) {
143 let acc_m = match bor {
144 Borrow::Frz(_) => return err!(MachineError(format!("Location should be frozen but it is not"))),
145 Borrow::Mut(acc_m) => acc_m,
147 // We definitely have to unfreeze this, even if we use the topmost item.
148 self.frozen_since = None;
149 // Pop until we see the one we are looking for.
150 while let Some(&itm) = self.borrows.last() {
152 BorStackItem::FnBarrier(_) => {
153 return err!(MachineError(format!("Trying to reactivate a borrow that lives behind a barrier")));
155 BorStackItem::Mut(loc_m) => {
156 if loc_m == acc_m { return Ok(()); }
161 // Nothing to be found. Simulate a "virtual raw" element at the bottom of the stack.
165 err!(MachineError(format!("Borrow-to-reactivate does not exist on the stack")))
169 fn initiate(&mut self, bor: Borrow) -> EvalResult<'tcx> {
172 match self.frozen_since {
173 None => self.frozen_since = Some(t),
174 Some(since) => assert!(since <= t),
178 match self.frozen_since {
179 None => self.borrows.push(BorStackItem::Mut(m)),
181 // FIXME: Do we want an exception for raw borrows?
182 return err!(MachineError(format!("Trying to mutate frozen location")))
191 fn increment_clock(&mut self) -> Timestamp {
198 pub trait EvalContextExt<'tcx> {
201 ptr: Pointer<Borrow>,
202 pointee_ty: Ty<'tcx>,
204 borrow_kind: mir::BorrowKind,
205 ) -> EvalResult<'tcx, Borrow>;
209 ptr: Pointer<Borrow>,
211 ) -> EvalResult<'tcx, Borrow>;
214 impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'a, 'mir, 'tcx> {
217 ptr: Pointer<Borrow>,
218 pointee_ty: Ty<'tcx>,
220 borrow_kind: mir::BorrowKind,
221 ) -> EvalResult<'tcx, Borrow> {
222 let old_bor = ptr.tag;
223 let time = self.machine.stacked_borrows.increment_clock();
224 // FIXME This does not do enough checking when only part of the data lacks
225 // interior mutability.
226 let new_bor = match borrow_kind {
227 mir::BorrowKind::Mut { .. } => Borrow::Mut(Mut::Uniq(time)),
229 if self.type_is_freeze(pointee_ty) {
232 Borrow::Mut(Mut::Raw)
235 trace!("tag_reference: Creating new tag for {:?} (pointee {}, size {}): {:?}", ptr, pointee_ty, size.bytes(), new_bor);
237 // Make sure this reference is not dangling or so
238 self.memory.check_bounds(ptr, size, false)?;
240 // Update the stacks. We cannot use `get_mut` becuse this might be immutable
242 let alloc = self.memory.get(ptr.alloc_id).expect("We checked that the ptr is fine!");
243 let mut stacks = alloc.extra.stacks.borrow_mut();
244 for stack in stacks.iter_mut(ptr.offset, size) {
245 if stack.check(new_bor) {
246 // The new borrow is already active! This can happen when creating multiple
247 // shared references from the same mutable reference. Do nothing.
249 // FIXME: The blog post says we should `reset` if this is a local.
250 stack.reactivate(old_bor, /*force_mut*/new_bor.is_uniq())?;
251 stack.initiate(new_bor)?;
260 ptr: Pointer<Borrow>,
262 ) -> EvalResult<'tcx, Borrow> {
263 // If this is a raw ptr, forget about the tag.
264 Ok(if ptr_ty.is_unsafe_ptr() {
265 trace!("tag_dereference: Erasing tag for {:?} ({})", ptr, ptr_ty);
266 Borrow::Mut(Mut::Raw)
268 // FIXME: Do we want to adjust the tag if it does not match the type?