Skip to content

Commit e1f82aa

Browse files
committed
determine whether a borrow is active based solely on the location
1 parent f93d5d3 commit e1f82aa

File tree

3 files changed

+111
-8
lines changed

3 files changed

+111
-8
lines changed

src/librustc/mir/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1991,7 +1991,7 @@ impl Location {
19911991
Location { block: self.block, statement_index: self.statement_index + 1 }
19921992
}
19931993

1994-
pub fn dominates(&self, other: &Location, dominators: &Dominators<BasicBlock>) -> bool {
1994+
pub fn dominates(&self, other: Location, dominators: &Dominators<BasicBlock>) -> bool {
19951995
if self.block == other.block {
19961996
self.statement_index <= other.statement_index
19971997
} else {

src/librustc_mir/borrow_check/mod.rs

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
2222
use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
2323
use rustc::mir::ClosureRegionRequirements;
2424

25+
use rustc_data_structures::control_flow_graph::dominators::Dominators;
2526
use rustc_data_structures::fx::FxHashSet;
2627
use rustc_data_structures::indexed_set::IdxSetBuf;
2728
use rustc_data_structures::indexed_vec::Idx;
@@ -66,8 +67,6 @@ pub fn provide(providers: &mut Providers) {
6667
};
6768
}
6869

69-
struct IsActive(bool);
70-
7170
fn mir_borrowck<'a, 'tcx>(
7271
tcx: TyCtxt<'a, 'tcx, 'tcx>,
7372
def_id: DefId,
@@ -234,6 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
234233
_ => false,
235234
};
236235

236+
let dominators = mir.dominators();
237+
237238
let mut mbcx = MirBorrowckCtxt {
238239
tcx: tcx,
239240
mir: mir,
@@ -250,6 +251,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
250251
moved_error_reported: FxHashSet(),
251252
nonlexical_regioncx: opt_regioncx,
252253
nonlexical_cause_info: None,
254+
dominators,
253255
};
254256

255257
let mut state = Flows::new(
@@ -302,6 +304,7 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
302304
/// find out which CFG points are contained in each borrow region.
303305
nonlexical_regioncx: Option<Rc<RegionInferenceContext<'tcx>>>,
304306
nonlexical_cause_info: Option<RegionCausalInfo>,
307+
dominators: Dominators<BasicBlock>,
305308
}
306309

307310
// Check that:
@@ -856,7 +859,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
856859
context,
857860
(sd, place_span.0),
858861
flow_state,
859-
|this, borrow_index, is_active, borrow| match (rw, borrow.kind) {
862+
|this, borrow_index, borrow| match (rw, borrow.kind) {
860863
// Obviously an activation is compatible with its own
861864
// reservation (or even prior activating uses of same
862865
// borrow); so don't check if they interfere.
@@ -881,7 +884,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
881884

882885
(Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => {
883886
// Reading from mere reservations of mutable-borrows is OK.
884-
if this.allow_two_phase_borrow(borrow.kind) && !is_active.0 {
887+
if !this.is_active(borrow, context.loc) {
888+
assert!(this.allow_two_phase_borrow(borrow.kind));
885889
return Control::Continue;
886890
}
887891

@@ -2234,7 +2238,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
22342238
flow_state: &Flows<'cx, 'gcx, 'tcx>,
22352239
mut op: F,
22362240
) where
2237-
F: FnMut(&mut Self, BorrowIndex, IsActive, &BorrowData<'tcx>) -> Control,
2241+
F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>) -> Control,
22382242
{
22392243
let (access, place) = access_place;
22402244

@@ -2247,21 +2251,86 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
22472251
// borrows of P, P.a.b, etc.
22482252
let mut iter_incoming = flow_state.borrows.iter_incoming();
22492253
while let Some(i) = iter_incoming.next() {
2254+
// TODO -- for now, just skip activations, since
2255+
// everywhere that activation is set, reservation should
2256+
// be set
2257+
if i.is_activation() {
2258+
continue;
2259+
}
2260+
22502261
let borrowed = &data[i.borrow_index()];
22512262

22522263
if self.places_conflict(&borrowed.borrowed_place, place, access) {
22532264
debug!(
22542265
"each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
22552266
i, borrowed, place, access
22562267
);
2257-
let is_active = IsActive(i.is_activation());
2258-
let ctrl = op(self, i.borrow_index(), is_active, borrowed);
2268+
let ctrl = op(self, i.borrow_index(), borrowed);
22592269
if ctrl == Control::Break {
22602270
return;
22612271
}
22622272
}
22632273
}
22642274
}
2275+
2276+
fn is_active(
2277+
&self,
2278+
borrow_data: &BorrowData<'tcx>,
2279+
location: Location
2280+
) -> bool {
2281+
debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
2282+
2283+
// If this is not a 2-phase borrow, it is always active.
2284+
let activation_location = match borrow_data.activation_location {
2285+
Some(v) => v,
2286+
None => return true,
2287+
};
2288+
2289+
// Otherwise, it is active for every location *except* in between
2290+
// the reservation and the activation:
2291+
//
2292+
// X
2293+
// /
2294+
// R <--+ Except for this
2295+
// / \ | diamond
2296+
// \ / |
2297+
// A <------+
2298+
// |
2299+
// Z
2300+
//
2301+
// Note that we assume that:
2302+
// - the reservation R dominates the activation A
2303+
// - the activation A post-dominates the reservation R (ignoring unwinding edges).
2304+
//
2305+
// This means that there can't be an edge that leaves A and
2306+
// comes back into that diamond unless it passes through R.
2307+
//
2308+
// Suboptimal: In some cases, this code walks the dominator
2309+
// tree twice when it only has to be walked once. I am
2310+
// lazy. -nmatsakis
2311+
2312+
// If dominated by the activation A, then it is active. The
2313+
// activation occurs upon entering the point A, so this is
2314+
// also true if location == activation_location.
2315+
if activation_location.dominates(location, &self.dominators) {
2316+
return true;
2317+
}
2318+
2319+
// The reservation starts *on exiting* the reservation block,
2320+
// so check if the location is dominated by R.successor. If so,
2321+
// this point falls in between the reservation and location.
2322+
let reserve_location = borrow_data.reserve_location.successor_within_block();
2323+
if reserve_location.dominates(location, &self.dominators) {
2324+
false
2325+
} else {
2326+
// Otherwise, this point is outside the diamond, so
2327+
// consider the borrow active. This could happen for
2328+
// example if the borrow remains active around a loop (in
2329+
// which case it would be active also for the point R,
2330+
// which would generate an error).
2331+
true
2332+
}
2333+
}
22652334
}
22662335

22672336
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that a borrow which starts as a 2-phase borrow and gets
12+
// carried around a loop winds up conflicting with itself.
13+
14+
#![feature(nll)]
15+
16+
struct Foo { x: String }
17+
18+
impl Foo {
19+
fn get_string(&mut self) -> &str {
20+
&self.x
21+
}
22+
}
23+
24+
fn main() {
25+
let mut foo = Foo { x: format!("Hello, world") };
26+
let mut strings = vec![];
27+
28+
loop {
29+
strings.push(foo.get_string()); //~ ERROR cannot borrow `foo` as mutable
30+
if strings.len() > 2 { break; }
31+
}
32+
33+
println!("{:?}", strings);
34+
}

0 commit comments

Comments
 (0)