Skip to content

Commit 3bc5073

Browse files
committed
Expand two-phase-borrows so that a case like this still compiles:
```rust fn main() { fn reuse<X>(_: &mut X) {} let mut t = 2f64; match t { ref mut _b if { false } => { reuse(_b); } _ => {} } } ``` Note: The way this is currently written is confusing; when `autoref` is off, then the arm body bindings (introduced by `bind_matched_candidate_for_arm_body`) are *also* used for the guard. (Any attempt to fix this needs to still ensure that the bindings used by the guard are introduced before the guard is evaluated.) (Once we turn NLL on by default, we can presumably simplify all of that.)
1 parent 638acd3 commit 3bc5073

File tree

5 files changed

+149
-113
lines changed

5 files changed

+149
-113
lines changed

src/librustc_mir/borrow_check/borrow_set.rs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,25 @@ impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
5353
}
5454
}
5555

56+
/// Every two-phase borrow has *exactly one* use (or else it is not a
57+
/// proper two-phase borrow under our current definition. However, not
58+
/// all uses are actually ones that activate the reservation.. In
59+
/// particular, a shared borrow of a `&mut` does not activate the
60+
/// reservation.
61+
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
62+
crate enum TwoPhaseUse {
63+
MutActivate,
64+
SharedUse,
65+
}
66+
5667
#[derive(Debug)]
5768
crate struct BorrowData<'tcx> {
5869
/// Location where the borrow reservation starts.
5970
/// In many cases, this will be equal to the activation location but not always.
6071
crate reserve_location: Location,
6172
/// Location where the borrow is activated. None if this is not a
6273
/// 2-phase borrow.
63-
crate activation_location: Option<Location>,
74+
crate activation_location: Option<(TwoPhaseUse, Location)>,
6475
/// What kind of borrow this is
6576
crate kind: mir::BorrowKind,
6677
/// The region for which this borrow is live
@@ -215,17 +226,16 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
215226
Some(&borrow_index) => {
216227
let borrow_data = &mut self.idx_vec[borrow_index];
217228

218-
// Watch out: the use of TMP in the borrow
219-
// itself doesn't count as an
220-
// activation. =)
229+
// Watch out: the use of TMP in the borrow itself
230+
// doesn't count as an activation. =)
221231
if borrow_data.reserve_location == location && context == PlaceContext::Store {
222232
return;
223233
}
224234

225235
if let Some(other_activation) = borrow_data.activation_location {
226236
span_bug!(
227237
self.mir.source_info(location).span,
228-
"found two activations for 2-phase borrow temporary {:?}: \
238+
"found two uses for 2-phase borrow temporary {:?}: \
229239
{:?} and {:?}",
230240
temp,
231241
location,
@@ -235,11 +245,25 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
235245

236246
// Otherwise, this is the unique later use
237247
// that we expect.
238-
borrow_data.activation_location = Some(location);
239-
self.activation_map
240-
.entry(location)
241-
.or_insert(Vec::new())
242-
.push(borrow_index);
248+
249+
let two_phase_use;
250+
251+
match context {
252+
// The use of TMP in a shared borrow does not
253+
// count as an actual activation.
254+
PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. } => {
255+
two_phase_use = TwoPhaseUse::SharedUse;
256+
}
257+
_ => {
258+
two_phase_use = TwoPhaseUse::MutActivate;
259+
self.activation_map
260+
.entry(location)
261+
.or_insert(Vec::new())
262+
.push(borrow_index);
263+
}
264+
}
265+
266+
borrow_data.activation_location = Some((two_phase_use, location));
243267
}
244268

245269
None => {}

src/librustc_mir/borrow_check/path_utils.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/// allowed to be split into separate Reservation and
1313
/// Activation phases.
1414
use borrow_check::ArtificialField;
15-
use borrow_check::borrow_set::{BorrowSet, BorrowData};
15+
use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseUse};
1616
use borrow_check::{Context, Overlap};
1717
use borrow_check::{ShallowOrDeep, Deep, Shallow};
1818
use dataflow::indexes::BorrowIndex;
@@ -431,10 +431,13 @@ pub(super) fn is_active<'tcx>(
431431
) -> bool {
432432
debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
433433

434-
// If this is not a 2-phase borrow, it is always active.
435434
let activation_location = match borrow_data.activation_location {
436-
Some(v) => v,
435+
// If this is not a 2-phase borrow, it is always active.
437436
None => return true,
437+
// And if the unique 2-phase use is not an activation, then it is *never* active.
438+
Some((TwoPhaseUse::SharedUse, _)) => return false,
439+
// Otherwise, we derive info from the activation point `v`:
440+
Some((TwoPhaseUse::MutActivate, v)) => v,
438441
};
439442

440443
// Otherwise, it is active for every location *except* in between

src/librustc_mir/build/expr/as_place.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! See docs in build/expr/mod.rs
1212
1313
use build::{BlockAnd, BlockAndExtension, Builder};
14-
use build::ForGuard::{OutsideGuard, WithinGuard};
14+
use build::ForGuard::{OutsideGuard, RefWithinGuard, ValWithinGuard};
1515
use build::expr::category::Category;
1616
use hair::*;
1717
use rustc::mir::*;
@@ -88,10 +88,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
8888
}
8989
ExprKind::VarRef { id } => {
9090
let place = if this.is_bound_var_in_guard(id) {
91-
let index = this.var_local_id(id, WithinGuard);
9291
if this.hir.tcx().all_pat_vars_are_implicit_refs_within_guards() {
92+
let index = this.var_local_id(id, RefWithinGuard);
9393
Place::Local(index).deref()
9494
} else {
95+
let index = this.var_local_id(id, ValWithinGuard);
9596
Place::Local(index)
9697
}
9798
} else {

0 commit comments

Comments
 (0)