Skip to content

Commit

Permalink
make reborrow shallow, and fix tests for that
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Aug 2, 2019
1 parent 5bbf673 commit c4c2716
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 54 deletions.
46 changes: 2 additions & 44 deletions src/stacked_borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc::hir::{MutMutable, MutImmutable};
use rustc::mir::RetagKind;

use crate::{
InterpResult, InterpError, MiriEvalContext, HelpersEvalContextExt, Evaluator, MutValueVisitor,
InterpResult, InterpError, HelpersEvalContextExt,
MemoryKind, MiriMemoryKind, RangeMap, AllocId, Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
};

Expand Down Expand Up @@ -632,54 +632,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
}
}

// We need a visitor to visit all references. However, that requires
// a `MemPlace`, so we have a fast path for reference types that
// avoids allocating.
// We only reborrow "bare" references/boxes.
if let Some((mutbl, protector)) = qualify(place.layout.ty, kind) {
// Fast path.
let val = this.read_immediate(this.place_to_op(place)?)?;
let val = this.retag_reference(val, mutbl, protector)?;
this.write_immediate(val, place)?;
return Ok(());
}
let place = this.force_allocation(place)?;

let mut visitor = RetagVisitor { ecx: this, kind };
visitor.visit_value(place)?;

// The actual visitor.
struct RetagVisitor<'ecx, 'mir, 'tcx> {
ecx: &'ecx mut MiriEvalContext<'mir, 'tcx>,
kind: RetagKind,
}
impl<'ecx, 'mir, 'tcx>
MutValueVisitor<'mir, 'tcx, Evaluator<'tcx>>
for
RetagVisitor<'ecx, 'mir, 'tcx>
{
type V = MPlaceTy<'tcx, Tag>;

#[inline(always)]
fn ecx(&mut self) -> &mut MiriEvalContext<'mir, 'tcx> {
&mut self.ecx
}

// Primitives of reference type, that is the one thing we are interested in.
fn visit_primitive(&mut self, place: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>
{
// Cannot use `builtin_deref` because that reports *immutable* for `Box`,
// making it useless.
if let Some((mutbl, protector)) = qualify(place.layout.ty, self.kind) {
let val = self.ecx.read_immediate(place.into())?;
let val = self.ecx.retag_reference(
val,
mutbl,
protector
)?;
self.ecx.write_immediate(val, place.into())?;
}
Ok(())
}
}

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion tests/compile-fail/stacked_borrows/buggy_split_at_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ mod safe {
assert!(mid <= len);

(from_raw_parts_mut(ptr, len - mid), // BUG: should be "mid" instead of "len - mid"
//~^ ERROR borrow stack
from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
}
}
Expand All @@ -18,6 +17,7 @@ mod safe {
fn main() {
let mut array = [1,2,3,4];
let (a, b) = safe::split_at_mut(&mut array, 0);
//~^ ERROR borrow stack
a[1] = 5;
b[1] = 6;
}
11 changes: 8 additions & 3 deletions tests/compile-fail/stacked_borrows/return_invalid_mut_option.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// Make sure that we cannot return a `&mut` that got already invalidated, not even in an `Option`.
// Due to shallow reborrowing, the error only surfaces when we look into the `Option`.
fn foo(x: &mut (i32, i32)) -> Option<&mut i32> {
let xraw = x as *mut (i32, i32);
let ret = Some(unsafe { &mut (*xraw).1 });
let ret = unsafe { &mut (*xraw).1 }; // let-bind to avoid 2phase
let ret = Some(ret);
let _val = unsafe { *xraw }; // invalidate xref
ret //~ ERROR borrow stack
ret
}

fn main() {
foo(&mut (1, 2));
match foo(&mut (1, 2)) {
Some(_x) => {}, //~ ERROR borrow stack
None => {},
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Make sure that we cannot return a `&mut` that got already invalidated, not even in a tuple.
// Due to shallow reborrowing, the error only surfaces when we look into the tuple.
fn foo(x: &mut (i32, i32)) -> (&mut i32,) {
let xraw = x as *mut (i32, i32);
let ret = (unsafe { &mut (*xraw).1 },);
let _val = unsafe { *xraw }; // invalidate xref
ret //~ ERROR borrow stack
ret
}

fn main() {
foo(&mut (1, 2));
foo(&mut (1, 2)).0; //~ ERROR: borrow stack
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// Make sure that we cannot return a `&` that got already invalidated, not even in an `Option`.
// Due to shallow reborrowing, the error only surfaces when we look into the `Option`.
fn foo(x: &mut (i32, i32)) -> Option<&i32> {
let xraw = x as *mut (i32, i32);
let ret = Some(unsafe { &(*xraw).1 });
unsafe { *xraw = (42, 23) }; // unfreeze
ret //~ ERROR borrow stack
ret
}

fn main() {
foo(&mut (1, 2));
match foo(&mut (1, 2)) {
Some(_x) => {}, //~ ERROR borrow stack
None => {},
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Make sure that we cannot return a `&` that got already invalidated, not even in a tuple.
// Due to shallow reborrowing, the error only surfaces when we look into the tuple.
fn foo(x: &mut (i32, i32)) -> (&i32,) {
let xraw = x as *mut (i32, i32);
let ret = (unsafe { &(*xraw).1 },);
unsafe { *xraw = (42, 23) }; // unfreeze
ret //~ ERROR borrow stack
ret
}

fn main() {
foo(&mut (1, 2));
foo(&mut (1, 2)).0; //~ ERROR borrow stack
}

0 comments on commit c4c2716

Please sign in to comment.