use dataflow::{EverInitializedLvals, MovingOutStatements};
use dataflow::{Borrows, BorrowData, ReserveOrActivateIndex};
use dataflow::{ActiveBorrows, Reservations};
+use dataflow::indexes::{BorrowIndex};
use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex};
use util::borrowck_errors::{BorrowckErrors, Origin};
},
storage_dead_or_drop_error_reported_l: FxHashSet(),
storage_dead_or_drop_error_reported_s: FxHashSet(),
+ reservation_error_reported: FxHashSet(),
};
let borrows = Borrows::new(tcx, mir, opt_regioncx, def_id, body_id);
storage_dead_or_drop_error_reported_l: FxHashSet<Local>,
/// Same as the above, but for statics (thread-locals)
storage_dead_or_drop_error_reported_s: FxHashSet<DefId>,
+ /// This field keeps track of when borrow conflict errors are reported
+ /// for reservations, so that we don't report seemingly duplicate
+ /// errors for corresponding activations
+ ///
+ /// FIXME: Ideally this would be a set of BorrowIndex, not Places,
+ /// but it is currently inconvenient to track down the BorrowIndex
+ /// at the time we detect and report a reservation error.
+ reservation_error_reported: FxHashSet<Place<'tcx>>,
}
// Check that:
flow_state
);
let span = stmt.source_info.span;
+
+ self.check_activations(location, span, flow_state);
+
match stmt.kind {
StatementKind::Assign(ref lhs, ref rhs) => {
// NOTE: NLL RFC calls for *shallow* write; using Deep
flow_state
);
let span = term.source_info.span;
+
+ self.check_activations(location, span, flow_state);
+
match term.kind {
TerminatorKind::SwitchInt {
ref discr,
}
use self::ShallowOrDeep::{Deep, Shallow};
-use self::ReadOrWrite::{Read, Write};
+use self::ReadOrWrite::{Activation, Read, Reservation, Write};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ArtificialField {
/// new values or otherwise invalidated (for example, it could be
/// de-initialized, as in a move operation).
Write(WriteKind),
+
+ /// For two-phase borrows, we distinguish a reservation (which is treated
+ /// like a Read) from an activation (which is treated like a write), and
+ /// each of those is furthermore distinguished from Reads/Writes above.
+ Reservation(WriteKind),
+ Activation(WriteKind, BorrowIndex),
}
/// Kind of read access to a value
) -> AccessErrorsReported {
let (sd, rw) = kind;
+ if let Activation(_, borrow_index) = rw {
+ if self.reservation_error_reported.contains(&place_span.0) {
+ debug!("skipping access_place for activation of invalid reservation \
+ place: {:?} borrow_index: {:?}", place_span.0, borrow_index);
+ return AccessErrorsReported { mutability_error: false, conflict_error: false };
+ }
+ }
+
let mutability_error =
self.check_access_permissions(place_span, rw, is_local_mutation_allowed);
let conflict_error =
(sd, place_span.0),
flow_state,
|this, index, borrow| match (rw, borrow.kind) {
- (Read(_), BorrowKind::Shared) => Control::Continue,
+ // Obviously an activation is compatible with its own reservation;
+ // so don't check if they interfere.
+ (Activation(_, activating), _) if index.is_reservation() &&
+ activating == index.borrow_index() => Control::Continue,
- (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => {
+ (Read(_), BorrowKind::Shared) |
+ (Reservation(..), BorrowKind::Shared) => Control::Continue,
+
+ (Read(kind), BorrowKind::Unique) |
+ (Read(kind), BorrowKind::Mut) => {
// Reading from mere reservations of mutable-borrows is OK.
if this.tcx.sess.opts.debugging_opts.two_phase_borrows &&
index.is_reservation()
}
Control::Break
}
+
+ (Reservation(kind), BorrowKind::Unique) |
+ (Reservation(kind), BorrowKind::Mut) |
+ (Activation(kind, _), _) |
(Write(kind), _) => {
+
+ match rw {
+ Reservation(_) => {
+ debug!("recording invalid reservation of \
+ place: {:?}", place_span.0);
+ this.reservation_error_reported.insert(place_span.0.clone());
+ }
+ Activation(_, activating) => {
+ debug!("observing check_place for activation of \
+ borrow_index: {:?}", activating);
+ }
+ Read(..) | Write(..) => {}
+ }
+
match kind {
WriteKind::MutableBorrow(bk) => {
let end_issued_loan_span = flow_state
.borrows
.operator()
.opt_region_end_span(&borrow.region);
+
error_reported = true;
this.report_conflicting_borrow(
context,
let access_kind = match bk {
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
BorrowKind::Unique | BorrowKind::Mut => {
- (Deep, Write(WriteKind::MutableBorrow(bk)))
+ let wk = WriteKind::MutableBorrow(bk);
+ if self.tcx.sess.opts.debugging_opts.two_phase_borrows {
+ (Deep, Reservation(wk))
+ } else {
+ (Deep, Write(wk))
+ }
}
};
+
self.access_place(
context,
(place, span),
LocalMutationIsAllowed::No,
flow_state,
);
+
self.check_if_path_is_moved(
context,
InitializationRequiringAction::Borrow,
)
}
}
+
+ fn check_activations(&mut self,
+ location: Location,
+ span: Span,
+ flow_state: &Flows<'cx, 'gcx, 'tcx>)
+ {
+ if !self.tcx.sess.opts.debugging_opts.two_phase_borrows {
+ return;
+ }
+
+ // Two-phase borrow support: For each activation that is newly
+ // generated at this statement, check if it interferes with
+ // another borrow.
+ let domain = flow_state.borrows.operator();
+ let data = domain.borrows();
+ flow_state.borrows.each_gen_bit(|gen| {
+ if gen.is_activation() && // must be activation,
+ !flow_state.borrows.contains(&gen) // and newly generated.
+ {
+ let borrow_index = gen.borrow_index();
+ let borrow = &data[borrow_index];
+ // currently the flow analysis registers
+ // activations for both mutable and immutable
+ // borrows. So make sure we are talking about a
+ // mutable borrow before we check it.
+ match borrow.kind {
+ BorrowKind::Shared => return,
+ BorrowKind::Unique |
+ BorrowKind::Mut => {}
+ }
+
+ self.access_place(ContextKind::Activation.new(location),
+ (&borrow.borrowed_place, span),
+ (Deep, Activation(WriteKind::MutableBorrow(borrow.kind),
+ borrow_index)),
+ LocalMutationIsAllowed::No,
+ flow_state);
+ // We do not need to call `check_if_path_is_moved`
+ // again, as we already called it when we made the
+ // initial reservation.
+ }
+ });
+ }
}
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
);
let mut error_reported = false;
match kind {
+ Reservation(WriteKind::MutableBorrow(BorrowKind::Unique)) |
Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
if let Err(_place_err) = self.is_mutable(place, LocalMutationIsAllowed::Yes) {
span_bug!(span, "&unique borrow for {:?} should not fail", place);
}
}
+ Reservation(WriteKind::MutableBorrow(BorrowKind::Mut)) |
Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) =
self.is_mutable(place, is_local_mutation_allowed)
{
err.emit();
},
+ Reservation(WriteKind::Mutate) |
Write(WriteKind::Mutate) => {
if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) {
error_reported = true;
err.emit();
}
}
+ Reservation(WriteKind::Move) |
+ Reservation(WriteKind::StorageDeadOrDrop) |
+ Reservation(WriteKind::MutableBorrow(BorrowKind::Shared)) |
Write(WriteKind::Move) |
Write(WriteKind::StorageDeadOrDrop) |
Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => {
);
}
}
+
+ Activation(..) => {} // permission checks are done at Reservation point.
+
Read(ReadKind::Borrow(BorrowKind::Unique)) |
Read(ReadKind::Borrow(BorrowKind::Mut)) |
Read(ReadKind::Borrow(BorrowKind::Shared)) |
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum ContextKind {
+ Activation,
AssignLhs,
AssignRhs,
SetDiscrim,