Skip to content

Commit 9c161b8

Browse files
committed
reading does not remove Unique, it just invalidates them
1 parent 8212314 commit 9c161b8

File tree

1 file changed

+26
-17
lines changed

1 file changed

+26
-17
lines changed

src/stacked_borrows.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ pub enum Permission {
4242
SharedReadWrite,
4343
/// Greants shared read-only access.
4444
SharedReadOnly,
45+
/// Grants no access, but separates two groups of SharedReadWrite so they are not
46+
/// all considered mutually compatible.
47+
Disabled,
4548
}
4649

4750
/// An item in the per-location borrow stack.
@@ -199,8 +202,8 @@ impl Default for Tag {
199202
impl Permission {
200203
/// This defines for a given permission, whether it permits the given kind of access.
201204
fn grants(self, access: AccessKind) -> bool {
202-
// All items grant read access, and except for SharedReadOnly they grant write access.
203-
access == AccessKind::Read || self != Permission::SharedReadOnly
205+
// Disabled grants nother. Otherwise, all items grant read access, and except for SharedReadOnly they grant write access.
206+
self != Permission::Disabled && (access == AccessKind::Read || self != Permission::SharedReadOnly)
204207
}
205208
}
206209

@@ -224,12 +227,14 @@ impl<'tcx> Stack {
224227
}
225228

226229
/// Find the first write-incompatible item above the given one --
227-
/// i.e, find the heigh to which the stack will be truncated when writing to `granting`.
230+
/// i.e, find the height to which the stack will be truncated when writing to `granting`.
228231
fn find_first_write_incompaible(&self, granting: usize) -> usize {
229232
let perm = self.borrows[granting].perm;
230233
match perm {
231234
Permission::SharedReadOnly =>
232235
bug!("Cannot use SharedReadOnly for writing"),
236+
Permission::Disabled =>
237+
bug!("Cannot use Disabled for anything"),
233238
Permission::Unique =>
234239
// On a write, everything above us is incompatible.
235240
granting+1,
@@ -250,10 +255,8 @@ impl<'tcx> Stack {
250255
}
251256
}
252257

253-
/// Remove the given item, enforcing barriers.
254-
/// `tag` is just used for the error message.
255-
fn remove(&mut self, idx: usize, tag: Option<Tag>, global: &GlobalState) -> EvalResult<'tcx> {
256-
let item = self.borrows.remove(idx);
258+
/// Check if the given item is protected.
259+
fn check_protector(item: &Item, tag: Option<Tag>, global: &GlobalState) -> EvalResult<'tcx> {
257260
if let Some(call) = item.protector {
258261
if global.is_active(call) {
259262
if let Some(tag) = tag {
@@ -268,7 +271,6 @@ impl<'tcx> Stack {
268271
}
269272
}
270273
}
271-
trace!("access: removing item {}", item);
272274
Ok(())
273275
}
274276

@@ -295,20 +297,26 @@ impl<'tcx> Stack {
295297
// Remove everything above the write-compatible items, like a proper stack. This makes sure read-only and unique
296298
// pointers become invalid on write accesses (ensures F2a, and ensures U2 for write accesses).
297299
let first_incompatible_idx = self.find_first_write_incompaible(granting_idx);
298-
for idx in (first_incompatible_idx..self.borrows.len()).rev() {
299-
self.remove(idx, Some(tag), global)?;
300+
while self.borrows.len() > first_incompatible_idx {
301+
let item = self.borrows.pop().unwrap();
302+
trace!("access: popping item {}", item);
303+
Stack::check_protector(&item, Some(tag), global)?;
300304
}
301305
} else {
302-
// On a read, remove all `Unique` above the granting item. This ensures U2 for read accesses.
303-
// The reason this is not following the stack discipline is that in
304-
// `let raw = &mut *x as *mut _; let _val = *x;`, the second statement
306+
// On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses.
307+
// The reason this is not following the stack discipline (by removing the first Unique and
308+
// everything on top of it) is that in `let raw = &mut *x as *mut _; let _val = *x;`, the second statement
305309
// would pop the `Unique` from the reborrow of the first statement, and subsequently also pop the
306310
// `SharedReadWrite` for `raw`.
307311
// This pattern occurs a lot in the standard library: create a raw pointer, then also create a shared
308312
// reference and use that.
313+
// We *disable* instead of removing `Unique` to avoid "connecting" two neighbouring blocks of SRWs.
309314
for idx in (granting_idx+1 .. self.borrows.len()).rev() {
310-
if self.borrows[idx].perm == Permission::Unique {
311-
self.remove(idx, Some(tag), global)?;
315+
let item = &mut self.borrows[idx];
316+
if item.perm == Permission::Unique {
317+
trace!("access: disabling item {}", item);
318+
Stack::check_protector(item, Some(tag), global)?;
319+
item.perm = Permission::Disabled;
312320
}
313321
}
314322
}
@@ -332,8 +340,9 @@ impl<'tcx> Stack {
332340
)))?;
333341

334342
// Step 2: Remove all items. Also checks for protectors.
335-
for idx in (0..self.borrows.len()).rev() {
336-
self.remove(idx, None, global)?;
343+
while self.borrows.len() > 0 {
344+
let item = self.borrows.pop().unwrap();
345+
Stack::check_protector(&item, None, global)?;
337346
}
338347

339348
Ok(())

0 commit comments

Comments
 (0)