mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-25 22:22:44 +00:00
use places_conflict
to handle reassignment
This fixes the handling of reassignment of struct fields.
This commit is contained in:
parent
97c58ed66c
commit
b64ddecae8
@ -605,8 +605,10 @@ enum WriteKind {
|
|||||||
/// - Take flow state into consideration in `is_assignable()` for local variables
|
/// - Take flow state into consideration in `is_assignable()` for local variables
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
enum LocalMutationIsAllowed {
|
enum LocalMutationIsAllowed {
|
||||||
Move,
|
|
||||||
Yes,
|
Yes,
|
||||||
|
/// We want use of immutable upvars to cause a "write to immutable upvar"
|
||||||
|
/// error, not an "reassignment" error.
|
||||||
|
ExceptUpvars,
|
||||||
No
|
No
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,7 +804,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
context,
|
context,
|
||||||
place_span,
|
place_span,
|
||||||
(kind, Write(WriteKind::Mutate)),
|
(kind, Write(WriteKind::Mutate)),
|
||||||
LocalMutationIsAllowed::Yes,
|
// We want immutable upvars to cause an "assignment to immutable var"
|
||||||
|
// error, not an "reassignment of immutable var" error, because the
|
||||||
|
// latter can't find a good previous assignment span.
|
||||||
|
//
|
||||||
|
// There's probably a better way to do this.
|
||||||
|
LocalMutationIsAllowed::ExceptUpvars,
|
||||||
flow_state,
|
flow_state,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -922,7 +929,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
context,
|
context,
|
||||||
(place, span),
|
(place, span),
|
||||||
(Deep, Write(WriteKind::Move)),
|
(Deep, Write(WriteKind::Move)),
|
||||||
LocalMutationIsAllowed::Move,
|
LocalMutationIsAllowed::Yes,
|
||||||
flow_state,
|
flow_state,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1009,34 +1016,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
(place, span): (&Place<'tcx>, Span),
|
(place, span): (&Place<'tcx>, Span),
|
||||||
flow_state: &Flows<'cx, 'gcx, 'tcx>,
|
flow_state: &Flows<'cx, 'gcx, 'tcx>,
|
||||||
) {
|
) {
|
||||||
let move_data = self.move_data;
|
debug!("check_if_reassignment_to_immutable_state({:?})", place);
|
||||||
|
|
||||||
// determine if this path has a non-mut owner (and thus needs checking).
|
// determine if this path has a non-mut owner (and thus needs checking).
|
||||||
if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) {
|
if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
debug!("check_if_reassignment_to_immutable_state({:?}) - is an imm local", place);
|
||||||
|
|
||||||
match self.move_path_closest_to(place) {
|
for i in flow_state.ever_inits.elems_incoming() {
|
||||||
Ok(mpi) => for ii in &move_data.init_path_map[mpi] {
|
let init = self.move_data.inits[i];
|
||||||
if flow_state.ever_inits.contains(ii) {
|
let init_place = &self.move_data.move_paths[init.path].place;
|
||||||
let first_assign_span = self.move_data.inits[*ii].span;
|
if self.places_conflict(&init_place, place, Deep) {
|
||||||
self.report_illegal_reassignment(context, (place, span), first_assign_span);
|
self.report_illegal_reassignment(context, (place, span), init.span);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(NoMovePathFound::ReachedStatic) => {
|
|
||||||
let item_msg = match self.describe_place(place) {
|
|
||||||
Some(name) => format!("immutable static item `{}`", name),
|
|
||||||
None => "immutable static item".to_owned(),
|
|
||||||
};
|
|
||||||
self.tcx.sess.delay_span_bug(
|
|
||||||
span,
|
|
||||||
&format!(
|
|
||||||
"cannot assign to {}, should have been caught by \
|
|
||||||
`check_access_permissions()`",
|
|
||||||
item_msg
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1341,7 +1333,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
match local.mutability {
|
match local.mutability {
|
||||||
Mutability::Not => match is_local_mutation_allowed {
|
Mutability::Not => match is_local_mutation_allowed {
|
||||||
LocalMutationIsAllowed::Yes |
|
LocalMutationIsAllowed::Yes |
|
||||||
LocalMutationIsAllowed::Move => Ok(()),
|
LocalMutationIsAllowed::ExceptUpvars => Ok(()),
|
||||||
LocalMutationIsAllowed::No => Err(place),
|
LocalMutationIsAllowed::No => Err(place),
|
||||||
},
|
},
|
||||||
Mutability::Mut => Ok(()),
|
Mutability::Mut => Ok(()),
|
||||||
@ -1410,8 +1402,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
decl, is_local_mutation_allowed, place);
|
decl, is_local_mutation_allowed, place);
|
||||||
return match (decl.mutability, is_local_mutation_allowed) {
|
return match (decl.mutability, is_local_mutation_allowed) {
|
||||||
(Mutability::Not, LocalMutationIsAllowed::No) |
|
(Mutability::Not, LocalMutationIsAllowed::No) |
|
||||||
(Mutability::Not, LocalMutationIsAllowed::Yes) => Err(place),
|
(Mutability::Not, LocalMutationIsAllowed::ExceptUpvars)
|
||||||
(Mutability::Not, LocalMutationIsAllowed::Move) |
|
=> Err(place),
|
||||||
|
(Mutability::Not, LocalMutationIsAllowed::Yes) |
|
||||||
(Mutability::Mut, _) => self.is_unique(&proj.base),
|
(Mutability::Mut, _) => self.is_unique(&proj.base),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1666,13 +1659,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn borrow_conflicts_with_place(&mut self,
|
|
||||||
borrow: &BorrowData<'tcx>,
|
/// Returns whether an access of kind `access` to `access_place` conflicts with
|
||||||
place: &Place<'tcx>,
|
/// a borrow/full access to `borrow_place` (for deep accesses to mutable
|
||||||
access: ShallowOrDeep)
|
/// locations, this function is symmetric between `borrow_place` & `access_place`).
|
||||||
-> bool
|
fn places_conflict(&mut self,
|
||||||
|
borrow_place: &Place<'tcx>,
|
||||||
|
access_place: &Place<'tcx>,
|
||||||
|
access: ShallowOrDeep)
|
||||||
|
-> bool
|
||||||
{
|
{
|
||||||
debug!("borrow_conflicts_with_place({:?},{:?},{:?})", borrow, place, access);
|
debug!("places_conflict({:?},{:?},{:?})", borrow_place, access_place, access);
|
||||||
|
|
||||||
// Return all the prefixes of `place` in reverse order, including
|
// Return all the prefixes of `place` in reverse order, including
|
||||||
// downcasts.
|
// downcasts.
|
||||||
@ -1694,9 +1691,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let borrow_components = place_elements(&borrow.place);
|
let borrow_components = place_elements(borrow_place);
|
||||||
let access_components = place_elements(place);
|
let access_components = place_elements(access_place);
|
||||||
debug!("borrow_conflicts_with_place: components {:?} / {:?}",
|
debug!("places_conflict: components {:?} / {:?}",
|
||||||
borrow_components, access_components);
|
borrow_components, access_components);
|
||||||
|
|
||||||
let borrow_components = borrow_components.into_iter()
|
let borrow_components = borrow_components.into_iter()
|
||||||
@ -1746,7 +1743,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
// - If we did run out of accesss, the borrow can access a part of it.
|
// - 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) {
|
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.
|
// 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);
|
debug!("places_conflict: {:?} vs. {:?}", borrow_c, access_c);
|
||||||
match (borrow_c, access_c) {
|
match (borrow_c, access_c) {
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
// If we didn't run out of access, the borrow can access all of our
|
// If we didn't run out of access, the borrow can access all of our
|
||||||
@ -1759,7 +1756,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
//
|
//
|
||||||
// FIXME: Differs from AST-borrowck; includes drive-by fix
|
// FIXME: Differs from AST-borrowck; includes drive-by fix
|
||||||
// to #38899. Will probably need back-compat mode flag.
|
// to #38899. Will probably need back-compat mode flag.
|
||||||
debug!("borrow_conflict_with_place: full borrow, CONFLICT");
|
debug!("places_conflict: full borrow, CONFLICT");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
(Some(borrow_c), None) => {
|
(Some(borrow_c), None) => {
|
||||||
@ -1784,7 +1781,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
//
|
//
|
||||||
// e.g. a (mutable) borrow of `a[5]` while we read the
|
// e.g. a (mutable) borrow of `a[5]` while we read the
|
||||||
// array length of `a`.
|
// array length of `a`.
|
||||||
debug!("borrow_conflicts_with_place: implicit field");
|
debug!("places_conflict: implicit field");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1792,7 +1789,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
// e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
|
// 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
|
// prefix thereof - the shallow access can't touch anything behind
|
||||||
// the pointer.
|
// the pointer.
|
||||||
debug!("borrow_conflicts_with_place: shallow access behind ptr");
|
debug!("places_conflict: shallow access behind ptr");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
(ProjectionElem::Deref, ty::TyRef(_, ty::TypeAndMut {
|
(ProjectionElem::Deref, ty::TyRef(_, ty::TypeAndMut {
|
||||||
@ -1803,7 +1800,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
// I'm not sure why we are tracking these borrows - shared
|
// I'm not sure why we are tracking these borrows - shared
|
||||||
// references can *always* be aliased, which means the
|
// references can *always* be aliased, which means the
|
||||||
// permission check already account for this borrow.
|
// permission check already account for this borrow.
|
||||||
debug!("borrow_conflicts_with_place: behind a shared ref");
|
debug!("places_conflict: behind a shared ref");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1836,7 +1833,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
// idea, at least for now, so just give up and
|
// idea, at least for now, so just give up and
|
||||||
// report a conflict. This is unsafe code anyway so
|
// report a conflict. This is unsafe code anyway so
|
||||||
// the user could always use raw pointers.
|
// the user could always use raw pointers.
|
||||||
debug!("borrow_conflicts_with_place: arbitrary -> conflict");
|
debug!("places_conflict: arbitrary -> conflict");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Overlap::EqualOrDisjoint => {
|
Overlap::EqualOrDisjoint => {
|
||||||
@ -1845,7 +1842,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
Overlap::Disjoint => {
|
Overlap::Disjoint => {
|
||||||
// We have proven the borrow disjoint - further
|
// We have proven the borrow disjoint - further
|
||||||
// projections will remain disjoint.
|
// projections will remain disjoint.
|
||||||
debug!("borrow_conflicts_with_place: disjoint");
|
debug!("places_conflict: disjoint");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1874,10 +1871,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
// check for loan restricting path P being used. Accounts for
|
// check for loan restricting path P being used. Accounts for
|
||||||
// borrows of P, P.a.b, etc.
|
// borrows of P, P.a.b, etc.
|
||||||
'next_borrow: for i in flow_state.borrows.elems_incoming() {
|
for i in flow_state.borrows.elems_incoming() {
|
||||||
let borrowed = &data[i];
|
let borrowed = &data[i];
|
||||||
|
|
||||||
if self.borrow_conflicts_with_place(borrowed, place, access) {
|
if self.places_conflict(&borrowed.place, place, access) {
|
||||||
let ctrl = op(self, i, borrowed);
|
let ctrl = op(self, i, borrowed);
|
||||||
if ctrl == Control::Break { return; }
|
if ctrl == Control::Break { return; }
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,17 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// revisions: ast mir
|
||||||
|
//[mir]compile-flags: -Z borrowck=mir
|
||||||
|
|
||||||
fn f(y: Box<isize>) {
|
fn f(y: Box<isize>) {
|
||||||
*y = 5; //~ ERROR cannot assign
|
*y = 5; //[ast]~ ERROR cannot assign
|
||||||
|
//[mir]~^ ERROR cannot assign twice
|
||||||
}
|
}
|
||||||
|
|
||||||
fn g() {
|
fn g() {
|
||||||
let _frob = |q: Box<isize>| { *q = 2; }; //~ ERROR cannot assign
|
let _frob = |q: Box<isize>| { *q = 2; }; //[ast]~ ERROR cannot assign
|
||||||
|
//[mir]~^ ERROR cannot assign twice
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
// revisions: ast mir
|
||||||
|
//[mir]compile-flags: -Z borrowck=mir
|
||||||
|
|
||||||
struct cat {
|
struct cat {
|
||||||
meows : usize,
|
meows : usize,
|
||||||
how_hungry : isize,
|
how_hungry : isize,
|
||||||
@ -22,5 +25,6 @@ fn cat(in_x : usize, in_y : isize) -> cat {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let nyan : cat = cat(52, 99);
|
let nyan : cat = cat(52, 99);
|
||||||
nyan.how_hungry = 0; //~ ERROR cannot assign
|
nyan.how_hungry = 0; //[ast]~ ERROR cannot assign
|
||||||
|
//[mir]~^ ERROR cannot assign
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user