Skip to content

Commit b64ddec

Browse files
committed
use places_conflict to handle reassignment
This fixes the handling of reassignment of struct fields.
1 parent 97c58ed commit b64ddec

File tree

3 files changed

+56
-52
lines changed

3 files changed

+56
-52
lines changed

src/librustc_mir/borrow_check/mod.rs

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,10 @@ enum WriteKind {
605605
/// - Take flow state into consideration in `is_assignable()` for local variables
606606
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
607607
enum LocalMutationIsAllowed {
608-
Move,
609608
Yes,
609+
/// We want use of immutable upvars to cause a "write to immutable upvar"
610+
/// error, not an "reassignment" error.
611+
ExceptUpvars,
610612
No
611613
}
612614

@@ -802,7 +804,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
802804
context,
803805
place_span,
804806
(kind, Write(WriteKind::Mutate)),
805-
LocalMutationIsAllowed::Yes,
807+
// We want immutable upvars to cause an "assignment to immutable var"
808+
// error, not an "reassignment of immutable var" error, because the
809+
// latter can't find a good previous assignment span.
810+
//
811+
// There's probably a better way to do this.
812+
LocalMutationIsAllowed::ExceptUpvars,
806813
flow_state,
807814
);
808815

@@ -922,7 +929,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
922929
context,
923930
(place, span),
924931
(Deep, Write(WriteKind::Move)),
925-
LocalMutationIsAllowed::Move,
932+
LocalMutationIsAllowed::Yes,
926933
flow_state,
927934
);
928935

@@ -1009,34 +1016,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10091016
(place, span): (&Place<'tcx>, Span),
10101017
flow_state: &Flows<'cx, 'gcx, 'tcx>,
10111018
) {
1012-
let move_data = self.move_data;
1013-
1019+
debug!("check_if_reassignment_to_immutable_state({:?})", place);
10141020
// determine if this path has a non-mut owner (and thus needs checking).
10151021
if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) {
10161022
return;
10171023
}
1018-
1019-
match self.move_path_closest_to(place) {
1020-
Ok(mpi) => for ii in &move_data.init_path_map[mpi] {
1021-
if flow_state.ever_inits.contains(ii) {
1022-
let first_assign_span = self.move_data.inits[*ii].span;
1023-
self.report_illegal_reassignment(context, (place, span), first_assign_span);
1024-
break;
1025-
}
1026-
},
1027-
Err(NoMovePathFound::ReachedStatic) => {
1028-
let item_msg = match self.describe_place(place) {
1029-
Some(name) => format!("immutable static item `{}`", name),
1030-
None => "immutable static item".to_owned(),
1031-
};
1032-
self.tcx.sess.delay_span_bug(
1033-
span,
1034-
&format!(
1035-
"cannot assign to {}, should have been caught by \
1036-
`check_access_permissions()`",
1037-
item_msg
1038-
),
1039-
);
1024+
debug!("check_if_reassignment_to_immutable_state({:?}) - is an imm local", place);
1025+
1026+
for i in flow_state.ever_inits.elems_incoming() {
1027+
let init = self.move_data.inits[i];
1028+
let init_place = &self.move_data.move_paths[init.path].place;
1029+
if self.places_conflict(&init_place, place, Deep) {
1030+
self.report_illegal_reassignment(context, (place, span), init.span);
1031+
break;
10401032
}
10411033
}
10421034
}
@@ -1341,7 +1333,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
13411333
match local.mutability {
13421334
Mutability::Not => match is_local_mutation_allowed {
13431335
LocalMutationIsAllowed::Yes |
1344-
LocalMutationIsAllowed::Move => Ok(()),
1336+
LocalMutationIsAllowed::ExceptUpvars => Ok(()),
13451337
LocalMutationIsAllowed::No => Err(place),
13461338
},
13471339
Mutability::Mut => Ok(()),
@@ -1410,8 +1402,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
14101402
decl, is_local_mutation_allowed, place);
14111403
return match (decl.mutability, is_local_mutation_allowed) {
14121404
(Mutability::Not, LocalMutationIsAllowed::No) |
1413-
(Mutability::Not, LocalMutationIsAllowed::Yes) => Err(place),
1414-
(Mutability::Not, LocalMutationIsAllowed::Move) |
1405+
(Mutability::Not, LocalMutationIsAllowed::ExceptUpvars)
1406+
=> Err(place),
1407+
(Mutability::Not, LocalMutationIsAllowed::Yes) |
14151408
(Mutability::Mut, _) => self.is_unique(&proj.base),
14161409
};
14171410
}
@@ -1666,13 +1659,17 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16661659
}
16671660
}
16681661
}
1669-
fn borrow_conflicts_with_place(&mut self,
1670-
borrow: &BorrowData<'tcx>,
1671-
place: &Place<'tcx>,
1672-
access: ShallowOrDeep)
1673-
-> bool
1662+
1663+
/// Returns whether an access of kind `access` to `access_place` conflicts with
1664+
/// a borrow/full access to `borrow_place` (for deep accesses to mutable
1665+
/// locations, this function is symmetric between `borrow_place` & `access_place`).
1666+
fn places_conflict(&mut self,
1667+
borrow_place: &Place<'tcx>,
1668+
access_place: &Place<'tcx>,
1669+
access: ShallowOrDeep)
1670+
-> bool
16741671
{
1675-
debug!("borrow_conflicts_with_place({:?},{:?},{:?})", borrow, place, access);
1672+
debug!("places_conflict({:?},{:?},{:?})", borrow_place, access_place, access);
16761673

16771674
// Return all the prefixes of `place` in reverse order, including
16781675
// downcasts.
@@ -1694,9 +1691,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16941691
}
16951692
}
16961693

1697-
let borrow_components = place_elements(&borrow.place);
1698-
let access_components = place_elements(place);
1699-
debug!("borrow_conflicts_with_place: components {:?} / {:?}",
1694+
let borrow_components = place_elements(borrow_place);
1695+
let access_components = place_elements(access_place);
1696+
debug!("places_conflict: components {:?} / {:?}",
17001697
borrow_components, access_components);
17011698

17021699
let borrow_components = borrow_components.into_iter()
@@ -1746,7 +1743,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17461743
// - If we did run out of accesss, the borrow can access a part of it.
17471744
for (borrow_c, access_c) in borrow_components.zip(access_components) {
17481745
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
1749-
debug!("borrow_conflicts_with_place: {:?} vs. {:?}", borrow_c, access_c);
1746+
debug!("places_conflict: {:?} vs. {:?}", borrow_c, access_c);
17501747
match (borrow_c, access_c) {
17511748
(None, _) => {
17521749
// 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> {
17591756
//
17601757
// FIXME: Differs from AST-borrowck; includes drive-by fix
17611758
// to #38899. Will probably need back-compat mode flag.
1762-
debug!("borrow_conflict_with_place: full borrow, CONFLICT");
1759+
debug!("places_conflict: full borrow, CONFLICT");
17631760
return true;
17641761
}
17651762
(Some(borrow_c), None) => {
@@ -1784,15 +1781,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
17841781
//
17851782
// e.g. a (mutable) borrow of `a[5]` while we read the
17861783
// array length of `a`.
1787-
debug!("borrow_conflicts_with_place: implicit field");
1784+
debug!("places_conflict: implicit field");
17881785
return false;
17891786
}
17901787

17911788
(ProjectionElem::Deref, _, Shallow(None)) => {
17921789
// e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
17931790
// prefix thereof - the shallow access can't touch anything behind
17941791
// the pointer.
1795-
debug!("borrow_conflicts_with_place: shallow access behind ptr");
1792+
debug!("places_conflict: shallow access behind ptr");
17961793
return false;
17971794
}
17981795
(ProjectionElem::Deref, ty::TyRef(_, ty::TypeAndMut {
@@ -1803,7 +1800,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18031800
// I'm not sure why we are tracking these borrows - shared
18041801
// references can *always* be aliased, which means the
18051802
// permission check already account for this borrow.
1806-
debug!("borrow_conflicts_with_place: behind a shared ref");
1803+
debug!("places_conflict: behind a shared ref");
18071804
return false;
18081805
}
18091806

@@ -1836,7 +1833,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18361833
// idea, at least for now, so just give up and
18371834
// report a conflict. This is unsafe code anyway so
18381835
// the user could always use raw pointers.
1839-
debug!("borrow_conflicts_with_place: arbitrary -> conflict");
1836+
debug!("places_conflict: arbitrary -> conflict");
18401837
return true;
18411838
}
18421839
Overlap::EqualOrDisjoint => {
@@ -1845,7 +1842,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18451842
Overlap::Disjoint => {
18461843
// We have proven the borrow disjoint - further
18471844
// projections will remain disjoint.
1848-
debug!("borrow_conflicts_with_place: disjoint");
1845+
debug!("places_conflict: disjoint");
18491846
return false;
18501847
}
18511848
}
@@ -1874,10 +1871,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18741871

18751872
// check for loan restricting path P being used. Accounts for
18761873
// borrows of P, P.a.b, etc.
1877-
'next_borrow: for i in flow_state.borrows.elems_incoming() {
1874+
for i in flow_state.borrows.elems_incoming() {
18781875
let borrowed = &data[i];
18791876

1880-
if self.borrow_conflicts_with_place(borrowed, place, access) {
1877+
if self.places_conflict(&borrowed.place, place, access) {
18811878
let ctrl = op(self, i, borrowed);
18821879
if ctrl == Control::Break { return; }
18831880
}

src/test/compile-fail/immut-function-arguments.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// revisions: ast mir
12+
//[mir]compile-flags: -Z borrowck=mir
1113

1214
fn f(y: Box<isize>) {
13-
*y = 5; //~ ERROR cannot assign
15+
*y = 5; //[ast]~ ERROR cannot assign
16+
//[mir]~^ ERROR cannot assign twice
1417
}
1518

1619
fn g() {
17-
let _frob = |q: Box<isize>| { *q = 2; }; //~ ERROR cannot assign
18-
20+
let _frob = |q: Box<isize>| { *q = 2; }; //[ast]~ ERROR cannot assign
21+
//[mir]~^ ERROR cannot assign twice
1922
}
2023

2124
fn main() {}

src/test/compile-fail/mutable-class-fields.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// revisions: ast mir
12+
//[mir]compile-flags: -Z borrowck=mir
13+
1114
struct cat {
1215
meows : usize,
1316
how_hungry : isize,
@@ -22,5 +25,6 @@ fn cat(in_x : usize, in_y : isize) -> cat {
2225

2326
fn main() {
2427
let nyan : cat = cat(52, 99);
25-
nyan.how_hungry = 0; //~ ERROR cannot assign
28+
nyan.how_hungry = 0; //[ast]~ ERROR cannot assign
29+
//[mir]~^ ERROR cannot assign
2630
}

0 commit comments

Comments
 (0)