Skip to content

Commit 1de7d2f

Browse files
committed
support for basic (non-overlapping) 2-phase borrows
1 parent 6ca4fc7 commit 1de7d2f

File tree

3 files changed

+93
-12
lines changed

3 files changed

+93
-12
lines changed

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
525525
fn retag(
526526
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
527527
fn_entry: bool,
528+
two_phase: bool,
528529
place: PlaceTy<'tcx, Borrow>,
529530
) -> EvalResult<'tcx> {
530531
if !ecx.tcx.sess.opts.debugging_opts.mir_emit_retag || !Self::enforce_validity(ecx) {
@@ -535,7 +536,7 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
535536
// uninitialized data.
536537
Ok(())
537538
} else {
538-
ecx.retag(fn_entry, place)
539+
ecx.retag(fn_entry, two_phase, place)
539540
}
540541
}
541542

src/stacked_borrows.rs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc::hir::{Mutability, MutMutable, MutImmutable};
88
use crate::{
99
EvalResult, EvalErrorKind, MiriEvalContext, HelpersEvalContextExt, Evaluator, MutValueVisitor,
1010
MemoryKind, MiriMemoryKind, RangeMap, AllocId, Allocation, AllocationExtra,
11-
Pointer, MemPlace, Scalar, Immediate, ImmTy, PlaceTy, MPlaceTy,
11+
Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
1212
};
1313

1414
pub type Timestamp = u64;
@@ -534,19 +534,21 @@ pub trait EvalContextExt<'tcx> {
534534
size: Size,
535535
fn_barrier: bool,
536536
new_bor: Borrow
537-
) -> EvalResult<'tcx, Pointer<Borrow>>;
537+
) -> EvalResult<'tcx>;
538538

539539
/// Retag an indidual pointer, returning the retagged version.
540540
fn retag_reference(
541541
&mut self,
542542
ptr: ImmTy<'tcx, Borrow>,
543543
mutbl: Mutability,
544544
fn_barrier: bool,
545+
two_phase: bool,
545546
) -> EvalResult<'tcx, Immediate<Borrow>>;
546547

547548
fn retag(
548549
&mut self,
549550
fn_entry: bool,
551+
two_phase: bool,
550552
place: PlaceTy<'tcx, Borrow>
551553
) -> EvalResult<'tcx>;
552554

@@ -644,9 +646,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
644646
size: Size,
645647
fn_barrier: bool,
646648
new_bor: Borrow
647-
) -> EvalResult<'tcx, Pointer<Borrow>> {
649+
) -> EvalResult<'tcx> {
648650
let ptr = place.ptr.to_ptr()?;
649-
let new_ptr = Pointer::new_with_tag(ptr.alloc_id, ptr.offset, new_bor);
650651
let barrier = if fn_barrier { Some(self.frame().extra) } else { None };
651652
trace!("reborrow: Creating new reference for {:?} (pointee {}): {:?}",
652653
ptr, place.layout.ty, new_bor);
@@ -666,14 +667,15 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
666667
let kind = if new_bor.is_unique() { RefKind::Unique } else { RefKind::Raw };
667668
alloc.extra.reborrow(ptr, size, barrier, new_bor, kind)?;
668669
}
669-
Ok(new_ptr)
670+
Ok(())
670671
}
671672

672673
fn retag_reference(
673674
&mut self,
674675
val: ImmTy<'tcx, Borrow>,
675676
mutbl: Mutability,
676677
fn_barrier: bool,
678+
two_phase: bool,
677679
) -> EvalResult<'tcx, Immediate<Borrow>> {
678680
// We want a place for where the ptr *points to*, so we get one.
679681
let place = self.ref_to_mplace(val)?;
@@ -693,16 +695,24 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
693695
};
694696

695697
// Reborrow.
696-
let new_ptr = self.reborrow(place, size, fn_barrier, new_bor)?;
698+
self.reborrow(place, size, fn_barrier, new_bor)?;
699+
let new_place = place.with_tag(new_bor);
700+
// Handle two-phase borrows.
701+
if two_phase {
702+
// We immediately share it, to allow read accesses
703+
let two_phase_time = self.machine.stacked_borrows.increment_clock();
704+
let two_phase_bor = Borrow::Shr(Some(two_phase_time));
705+
self.reborrow(new_place, size, /*fn_barrier*/false, two_phase_bor)?;
706+
}
697707

698-
// Return new ptr
699-
let new_place = MemPlace { ptr: Scalar::Ptr(new_ptr), ..*place };
708+
// Return new ptr.
700709
Ok(new_place.to_ref())
701710
}
702711

703712
fn retag(
704713
&mut self,
705714
fn_entry: bool,
715+
two_phase: bool,
706716
place: PlaceTy<'tcx, Borrow>
707717
) -> EvalResult<'tcx> {
708718
// Determine mutability and whether to add a barrier.
@@ -725,19 +735,20 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
725735
if let Some((mutbl, barrier)) = qualify(place.layout.ty, fn_entry) {
726736
// fast path
727737
let val = self.read_immediate(self.place_to_op(place)?)?;
728-
let val = self.retag_reference(val, mutbl, barrier)?;
738+
let val = self.retag_reference(val, mutbl, barrier, two_phase)?;
729739
self.write_immediate(val, place)?;
730740
return Ok(());
731741
}
732742
let place = self.force_allocation(place)?;
733743

734-
let mut visitor = RetagVisitor { ecx: self, fn_entry };
744+
let mut visitor = RetagVisitor { ecx: self, fn_entry, two_phase };
735745
visitor.visit_value(place)?;
736746

737747
// The actual visitor
738748
struct RetagVisitor<'ecx, 'a, 'mir, 'tcx> {
739749
ecx: &'ecx mut MiriEvalContext<'a, 'mir, 'tcx>,
740750
fn_entry: bool,
751+
two_phase: bool,
741752
}
742753
impl<'ecx, 'a, 'mir, 'tcx>
743754
MutValueVisitor<'a, 'mir, 'tcx, Evaluator<'tcx>>
@@ -758,7 +769,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
758769
// making it useless.
759770
if let Some((mutbl, barrier)) = qualify(place.layout.ty, self.fn_entry) {
760771
let val = self.ecx.read_immediate(place.into())?;
761-
let val = self.ecx.retag_reference(val, mutbl, barrier)?;
772+
let val = self.ecx.retag_reference(val, mutbl, barrier, self.two_phase)?;
762773
self.ecx.write_immediate(val, place.into())?;
763774
}
764775
Ok(())

tests/run-pass/2phase.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#![feature(nll)]
2+
3+
trait S: Sized {
4+
fn tpb(&mut self, _s: Self) {}
5+
}
6+
7+
impl S for i32 {}
8+
9+
fn two_phase1() {
10+
let mut x = 3;
11+
x.tpb(x);
12+
}
13+
14+
fn two_phase2() {
15+
let mut v = vec![];
16+
v.push(v.len());
17+
}
18+
19+
/*
20+
fn two_phase_overlapping1() {
21+
let mut x = vec![];
22+
let p = &x;
23+
x.push(p.len());
24+
}
25+
26+
fn two_phase_overlapping2() {
27+
use std::ops::AddAssign;
28+
let mut x = 1;
29+
let l = &x;
30+
x.add_assign(x + *l);
31+
}
32+
*/
33+
34+
fn match_two_phase() {
35+
let mut x = 3;
36+
match x {
37+
ref mut y if { let _val = x; let _val = *y; true } => {},
38+
_ => (),
39+
}
40+
}
41+
42+
fn with_interior_mutability() {
43+
use std::cell::Cell;
44+
45+
trait Thing: Sized {
46+
fn do_the_thing(&mut self, _s: i32) {}
47+
}
48+
49+
impl<T> Thing for Cell<T> {}
50+
51+
let mut x = Cell::new(1);
52+
let l = &x;
53+
x
54+
.do_the_thing({
55+
x.set(3);
56+
l.set(4);
57+
x.get() + l.get()
58+
})
59+
;
60+
}
61+
62+
fn main() {
63+
two_phase1();
64+
two_phase2();
65+
//two_phase_overlapping1();
66+
//two_phase_overlapping2();
67+
match_two_phase();
68+
with_interior_mutability();
69+
}

0 commit comments

Comments
 (0)