Auto merge of #46268 - arielb1:union-borrow, r=nikomatsakis
MIR borrowck: implement union-and-array-compatible semantics Fixes #44831. Fixes #44834. Fixes #45537. Fixes #45696 (by implementing DerefPure semantics, which is what we want going forward). r? @nikomatsakis
This commit is contained in:
commit
cf30759a84
@ -1084,9 +1084,11 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
|
||||
pub fn map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
|
||||
where F: FnOnce(&mut T) -> &mut U
|
||||
{
|
||||
// FIXME(nll-rfc#40): fix borrow-check
|
||||
let RefMut { value, borrow } = orig;
|
||||
RefMut {
|
||||
value: f(orig.value),
|
||||
borrow: orig.borrow,
|
||||
value: f(value),
|
||||
borrow: borrow,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1776,12 +1776,18 @@ impl<I: Iterator> Iterator for Peekable<I> {
|
||||
|
||||
#[inline]
|
||||
fn nth(&mut self, n: usize) -> Option<I::Item> {
|
||||
match self.peeked.take() {
|
||||
// the .take() below is just to avoid "move into pattern guard"
|
||||
Some(ref mut v) if n == 0 => v.take(),
|
||||
Some(None) => None,
|
||||
Some(Some(_)) => self.iter.nth(n - 1),
|
||||
None => self.iter.nth(n),
|
||||
// FIXME(#6393): merge these when borrow-checking gets better.
|
||||
if n == 0 {
|
||||
match self.peeked.take() {
|
||||
Some(v) => v,
|
||||
None => self.iter.nth(n),
|
||||
}
|
||||
} else {
|
||||
match self.peeked.take() {
|
||||
Some(None) => None,
|
||||
Some(Some(_)) => self.iter.nth(n - 1),
|
||||
None => self.iter.nth(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::map::definitions::DefPathData;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::ty::{self, ParamEnv, TyCtxt};
|
||||
use rustc::ty::maps::Providers;
|
||||
@ -36,6 +37,9 @@ use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
|
||||
use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex};
|
||||
use util::borrowck_errors::{BorrowckErrors, Origin};
|
||||
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
|
||||
use self::MutateMode::{JustWrite, WriteAndRead};
|
||||
|
||||
pub(crate) mod nll;
|
||||
@ -128,6 +132,12 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
move_data: move_data,
|
||||
param_env: param_env,
|
||||
};
|
||||
let body_id = match tcx.def_key(def_id).disambiguated_data.data {
|
||||
DefPathData::StructCtor |
|
||||
DefPathData::EnumVariant(_) => None,
|
||||
_ => Some(tcx.hir.body_owned_by(id))
|
||||
};
|
||||
|
||||
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
|
||||
let mut flow_inits = FlowInProgress::new(do_dataflow(
|
||||
tcx,
|
||||
@ -189,6 +199,11 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
node_id: id,
|
||||
move_data: &mdpe.move_data,
|
||||
param_env: param_env,
|
||||
locals_are_invalidated_at_exit: match tcx.hir.body_owner_kind(id) {
|
||||
hir::BodyOwnerKind::Const |
|
||||
hir::BodyOwnerKind::Static(_) => false,
|
||||
hir::BodyOwnerKind::Fn => true,
|
||||
},
|
||||
storage_dead_or_drop_error_reported: FxHashSet(),
|
||||
};
|
||||
|
||||
@ -198,7 +213,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
||||
id,
|
||||
&attributes,
|
||||
&dead_unwinds,
|
||||
Borrows::new(tcx, mir, opt_regioncx),
|
||||
Borrows::new(tcx, mir, opt_regioncx, def_id, body_id),
|
||||
|bd, i| bd.location(i),
|
||||
));
|
||||
|
||||
@ -220,6 +235,13 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
node_id: ast::NodeId,
|
||||
move_data: &'cx MoveData<'tcx>,
|
||||
param_env: ParamEnv<'gcx>,
|
||||
/// This keeps track of whether local variables are free-ed when the function
|
||||
/// exits even without a `StorageDead`, which appears to be the case for
|
||||
/// constants.
|
||||
///
|
||||
/// I'm not sure this is the right approach - @eddyb could you try and
|
||||
/// figure this out?
|
||||
locals_are_invalidated_at_exit: bool,
|
||||
/// This field keeps track of when storage dead or drop errors are reported
|
||||
/// in order to stop duplicate error reporting and identify the conditions required
|
||||
/// for a "temporary value dropped here while still borrowed" error. See #45360.
|
||||
@ -306,8 +328,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
|
||||
}
|
||||
|
||||
fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
|
||||
let summary = flow_state.summary();
|
||||
debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary);
|
||||
debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, flow_state);
|
||||
}
|
||||
|
||||
fn visit_statement_entry(
|
||||
@ -316,12 +337,11 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
|
||||
stmt: &Statement<'tcx>,
|
||||
flow_state: &Self::FlowState,
|
||||
) {
|
||||
let summary = flow_state.summary();
|
||||
debug!(
|
||||
"MirBorrowckCtxt::process_statement({:?}, {:?}): {}",
|
||||
location,
|
||||
stmt,
|
||||
summary
|
||||
flow_state
|
||||
);
|
||||
let span = stmt.source_info.span;
|
||||
match stmt.kind {
|
||||
@ -423,12 +443,11 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
|
||||
flow_state: &Self::FlowState,
|
||||
) {
|
||||
let loc = location;
|
||||
let summary = flow_state.summary();
|
||||
debug!(
|
||||
"MirBorrowckCtxt::process_terminator({:?}, {:?}): {}",
|
||||
location,
|
||||
term,
|
||||
summary
|
||||
flow_state
|
||||
);
|
||||
let span = term.source_info.span;
|
||||
match term.kind {
|
||||
@ -540,14 +559,13 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
|
||||
|
||||
if self.place_is_invalidated_at_exit(&borrow.place) {
|
||||
debug!("borrow conflicts at exit {:?}", borrow);
|
||||
let borrow_span = self.mir.source_info(borrow.location).span;
|
||||
// FIXME: should be talking about the region lifetime instead
|
||||
// of just a span here.
|
||||
let end_span = domain.opt_region_end_span(&borrow.region);
|
||||
|
||||
self.report_borrowed_value_does_not_live_long_enough(
|
||||
ContextKind::StorageDead.new(loc),
|
||||
(&borrow.place, borrow_span),
|
||||
(&borrow.place, end_span.unwrap_or(span)),
|
||||
end_span,
|
||||
)
|
||||
}
|
||||
@ -641,8 +659,9 @@ enum WriteKind {
|
||||
/// - Take flow state into consideration in `is_assignable()` for local variables
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum LocalMutationIsAllowed {
|
||||
Move,
|
||||
Yes,
|
||||
No,
|
||||
No
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -710,7 +729,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
context,
|
||||
(sd, place_span.0),
|
||||
flow_state,
|
||||
|this, _index, borrow, common_prefix| match (rw, borrow.kind) {
|
||||
|this, _index, borrow| match (rw, borrow.kind) {
|
||||
(Read(_), BorrowKind::Shared) => Control::Continue,
|
||||
(Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => {
|
||||
match kind {
|
||||
@ -727,7 +746,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
error_reported = true;
|
||||
this.report_conflicting_borrow(
|
||||
context,
|
||||
common_prefix,
|
||||
place_span,
|
||||
bk,
|
||||
&borrow,
|
||||
@ -748,7 +766,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
error_reported = true;
|
||||
this.report_conflicting_borrow(
|
||||
context,
|
||||
common_prefix,
|
||||
place_span,
|
||||
bk,
|
||||
&borrow,
|
||||
@ -934,7 +951,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
context,
|
||||
(place, span),
|
||||
(Deep, Write(WriteKind::Move)),
|
||||
LocalMutationIsAllowed::Yes,
|
||||
LocalMutationIsAllowed::Move,
|
||||
flow_state,
|
||||
);
|
||||
|
||||
@ -952,14 +969,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
|
||||
/// Returns whether a borrow of this place is invalidated when the function
|
||||
/// exits
|
||||
fn place_is_invalidated_at_exit(&self, place: &Place<'tcx>) -> bool {
|
||||
fn place_is_invalidated_at_exit(&mut self, place: &Place<'tcx>) -> bool {
|
||||
debug!("place_is_invalidated_at_exit({:?})", place);
|
||||
let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
|
||||
|
||||
// FIXME(nll-rfc#40): do more precise destructor tracking here. For now
|
||||
// we just know that all locals are dropped at function exit (otherwise
|
||||
// we'll have a memory leak) and assume that all statics have a destructor.
|
||||
let (might_be_alive, will_be_dropped) = match root_place {
|
||||
//
|
||||
// FIXME: allow thread-locals to borrow other thread locals?
|
||||
let (might_be_alive, will_be_dropped, local) = match root_place {
|
||||
Place::Static(statik) => {
|
||||
// Thread-locals might be dropped after the function exits, but
|
||||
// "true" statics will never be.
|
||||
@ -968,12 +987,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
.iter()
|
||||
.any(|attr| attr.check_name("thread_local"));
|
||||
|
||||
(true, is_thread_local)
|
||||
(true, is_thread_local, None)
|
||||
}
|
||||
Place::Local(_) => {
|
||||
Place::Local(local) => {
|
||||
// Locals are always dropped at function exit, and if they
|
||||
// have a destructor it would've been called already.
|
||||
(false, true)
|
||||
(false, self.locals_are_invalidated_at_exit, Some(*local))
|
||||
}
|
||||
Place::Projection(..) => {
|
||||
bug!("root of {:?} is a projection ({:?})?", place, root_place)
|
||||
@ -996,8 +1015,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
PrefixSet::Shallow
|
||||
};
|
||||
|
||||
self.prefixes(place, prefix_set)
|
||||
.any(|prefix| prefix == root_place)
|
||||
let result =
|
||||
self.prefixes(place, prefix_set).any(|prefix| prefix == root_place);
|
||||
|
||||
if result {
|
||||
if let Some(local) = local {
|
||||
if let Some(_) = self.storage_dead_or_drop_error_reported.replace(local) {
|
||||
debug!("place_is_invalidated_at_exit({:?}) - suppressed", place);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
@ -1343,7 +1373,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
let local = &self.mir.local_decls[local];
|
||||
match local.mutability {
|
||||
Mutability::Not => match is_local_mutation_allowed {
|
||||
LocalMutationIsAllowed::Yes => Ok(()),
|
||||
LocalMutationIsAllowed::Yes |
|
||||
LocalMutationIsAllowed::Move => Ok(()),
|
||||
LocalMutationIsAllowed::No => Err(place),
|
||||
},
|
||||
Mutability::Mut => Ok(()),
|
||||
@ -1368,10 +1399,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
// Mutably borrowed data is mutable, but only if we have a
|
||||
// unique path to the `&mut`
|
||||
hir::MutMutable => {
|
||||
if self.is_upvar_field_projection(&proj.base).is_some() {
|
||||
self.is_mutable(&proj.base, is_local_mutation_allowed)
|
||||
} else {
|
||||
self.is_unique(&proj.base)
|
||||
match self.is_upvar_field_projection(&proj.base) {
|
||||
Some(field) if {
|
||||
self.mir.upvar_decls[field.index()].by_ref
|
||||
} => {
|
||||
self.is_mutable(&proj.base,
|
||||
is_local_mutation_allowed)
|
||||
}
|
||||
_ => self.is_unique(&proj.base)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1387,7 +1422,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
}
|
||||
// `Box<T>` owns its content, so mutable if its location is mutable
|
||||
_ if base_ty.is_box() => {
|
||||
self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
|
||||
self.is_mutable(&proj.base, is_local_mutation_allowed)
|
||||
}
|
||||
// Deref should only be for reference, pointers or boxes
|
||||
_ => bug!("Deref of unexpected type: {:?}", base_ty),
|
||||
@ -1404,14 +1439,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
|
||||
if let Some(field) = field_projection {
|
||||
let decl = &self.mir.upvar_decls[field.index()];
|
||||
|
||||
return match decl.mutability {
|
||||
Mutability::Mut => self.is_unique(&proj.base),
|
||||
Mutability::Not => Err(place),
|
||||
debug!("decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
|
||||
decl, is_local_mutation_allowed, place);
|
||||
return match (decl.mutability, is_local_mutation_allowed) {
|
||||
(Mutability::Not, LocalMutationIsAllowed::No) |
|
||||
(Mutability::Not, LocalMutationIsAllowed::Yes) => Err(place),
|
||||
(Mutability::Not, LocalMutationIsAllowed::Move) |
|
||||
(Mutability::Mut, _) => self.is_unique(&proj.base),
|
||||
};
|
||||
}
|
||||
|
||||
self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
|
||||
self.is_mutable(&proj.base, is_local_mutation_allowed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1425,9 +1463,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
// Local variables are unique
|
||||
Ok(())
|
||||
}
|
||||
Place::Static(..) => {
|
||||
// Static variables are not
|
||||
Err(place)
|
||||
Place::Static(ref static_) => {
|
||||
if !self.tcx.is_static_mut(static_.def_id) {
|
||||
Err(place)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Place::Projection(ref proj) => {
|
||||
match proj.elem {
|
||||
@ -1478,7 +1519,350 @@ enum NoMovePathFound {
|
||||
ReachedStatic,
|
||||
}
|
||||
|
||||
/// The degree of overlap between 2 places for borrow-checking.
|
||||
enum Overlap {
|
||||
/// The places might partially overlap - in this case, we give
|
||||
/// up and say that they might conflict. This occurs when
|
||||
/// different fields of a union are borrowed. For example,
|
||||
/// if `u` is a union, we have no way of telling how disjoint
|
||||
/// `u.a.x` and `a.b.y` are.
|
||||
Arbitrary,
|
||||
/// The places have the same type, and are either completely disjoint
|
||||
/// or equal - i.e. they can't "partially" overlap as can occur with
|
||||
/// unions. This is the "base case" on which we recur for extensions
|
||||
/// of the place.
|
||||
EqualOrDisjoint,
|
||||
/// The places are disjoint, so we know all extensions of them
|
||||
/// will also be disjoint.
|
||||
Disjoint,
|
||||
}
|
||||
|
||||
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
// Given that the bases of `elem1` and `elem2` are always either equal
|
||||
// or disjoint (and have the same type!), return the overlap situation
|
||||
// between `elem1` and `elem2`.
|
||||
fn place_element_conflict(&self,
|
||||
elem1: &Place<'tcx>,
|
||||
elem2: &Place<'tcx>)
|
||||
-> Overlap
|
||||
{
|
||||
match (elem1, elem2) {
|
||||
(Place::Local(l1), Place::Local(l2)) => {
|
||||
if l1 == l2 {
|
||||
// the same local - base case, equal
|
||||
debug!("place_element_conflict: DISJOINT-OR-EQ-LOCAL");
|
||||
Overlap::EqualOrDisjoint
|
||||
} else {
|
||||
// different locals - base case, disjoint
|
||||
debug!("place_element_conflict: DISJOINT-LOCAL");
|
||||
Overlap::Disjoint
|
||||
}
|
||||
}
|
||||
(Place::Static(..), Place::Static(..)) => {
|
||||
// Borrows of statics do not have to be tracked here.
|
||||
debug!("place_element_conflict: IGNORED-STATIC");
|
||||
Overlap::Disjoint
|
||||
}
|
||||
(Place::Local(_), Place::Static(_)) |
|
||||
(Place::Static(_), Place::Local(_)) => {
|
||||
debug!("place_element_conflict: DISJOINT-STATIC-LOCAL");
|
||||
Overlap::Disjoint
|
||||
}
|
||||
(Place::Projection(pi1), Place::Projection(pi2)) => {
|
||||
match (&pi1.elem, &pi2.elem) {
|
||||
(ProjectionElem::Deref, ProjectionElem::Deref) => {
|
||||
// derefs (e.g. `*x` vs. `*x`) - recur.
|
||||
debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF");
|
||||
Overlap::EqualOrDisjoint
|
||||
}
|
||||
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
|
||||
if f1 == f2 {
|
||||
// same field (e.g. `a.y` vs. `a.y`) - recur.
|
||||
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
|
||||
Overlap::EqualOrDisjoint
|
||||
} else {
|
||||
let ty = pi1.base.ty(self.mir, self.tcx).to_ty(self.tcx);
|
||||
match ty.sty {
|
||||
ty::TyAdt(def, _) if def.is_union() => {
|
||||
// Different fields of a union, we are basically stuck.
|
||||
debug!("place_element_conflict: STUCK-UNION");
|
||||
Overlap::Arbitrary
|
||||
}
|
||||
_ => {
|
||||
// Different fields of a struct (`a.x` vs. `a.y`). Disjoint!
|
||||
debug!("place_element_conflict: DISJOINT-FIELD");
|
||||
Overlap::Disjoint
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(ProjectionElem::Downcast(_, v1), ProjectionElem::Downcast(_, v2)) => {
|
||||
// different variants are treated as having disjoint fields,
|
||||
// even if they occupy the same "space", because it's
|
||||
// impossible for 2 variants of the same enum to exist
|
||||
// (and therefore, to be borrowed) at the same time.
|
||||
//
|
||||
// Note that this is different from unions - we *do* allow
|
||||
// this code to compile:
|
||||
//
|
||||
// ```
|
||||
// fn foo(x: &mut Result<i32, i32>) {
|
||||
// let mut v = None;
|
||||
// if let Ok(ref mut a) = *x {
|
||||
// v = Some(a);
|
||||
// }
|
||||
// // here, you would *think* that the
|
||||
// // *entirety* of `x` would be borrowed,
|
||||
// // but in fact only the `Ok` variant is,
|
||||
// // so the `Err` variant is *entirely free*:
|
||||
// if let Err(ref mut a) = *x {
|
||||
// v = Some(a);
|
||||
// }
|
||||
// drop(v);
|
||||
// }
|
||||
// ```
|
||||
if v1 == v2 {
|
||||
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
|
||||
Overlap::EqualOrDisjoint
|
||||
} else {
|
||||
debug!("place_element_conflict: DISJOINT-FIELD");
|
||||
Overlap::Disjoint
|
||||
}
|
||||
}
|
||||
(ProjectionElem::Index(..), ProjectionElem::Index(..)) |
|
||||
(ProjectionElem::Index(..), ProjectionElem::ConstantIndex { .. }) |
|
||||
(ProjectionElem::Index(..), ProjectionElem::Subslice { .. }) |
|
||||
(ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..)) |
|
||||
(ProjectionElem::ConstantIndex { .. }, ProjectionElem::ConstantIndex { .. }) |
|
||||
(ProjectionElem::ConstantIndex { .. }, ProjectionElem::Subslice { .. }) |
|
||||
(ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) |
|
||||
(ProjectionElem::Subslice { .. }, ProjectionElem::ConstantIndex { .. }) |
|
||||
(ProjectionElem::Subslice { .. }, ProjectionElem::Subslice { .. }) => {
|
||||
// Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
|
||||
// (if the indexes differ) or equal (if they are the same), so this
|
||||
// is the recursive case that gives "equal *or* disjoint" its meaning.
|
||||
//
|
||||
// Note that by construction, MIR at borrowck can't subdivide
|
||||
// `Subslice` accesses (e.g. `a[2..3][i]` will never be present) - they
|
||||
// are only present in slice patterns, and we "merge together" nested
|
||||
// slice patterns. That means we don't have to think about these. It's
|
||||
// probably a good idea to assert this somewhere, but I'm too lazy.
|
||||
//
|
||||
// FIXME(#8636) we might want to return Disjoint if
|
||||
// both projections are constant and disjoint.
|
||||
debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY");
|
||||
Overlap::EqualOrDisjoint
|
||||
}
|
||||
|
||||
(ProjectionElem::Deref, _) |
|
||||
(ProjectionElem::Field(..), _) |
|
||||
(ProjectionElem::Index(..), _) |
|
||||
(ProjectionElem::ConstantIndex { .. }, _) |
|
||||
(ProjectionElem::Subslice { .. }, _) |
|
||||
(ProjectionElem::Downcast(..), _) => {
|
||||
bug!("mismatched projections in place_element_conflict: {:?} and {:?}",
|
||||
|
||||
elem1, elem2)
|
||||
}
|
||||
}
|
||||
}
|
||||
(Place::Projection(_), _) |
|
||||
(_, Place::Projection(_)) => {
|
||||
bug!("unexpected elements in place_element_conflict: {:?} and {:?}",
|
||||
elem1, elem2)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn borrow_conflicts_with_place(&mut self,
|
||||
borrow: &BorrowData<'tcx>,
|
||||
place: &Place<'tcx>,
|
||||
access: ShallowOrDeep)
|
||||
-> bool
|
||||
{
|
||||
debug!("borrow_conflicts_with_place({:?},{:?},{:?})", borrow, place, access);
|
||||
|
||||
// Return all the prefixes of `place` in reverse order, including
|
||||
// downcasts.
|
||||
fn place_elements<'a, 'tcx>(place: &'a Place<'tcx>) -> Vec<&'a Place<'tcx>>
|
||||
{
|
||||
let mut result = vec![];
|
||||
let mut place = place;
|
||||
loop {
|
||||
result.push(place);
|
||||
match place {
|
||||
Place::Projection(interior) => {
|
||||
place = &interior.base;
|
||||
}
|
||||
Place::Local(_) | Place::Static(_) => {
|
||||
result.reverse();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let borrow_components = place_elements(&borrow.place);
|
||||
let access_components = place_elements(place);
|
||||
debug!("borrow_conflicts_with_place: components {:?} / {:?}",
|
||||
borrow_components, access_components);
|
||||
|
||||
let borrow_components = borrow_components.into_iter()
|
||||
.map(Some).chain(iter::repeat(None));
|
||||
let access_components = access_components.into_iter()
|
||||
.map(Some).chain(iter::repeat(None));
|
||||
// The borrowck rules for proving disjointness are applied from the "root" of the
|
||||
// borrow forwards, iterating over "similar" projections in lockstep until
|
||||
// we can prove overlap one way or another. Essentially, we treat `Overlap` as
|
||||
// a monoid and report a conflict if the product ends up not being `Disjoint`.
|
||||
//
|
||||
// At each step, if we didn't run out of borrow or place, we know that our elements
|
||||
// have the same type, and that they only overlap if they are the identical.
|
||||
//
|
||||
// For example, if we are comparing these:
|
||||
// BORROW: (*x1[2].y).z.a
|
||||
// ACCESS: (*x1[i].y).w.b
|
||||
//
|
||||
// Then our steps are:
|
||||
// x1 | x1 -- places are the same
|
||||
// x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
|
||||
// x1[2].y | x1[i].y -- equal or disjoint
|
||||
// *x1[2].y | *x1[i].y -- equal or disjoint
|
||||
// (*x1[2].y).z | (*x1[i].y).w -- we are disjoint and don't need to check more!
|
||||
//
|
||||
// Because `zip` does potentially bad things to the iterator inside, this loop
|
||||
// also handles the case where the access might be a *prefix* of the borrow, e.g.
|
||||
//
|
||||
// BORROW: (*x1[2].y).z.a
|
||||
// ACCESS: x1[i].y
|
||||
//
|
||||
// Then our steps are:
|
||||
// x1 | x1 -- places are the same
|
||||
// x1[2] | x1[i] -- equal or disjoint (disjoint if indexes differ)
|
||||
// x1[2].y | x1[i].y -- equal or disjoint
|
||||
//
|
||||
// -- here we run out of access - the borrow can access a part of it. If this
|
||||
// is a full deep access, then we *know* the borrow conflicts with it. However,
|
||||
// if the access is shallow, then we can proceed:
|
||||
//
|
||||
// x1[2].y | (*x1[i].y) -- a deref! the access can't get past this, so we
|
||||
// are disjoint
|
||||
//
|
||||
// Our invariant is, that at each step of the iteration:
|
||||
// - If we didn't run out of access to match, our borrow and access are comparable
|
||||
// and either equal or disjoint.
|
||||
// - If we did run out of accesss, the borrow can access a part of it.
|
||||
for (borrow_c, access_c) in borrow_components.zip(access_components) {
|
||||
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
|
||||
debug!("borrow_conflicts_with_place: {:?} vs. {:?}", borrow_c, access_c);
|
||||
match (borrow_c, access_c) {
|
||||
(None, _) => {
|
||||
// If we didn't run out of access, the borrow can access all of our
|
||||
// place (e.g. a borrow of `a.b` with an access to `a.b.c`),
|
||||
// so we have a conflict.
|
||||
//
|
||||
// If we did, then we still know that the borrow can access a *part*
|
||||
// of our place that our access cares about (a borrow of `a.b.c`
|
||||
// with an access to `a.b`), so we still have a conflict.
|
||||
//
|
||||
// FIXME: Differs from AST-borrowck; includes drive-by fix
|
||||
// to #38899. Will probably need back-compat mode flag.
|
||||
debug!("borrow_conflict_with_place: full borrow, CONFLICT");
|
||||
return true;
|
||||
}
|
||||
(Some(borrow_c), None) => {
|
||||
// We know that the borrow can access a part of our place. This
|
||||
// is a conflict if that is a part our access cares about.
|
||||
|
||||
let (base, elem) = match borrow_c {
|
||||
Place::Projection(box Projection { base, elem }) => (base, elem),
|
||||
_ => bug!("place has no base?")
|
||||
};
|
||||
let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
|
||||
|
||||
match (elem, &base_ty.sty, access) {
|
||||
(_, _, Shallow(Some(ArtificialField::Discriminant))) |
|
||||
(_, _, Shallow(Some(ArtificialField::ArrayLength))) => {
|
||||
// The discriminant and array length are like
|
||||
// additional fields on the type; they do not
|
||||
// overlap any existing data there. Furthermore,
|
||||
// they cannot actually be a prefix of any
|
||||
// borrowed place (at least in MIR as it is
|
||||
// currently.)
|
||||
//
|
||||
// e.g. a (mutable) borrow of `a[5]` while we read the
|
||||
// array length of `a`.
|
||||
debug!("borrow_conflicts_with_place: implicit field");
|
||||
return false;
|
||||
}
|
||||
|
||||
(ProjectionElem::Deref, _, Shallow(None)) => {
|
||||
// e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
|
||||
// prefix thereof - the shallow access can't touch anything behind
|
||||
// the pointer.
|
||||
debug!("borrow_conflicts_with_place: shallow access behind ptr");
|
||||
return false;
|
||||
}
|
||||
(ProjectionElem::Deref, ty::TyRef(_, ty::TypeAndMut {
|
||||
ty: _, mutbl: hir::MutImmutable
|
||||
}), _) => {
|
||||
// the borrow goes through a dereference of a shared reference.
|
||||
//
|
||||
// I'm not sure why we are tracking these borrows - shared
|
||||
// references can *always* be aliased, which means the
|
||||
// permission check already account for this borrow.
|
||||
debug!("borrow_conflicts_with_place: behind a shared ref");
|
||||
return false;
|
||||
}
|
||||
|
||||
(ProjectionElem::Deref, _, Deep) |
|
||||
(ProjectionElem::Field { .. }, _, _) |
|
||||
(ProjectionElem::Index { ..}, _, _) |
|
||||
(ProjectionElem::ConstantIndex { .. }, _, _) |
|
||||
(ProjectionElem::Subslice { .. }, _, _) |
|
||||
(ProjectionElem::Downcast { .. }, _, _) => {
|
||||
// Recursive case. This can still be disjoint on a
|
||||
// further iteration if this a shallow access and
|
||||
// there's a deref later on, e.g. a borrow
|
||||
// of `*x.y` while accessing `x`.
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(borrow_c), Some(access_c)) => {
|
||||
match self.place_element_conflict(&borrow_c, access_c) {
|
||||
Overlap::Arbitrary => {
|
||||
// We have encountered different fields of potentially
|
||||
// the same union - the borrow now partially overlaps.
|
||||
//
|
||||
// There is no *easy* way of comparing the fields
|
||||
// further on, because they might have different types
|
||||
// (e.g. borrows of `u.a.0` and `u.b.y` where `.0` and
|
||||
// `.y` come from different structs).
|
||||
//
|
||||
// We could try to do some things here - e.g. count
|
||||
// dereferences - but that's probably not a good
|
||||
// idea, at least for now, so just give up and
|
||||
// report a conflict. This is unsafe code anyway so
|
||||
// the user could always use raw pointers.
|
||||
debug!("borrow_conflicts_with_place: arbitrary -> conflict");
|
||||
return true;
|
||||
}
|
||||
Overlap::EqualOrDisjoint => {
|
||||
// This is the recursive case - proceed to the next element.
|
||||
}
|
||||
Overlap::Disjoint => {
|
||||
// We have proven the borrow disjoint - further
|
||||
// projections will remain disjoint.
|
||||
debug!("borrow_conflicts_with_place: disjoint");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!("iter::repeat returned None")
|
||||
}
|
||||
|
||||
fn each_borrow_involving_path<F>(
|
||||
&mut self,
|
||||
_context: Context,
|
||||
@ -1486,7 +1870,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
flow_state: &InProgress<'cx, 'gcx, 'tcx>,
|
||||
mut op: F,
|
||||
) where
|
||||
F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control,
|
||||
F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>) -> Control,
|
||||
{
|
||||
let (access, place) = access_place;
|
||||
|
||||
@ -1501,47 +1885,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
'next_borrow: for i in flow_state.borrows.elems_incoming() {
|
||||
let borrowed = &data[i];
|
||||
|
||||
// Is `place` (or a prefix of it) already borrowed? If
|
||||
// so, that's relevant.
|
||||
//
|
||||
// FIXME: Differs from AST-borrowck; includes drive-by fix
|
||||
// to #38899. Will probably need back-compat mode flag.
|
||||
for accessed_prefix in self.prefixes(place, PrefixSet::All) {
|
||||
if *accessed_prefix == borrowed.place {
|
||||
// FIXME: pass in enum describing case we are in?
|
||||
let ctrl = op(self, i, borrowed, accessed_prefix);
|
||||
if ctrl == Control::Break {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is `place` a prefix (modulo access type) of the
|
||||
// `borrowed.place`? If so, that's relevant.
|
||||
|
||||
let prefix_kind = match access {
|
||||
Shallow(Some(ArtificialField::Discriminant)) |
|
||||
Shallow(Some(ArtificialField::ArrayLength)) => {
|
||||
// The discriminant and array length are like
|
||||
// additional fields on the type; they do not
|
||||
// overlap any existing data there. Furthermore,
|
||||
// they cannot actually be a prefix of any
|
||||
// borrowed place (at least in MIR as it is
|
||||
// currently.)
|
||||
continue 'next_borrow;
|
||||
}
|
||||
Shallow(None) => PrefixSet::Shallow,
|
||||
Deep => PrefixSet::Supporting,
|
||||
};
|
||||
|
||||
for borrowed_prefix in self.prefixes(&borrowed.place, prefix_kind) {
|
||||
if borrowed_prefix == place {
|
||||
// FIXME: pass in enum describing case we are in?
|
||||
let ctrl = op(self, i, borrowed, borrowed_prefix);
|
||||
if ctrl == Control::Break {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if self.borrow_conflicts_with_place(borrowed, place, access) {
|
||||
let ctrl = op(self, i, borrowed);
|
||||
if ctrl == Control::Break { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1595,6 +1941,7 @@ mod prefixes {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(super) enum PrefixSet {
|
||||
/// Doesn't stop until it returns the base case (a Local or
|
||||
/// Static prefix).
|
||||
@ -1907,17 +2254,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
||||
fn report_conflicting_borrow(
|
||||
&mut self,
|
||||
context: Context,
|
||||
common_prefix: &Place<'tcx>,
|
||||
(place, span): (&Place<'tcx>, Span),
|
||||
gen_borrow_kind: BorrowKind,
|
||||
issued_borrow: &BorrowData,
|
||||
end_issued_loan_span: Option<Span>,
|
||||
) {
|
||||
use self::prefixes::IsPrefixOf;
|
||||
|
||||
assert!(common_prefix.is_prefix_of(place));
|
||||
assert!(common_prefix.is_prefix_of(&issued_borrow.place));
|
||||
|
||||
let issued_span = self.retrieve_borrow_span(issued_borrow);
|
||||
|
||||
let new_closure_span = self.find_closure_span(span, context.loc);
|
||||
@ -2373,8 +2714,10 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
|
||||
xform_move_outs(&mut self.move_outs);
|
||||
xform_ever_inits(&mut self.ever_inits);
|
||||
}
|
||||
}
|
||||
|
||||
fn summary(&self) -> String {
|
||||
impl<'b, 'gcx, 'tcx> fmt::Display for InProgress<'b, 'gcx, 'tcx> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut s = String::new();
|
||||
|
||||
s.push_str("borrows in effect: [");
|
||||
@ -2451,7 +2794,7 @@ impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
|
||||
});
|
||||
s.push_str("]");
|
||||
|
||||
return s;
|
||||
fmt::Display::fmt(&s, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,17 @@ impl<'tcx> CFG<'tcx> {
|
||||
source_info: SourceInfo,
|
||||
region_scope: region::Scope) {
|
||||
if tcx.sess.emit_end_regions() {
|
||||
if let region::ScopeData::CallSite(_) = region_scope.data() {
|
||||
// The CallSite scope (aka the root scope) is sort of weird, in that it is
|
||||
// supposed to "separate" the "interior" and "exterior" of a closure. Being
|
||||
// that, it is not really a part of the region hierarchy, but for some
|
||||
// reason it *is* considered a part of it.
|
||||
//
|
||||
// It should die a hopefully painful death with NLL, so let's leave this hack
|
||||
// for now so that nobody can complain about soundness.
|
||||
return
|
||||
}
|
||||
|
||||
self.push(block, Statement {
|
||||
source_info,
|
||||
kind: StatementKind::EndRegion(region_scope),
|
||||
|
@ -8,6 +8,9 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::middle::region;
|
||||
use rustc::mir::{self, Location, Mir};
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::ty::{self, Region, TyCtxt};
|
||||
@ -27,6 +30,7 @@ use borrow_check::nll::ToRegionVid;
|
||||
use syntax_pos::Span;
|
||||
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
// `Borrows` maps each dataflow bit to an `Rvalue::Ref`, which can be
|
||||
// uniquely identified in the MIR by the `Location` of the assigment
|
||||
@ -34,9 +38,12 @@ use std::fmt;
|
||||
pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
scope_tree: Rc<region::ScopeTree>,
|
||||
root_scope: Option<region::Scope>,
|
||||
borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
|
||||
location_map: FxHashMap<Location, BorrowIndex>,
|
||||
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
|
||||
local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
|
||||
region_span_map: FxHashMap<RegionKind, Span>,
|
||||
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
|
||||
}
|
||||
@ -69,22 +76,32 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
|
||||
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
mir: &'a Mir<'tcx>,
|
||||
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>)
|
||||
nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
|
||||
def_id: DefId,
|
||||
body_id: Option<hir::BodyId>)
|
||||
-> Self {
|
||||
let scope_tree = tcx.region_scope_tree(def_id);
|
||||
let root_scope = body_id.map(|body_id| {
|
||||
region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
|
||||
});
|
||||
let mut visitor = GatherBorrows {
|
||||
tcx,
|
||||
mir,
|
||||
idx_vec: IndexVec::new(),
|
||||
location_map: FxHashMap(),
|
||||
region_map: FxHashMap(),
|
||||
local_map: FxHashMap(),
|
||||
region_span_map: FxHashMap()
|
||||
};
|
||||
visitor.visit_mir(mir);
|
||||
return Borrows { tcx: tcx,
|
||||
mir: mir,
|
||||
borrows: visitor.idx_vec,
|
||||
scope_tree,
|
||||
root_scope,
|
||||
location_map: visitor.location_map,
|
||||
region_map: visitor.region_map,
|
||||
local_map: visitor.local_map,
|
||||
region_span_map: visitor.region_span_map,
|
||||
nonlexical_regioncx };
|
||||
|
||||
@ -94,6 +111,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
||||
idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
|
||||
location_map: FxHashMap<Location, BorrowIndex>,
|
||||
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
|
||||
local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
|
||||
region_span_map: FxHashMap<RegionKind, Span>,
|
||||
}
|
||||
|
||||
@ -101,6 +119,14 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
||||
fn visit_rvalue(&mut self,
|
||||
rvalue: &mir::Rvalue<'tcx>,
|
||||
location: mir::Location) {
|
||||
fn root_local(mut p: &mir::Place<'_>) -> Option<mir::Local> {
|
||||
loop { match p {
|
||||
mir::Place::Projection(pi) => p = &pi.base,
|
||||
mir::Place::Static(_) => return None,
|
||||
mir::Place::Local(l) => return Some(*l)
|
||||
}}
|
||||
}
|
||||
|
||||
if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
|
||||
if is_unsafe_place(self.tcx, self.mir, place) { return; }
|
||||
|
||||
@ -109,8 +135,14 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
||||
};
|
||||
let idx = self.idx_vec.push(borrow);
|
||||
self.location_map.insert(location, idx);
|
||||
|
||||
let borrows = self.region_map.entry(region).or_insert(FxHashSet());
|
||||
borrows.insert(idx);
|
||||
|
||||
if let Some(local) = root_local(place) {
|
||||
let borrows = self.local_map.entry(local).or_insert(FxHashSet());
|
||||
borrows.insert(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +231,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
|
||||
mir::StatementKind::EndRegion(region_scope) => {
|
||||
if let Some(borrow_indexes) = self.region_map.get(&ReScope(region_scope)) {
|
||||
assert!(self.nonlexical_regioncx.is_none());
|
||||
for idx in borrow_indexes { sets.kill(&idx); }
|
||||
sets.kill_all(borrow_indexes);
|
||||
} else {
|
||||
// (if there is no entry, then there are no borrows to be tracked)
|
||||
}
|
||||
@ -224,10 +256,19 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
mir::StatementKind::StorageDead(local) => {
|
||||
// Make sure there are no remaining borrows for locals that
|
||||
// are gone out of scope.
|
||||
//
|
||||
// FIXME: expand this to variables that are assigned over.
|
||||
if let Some(borrow_indexes) = self.local_map.get(&local) {
|
||||
sets.kill_all(borrow_indexes);
|
||||
}
|
||||
}
|
||||
|
||||
mir::StatementKind::InlineAsm { .. } |
|
||||
mir::StatementKind::SetDiscriminant { .. } |
|
||||
mir::StatementKind::StorageLive(..) |
|
||||
mir::StatementKind::StorageDead(..) |
|
||||
mir::StatementKind::Validate(..) |
|
||||
mir::StatementKind::Nop => {}
|
||||
|
||||
@ -253,8 +294,17 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
|
||||
// like unwind paths, we do not always emit `EndRegion` statements, so we
|
||||
// add some kills here as a "backup" and to avoid spurious error messages.
|
||||
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
|
||||
if let ReScope(..) = borrow_data.region {
|
||||
sets.kill(&borrow_index);
|
||||
if let ReScope(scope) = borrow_data.region {
|
||||
// Check that the scope is not actually a scope from a function that is
|
||||
// a parent of our closure. Note that the CallSite scope itself is
|
||||
// *outside* of the closure, for some weird reason.
|
||||
if let Some(root_scope) = self.root_scope {
|
||||
if *scope != root_scope &&
|
||||
self.scope_tree.is_subscope_of(*scope, root_scope)
|
||||
{
|
||||
sets.kill(&borrow_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc::mir::{self, Mir, Location};
|
||||
use rustc_data_structures::bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
|
||||
use rustc_data_structures::bitslice::{BitwiseOperator};
|
||||
use rustc_data_structures::indexed_set::{IdxSet};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
@ -504,7 +503,6 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
|
||||
let stmt = &mir[location.block].statements[location.statement_index];
|
||||
let loc_map = &move_data.loc_map;
|
||||
let path_map = &move_data.path_map;
|
||||
let bits_per_block = self.bits_per_block();
|
||||
|
||||
match stmt.kind {
|
||||
// this analysis only tries to find moves explicitly
|
||||
@ -515,21 +513,15 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
|
||||
_ => {
|
||||
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
|
||||
stmt, location, &loc_map[location]);
|
||||
for move_index in &loc_map[location] {
|
||||
// Every path deinitialized by a *particular move*
|
||||
// has corresponding bit, "gen'ed" (i.e. set)
|
||||
// here, in dataflow vector
|
||||
zero_to_one(sets.gen_set.words_mut(), *move_index);
|
||||
}
|
||||
// Every path deinitialized by a *particular move*
|
||||
// has corresponding bit, "gen'ed" (i.e. set)
|
||||
// here, in dataflow vector
|
||||
sets.gen_all_and_assert_dead(&loc_map[location]);
|
||||
}
|
||||
}
|
||||
|
||||
for_location_inits(tcx, mir, move_data, location,
|
||||
|mpi| for moi in &path_map[mpi] {
|
||||
assert!(moi.index() < bits_per_block);
|
||||
sets.kill_set.add(&moi);
|
||||
}
|
||||
);
|
||||
|mpi| sets.kill_all(&path_map[mpi]));
|
||||
}
|
||||
|
||||
fn terminator_effect(&self,
|
||||
@ -543,18 +535,10 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
|
||||
|
||||
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
|
||||
term, location, &loc_map[location]);
|
||||
let bits_per_block = self.bits_per_block();
|
||||
for move_index in &loc_map[location] {
|
||||
assert!(move_index.index() < bits_per_block);
|
||||
zero_to_one(sets.gen_set.words_mut(), *move_index);
|
||||
}
|
||||
sets.gen_all_and_assert_dead(&loc_map[location]);
|
||||
|
||||
for_location_inits(tcx, mir, move_data, location,
|
||||
|mpi| for moi in &path_map[mpi] {
|
||||
assert!(moi.index() < bits_per_block);
|
||||
sets.kill_set.add(&moi);
|
||||
}
|
||||
);
|
||||
|mpi| sets.kill_all(&path_map[mpi]));
|
||||
}
|
||||
|
||||
fn propagate_call_return(&self,
|
||||
@ -585,11 +569,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
fn start_block_effect(&self, sets: &mut BlockSets<InitIndex>) {
|
||||
let bits_per_block = self.bits_per_block();
|
||||
for init_index in (0..self.mir.arg_count).map(InitIndex::new) {
|
||||
assert!(init_index.index() < bits_per_block);
|
||||
sets.gen_set.add(&init_index);
|
||||
}
|
||||
sets.gen_all((0..self.mir.arg_count).map(InitIndex::new));
|
||||
}
|
||||
fn statement_effect(&self,
|
||||
sets: &mut BlockSets<InitIndex>,
|
||||
@ -599,26 +579,39 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
||||
let init_path_map = &move_data.init_path_map;
|
||||
let init_loc_map = &move_data.init_loc_map;
|
||||
let rev_lookup = &move_data.rev_lookup;
|
||||
let bits_per_block = self.bits_per_block();
|
||||
|
||||
debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
|
||||
stmt, location, &init_loc_map[location]);
|
||||
for init_index in &init_loc_map[location] {
|
||||
assert!(init_index.index() < bits_per_block);
|
||||
sets.gen_set.add(init_index);
|
||||
}
|
||||
sets.gen_all(&init_loc_map[location]);
|
||||
|
||||
match stmt.kind {
|
||||
mir::StatementKind::StorageDead(local) => {
|
||||
// End inits for StorageDead, so that an immutable variable can
|
||||
// be reinitialized on the next iteration of the loop.
|
||||
mir::StatementKind::StorageDead(local) |
|
||||
mir::StatementKind::StorageLive(local) => {
|
||||
// End inits for StorageDead and StorageLive, so that an immutable
|
||||
// variable can be reinitialized on the next iteration of the loop.
|
||||
//
|
||||
// FIXME(#46525): We *need* to do this for StorageLive as well as
|
||||
// StorageDead, because lifetimes of match bindings with guards are
|
||||
// weird - i.e. this code
|
||||
//
|
||||
// ```
|
||||
// fn main() {
|
||||
// match 0 {
|
||||
// a | a
|
||||
// if { println!("a={}", a); false } => {}
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// runs the guard twice, using the same binding for `a`, and only
|
||||
// storagedeads after everything ends, so if we don't regard the
|
||||
// storagelive as killing storage, we would have a multiple assignment
|
||||
// to immutable data error.
|
||||
if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Place::Local(local)) {
|
||||
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
|
||||
stmt, location, &init_path_map[mpi]);
|
||||
for ii in &init_path_map[mpi] {
|
||||
assert!(ii.index() < bits_per_block);
|
||||
sets.kill_set.add(&ii);
|
||||
}
|
||||
stmt, location, &init_path_map[mpi]);
|
||||
sets.kill_all(&init_path_map[mpi]);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -634,13 +627,11 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
||||
let init_loc_map = &move_data.init_loc_map;
|
||||
debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
|
||||
term, location, &init_loc_map[location]);
|
||||
let bits_per_block = self.bits_per_block();
|
||||
for init_index in &init_loc_map[location] {
|
||||
if move_data.inits[*init_index].kind != InitKind::NonPanicPathOnly {
|
||||
assert!(init_index.index() < bits_per_block);
|
||||
sets.gen_set.add(init_index);
|
||||
}
|
||||
}
|
||||
sets.gen_all(
|
||||
init_loc_map[location].iter().filter(|init_index| {
|
||||
move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn propagate_call_return(&self,
|
||||
@ -663,11 +654,6 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
|
||||
let retval = bitvec.set_bit(move_index.index());
|
||||
assert!(retval);
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
||||
#[inline]
|
||||
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
||||
|
@ -18,6 +18,7 @@ use rustc::ty::{self, TyCtxt};
|
||||
use rustc::mir::{self, Mir, BasicBlock, BasicBlockData, Location, Statement, Terminator};
|
||||
use rustc::session::Session;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
@ -492,10 +493,39 @@ impl<'a, E:Idx> BlockSets<'a, E> {
|
||||
self.gen_set.add(e);
|
||||
self.kill_set.remove(e);
|
||||
}
|
||||
fn gen_all<I>(&mut self, i: I)
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<E>
|
||||
{
|
||||
for j in i {
|
||||
self.gen(j.borrow());
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_all_and_assert_dead<I>(&mut self, i: I)
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<E>
|
||||
{
|
||||
for j in i {
|
||||
let j = j.borrow();
|
||||
let retval = self.gen_set.add(j);
|
||||
self.kill_set.remove(j);
|
||||
assert!(retval);
|
||||
}
|
||||
}
|
||||
|
||||
fn kill(&mut self, e: &E) {
|
||||
self.gen_set.remove(e);
|
||||
self.kill_set.add(e);
|
||||
}
|
||||
fn kill_all<I>(&mut self, i: I)
|
||||
where I: IntoIterator,
|
||||
I::Item: Borrow<E>
|
||||
{
|
||||
for j in i {
|
||||
self.kill(j.borrow());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E:Idx> AllSets<E> {
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
fn cplusplus_mode(x: isize) -> &'static isize {
|
||||
&x //[ast]~ ERROR `x` does not live long enough
|
||||
//[mir]~^ ERROR borrowed value does not live long enough
|
||||
}
|
||||
//[mir]~^ ERROR borrowed value does not live long enough
|
||||
|
||||
fn main() {}
|
||||
|
@ -14,9 +14,9 @@
|
||||
fn cplusplus_mode_exceptionally_unsafe(x: &mut Option<&'static mut isize>) {
|
||||
let mut z = (0, 0);
|
||||
*x = Some(&mut z.1); //[ast]~ ERROR [E0597]
|
||||
//[mir]~^ ERROR [E0597]
|
||||
panic!("catch me for a dangling pointer!")
|
||||
}
|
||||
//[mir]~^ ERROR [E0597]
|
||||
|
||||
fn main() {
|
||||
cplusplus_mode_exceptionally_unsafe(&mut None);
|
||||
|
@ -8,6 +8,9 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// revisions: ast mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
#![feature(box_syntax)]
|
||||
|
||||
fn call_f<F:FnOnce() -> isize>(f: F) -> isize {
|
||||
@ -18,5 +21,6 @@ fn main() {
|
||||
let t: Box<_> = box 3;
|
||||
|
||||
call_f(move|| { *t + 1 });
|
||||
call_f(move|| { *t + 1 }); //~ ERROR capture of moved value
|
||||
call_f(move|| { *t + 1 }); //[ast]~ ERROR capture of moved value
|
||||
//[mir]~^ ERROR use of moved value
|
||||
}
|
||||
|
@ -23,15 +23,9 @@ fn main() {
|
||||
1 => { addr = &mut x; } //[ast]~ ERROR [E0499]
|
||||
//[mir]~^ ERROR [E0499]
|
||||
2 => { addr = &mut x; } //[ast]~ ERROR [E0499]
|
||||
//[mir]~^ ERROR [E0506]
|
||||
//[mir]~| ERROR [E0499]
|
||||
//[mir]~| ERROR [E0499]
|
||||
//[mir]~^ ERROR [E0499]
|
||||
_ => { addr = &mut x; } //[ast]~ ERROR [E0499]
|
||||
//[mir]~^ ERROR [E0506]
|
||||
//[mir]~| ERROR [E0499]
|
||||
//[mir]~| ERROR [E0499]
|
||||
//[mir]~^ ERROR [E0499]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ignore-test will be fixed later
|
||||
// revisions: ast mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
|
@ -52,12 +52,12 @@ fn main() {
|
||||
{
|
||||
let ra = &u.a;
|
||||
let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
|
||||
// FIXME Error for MIR (needs support for union)
|
||||
//[mir]~^ ERROR cannot borrow `u.b` as mutable because it is also borrowed as immutable
|
||||
}
|
||||
{
|
||||
let ra = &u.a;
|
||||
u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
|
||||
// FIXME Error for MIR (needs support for union)
|
||||
//[mir]~^ ERROR cannot assign to `u.b` because it is borrowed
|
||||
}
|
||||
// Mut borrow, same field
|
||||
{
|
||||
@ -84,22 +84,23 @@ fn main() {
|
||||
{
|
||||
let rma = &mut u.a;
|
||||
let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
|
||||
// FIXME Error for MIR (needs support for union)
|
||||
//[mir]~^ ERROR cannot borrow `u.b` as immutable because it is also borrowed as mutable
|
||||
}
|
||||
{
|
||||
let ra = &mut u.a;
|
||||
let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed
|
||||
// FIXME Error for MIR (needs support for union)
|
||||
//[mir]~^ ERROR cannot use `u.b` because it was mutably borrowed
|
||||
|
||||
}
|
||||
{
|
||||
let rma = &mut u.a;
|
||||
let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
|
||||
// FIXME Error for MIR (needs support for union)
|
||||
//[mir]~^ ERROR cannot borrow `u.b` as mutable more than once at a time
|
||||
}
|
||||
{
|
||||
let rma = &mut u.a;
|
||||
u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
|
||||
// FIXME Error for MIR (needs support for union)
|
||||
//[mir]~^ ERROR cannot assign to `u.b` because it is borrowed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
@ -22,7 +23,7 @@ fn main() {
|
||||
println!("t[0]: {}", t[0]);
|
||||
a[2] = 0; //[ast]~ ERROR cannot assign to `a[..]` because it is borrowed
|
||||
//[cmp]~^ ERROR cannot assign to `a[..]` because it is borrowed (Ast)
|
||||
// FIXME Error for MIR (error missed)
|
||||
//[cmp]~| ERROR cannot assign to `a[..]` because it is borrowed (Mir)
|
||||
println!("t[0]: {}", t[0]);
|
||||
t[0];
|
||||
}
|
||||
|
@ -20,13 +20,9 @@ fn causes_ice(mut l: &mut Sexpression) {
|
||||
loop { match l {
|
||||
&mut Sexpression::Num(ref mut n) => {},
|
||||
&mut Sexpression::Cons(ref mut expr) => { //[ast]~ ERROR [E0499]
|
||||
//[mir]~^ ERROR [E0506]
|
||||
//[mir]~| ERROR [E0499]
|
||||
//[mir]~^ ERROR [E0499]
|
||||
l = &mut **expr; //[ast]~ ERROR [E0506]
|
||||
//[mir]~^ ERROR [E0506]
|
||||
//[mir]~| ERROR [E0506]
|
||||
//[mir]~| ERROR [E0499]
|
||||
//[mir]~| ERROR [E0499]
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -16,6 +16,10 @@
|
||||
// behavior (because the improperly accepted closure was actually
|
||||
// able to be invoked).
|
||||
|
||||
// ignore-tidy-linelength
|
||||
// revisions: ast mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
struct WrapA<F>(Option<F>);
|
||||
|
||||
impl<F> WrapA<F> {
|
||||
@ -75,9 +79,11 @@ impl<F, T> WrapA<F>
|
||||
fn main() {
|
||||
let mut w = WrapA::new().set(|x: usize, y: usize| {
|
||||
WrapB::new().set(|t: bool| if t { x } else { y }) // (separate errors for `x` vs `y`)
|
||||
//~^ ERROR `x` does not live long enough
|
||||
//~| ERROR `y` does not live long enough
|
||||
//[ast]~^ ERROR `x` does not live long enough
|
||||
//[ast]~| ERROR `y` does not live long enough
|
||||
});
|
||||
//[mir]~^ ERROR borrowed value does not live long enough
|
||||
//[mir]~| ERROR borrowed value does not live long enough
|
||||
|
||||
w.handle(); // This works
|
||||
// w.handle_ref(); // This doesn't
|
||||
|
@ -8,6 +8,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -Z borrowck=compare
|
||||
|
||||
fn test1() {
|
||||
// from issue 6338
|
||||
|
Loading…
Reference in New Issue
Block a user