Skip to content

Commit 3ac79c7

Browse files
committed
Auto merge of #53258 - nikomatsakis:issue-53189-optimize-reassignment-immutable-state, r=pnkfelix
optimize reassignment immutable state This is the "simple fix" when it comes to checking for reassignment. We just shoot for compatibility with the AST-based checker. Makes no attempt to solve #21232. I opted for this simpler fix because I didn't want to think about complications [like the ones described here](#21232 (comment)). Let's do some profiling measurements. Fixes #53189 r? @pnkfelix
2 parents bfc3b20 + 58e4b54 commit 3ac79c7

18 files changed

+425
-90
lines changed

src/librustc_mir/borrow_check/mod.rs

+34-51
Original file line numberDiff line numberDiff line change
@@ -798,12 +798,6 @@ enum LocalMutationIsAllowed {
798798
No,
799799
}
800800

801-
struct AccessErrorsReported {
802-
mutability_error: bool,
803-
#[allow(dead_code)]
804-
conflict_error: bool,
805-
}
806-
807801
#[derive(Copy, Clone)]
808802
enum InitializationRequiringAction {
809803
Update,
@@ -1072,7 +1066,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10721066
kind: (ShallowOrDeep, ReadOrWrite),
10731067
is_local_mutation_allowed: LocalMutationIsAllowed,
10741068
flow_state: &Flows<'cx, 'gcx, 'tcx>,
1075-
) -> AccessErrorsReported {
1069+
) {
10761070
let (sd, rw) = kind;
10771071

10781072
if let Activation(_, borrow_index) = rw {
@@ -1082,10 +1076,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10821076
place: {:?} borrow_index: {:?}",
10831077
place_span.0, borrow_index
10841078
);
1085-
return AccessErrorsReported {
1086-
mutability_error: false,
1087-
conflict_error: true,
1088-
};
1079+
return;
10891080
}
10901081
}
10911082

@@ -1097,10 +1088,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
10971088
"access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
10981089
place_span, kind
10991090
);
1100-
return AccessErrorsReported {
1101-
mutability_error: false,
1102-
conflict_error: true,
1103-
};
1091+
return;
11041092
}
11051093

11061094
let mutability_error =
@@ -1122,11 +1110,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11221110
self.access_place_error_reported
11231111
.insert((place_span.0.clone(), place_span.1));
11241112
}
1125-
1126-
AccessErrorsReported {
1127-
mutability_error,
1128-
conflict_error,
1129-
}
11301113
}
11311114

11321115
fn check_access_for_conflict(
@@ -1275,23 +1258,30 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12751258
}
12761259
}
12771260

1278-
let errors_reported = self.access_place(
1261+
// Special case: you can assign a immutable local variable
1262+
// (e.g., `x = ...`) so long as it has never been initialized
1263+
// before (at this point in the flow).
1264+
if let &Place::Local(local) = place_span.0 {
1265+
if let Mutability::Not = self.mir.local_decls[local].mutability {
1266+
// check for reassignments to immutable local variables
1267+
self.check_if_reassignment_to_immutable_state(
1268+
context,
1269+
local,
1270+
place_span,
1271+
flow_state,
1272+
);
1273+
return;
1274+
}
1275+
}
1276+
1277+
// Otherwise, use the normal access permission rules.
1278+
self.access_place(
12791279
context,
12801280
place_span,
12811281
(kind, Write(WriteKind::Mutate)),
1282-
// We want immutable upvars to cause an "assignment to immutable var"
1283-
// error, not an "reassignment of immutable var" error, because the
1284-
// latter can't find a good previous assignment span.
1285-
//
1286-
// There's probably a better way to do this.
1287-
LocalMutationIsAllowed::ExceptUpvars,
1282+
LocalMutationIsAllowed::No,
12881283
flow_state,
12891284
);
1290-
1291-
if !errors_reported.mutability_error {
1292-
// check for reassignments to immutable local variables
1293-
self.check_if_reassignment_to_immutable_state(context, place_span, flow_state);
1294-
}
12951285
}
12961286

12971287
fn consume_rvalue(
@@ -1590,27 +1580,20 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
15901580
fn check_if_reassignment_to_immutable_state(
15911581
&mut self,
15921582
context: Context,
1593-
(place, span): (&Place<'tcx>, Span),
1583+
local: Local,
1584+
place_span: (&Place<'tcx>, Span),
15941585
flow_state: &Flows<'cx, 'gcx, 'tcx>,
15951586
) {
1596-
debug!("check_if_reassignment_to_immutable_state({:?})", place);
1597-
// determine if this path has a non-mut owner (and thus needs checking).
1598-
let err_place = match self.is_mutable(place, LocalMutationIsAllowed::No) {
1599-
Ok(..) => return,
1600-
Err(place) => place,
1601-
};
1602-
debug!(
1603-
"check_if_reassignment_to_immutable_state({:?}) - is an imm local",
1604-
place
1605-
);
1606-
1607-
for i in flow_state.ever_inits.iter_incoming() {
1608-
let init = self.move_data.inits[i];
1609-
let init_place = &self.move_data.move_paths[init.path].place;
1610-
if places_conflict::places_conflict(self.tcx, self.mir, &init_place, place, Deep) {
1611-
self.report_illegal_reassignment(context, (place, span), init.span, err_place);
1612-
break;
1613-
}
1587+
debug!("check_if_reassignment_to_immutable_state({:?})", local);
1588+
1589+
// Check if any of the initializiations of `local` have happened yet:
1590+
let mpi = self.move_data.rev_lookup.find_local(local);
1591+
let init_indices = &self.move_data.init_path_map[mpi];
1592+
let first_init_index = init_indices.iter().find(|ii| flow_state.ever_inits.contains(ii));
1593+
if let Some(&init_index) = first_init_index {
1594+
// And, if so, report an error.
1595+
let init = &self.move_data.inits[init_index];
1596+
self.report_illegal_reassignment(context, place_span, init.span, place_span.0);
16141597
}
16151598
}
16161599

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0381]: use of possibly uninitialized variable: `x`
2+
--> $DIR/assign_mutable_fields.rs:29:10
3+
|
4+
LL | drop(x); //~ ERROR
5+
| ^ use of possibly uninitialized `x`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0381`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Currently, we permit you to assign to individual fields of a mut
12+
// var, but we do not permit you to use the complete var afterwards.
13+
// We hope to fix this at some point.
14+
//
15+
// FIXME(#21232)
16+
17+
fn assign_both_fields_and_use() {
18+
let mut x: (u32, u32);
19+
x.0 = 1;
20+
x.1 = 22;
21+
drop(x.0); //~ ERROR
22+
drop(x.1); //~ ERROR
23+
}
24+
25+
fn assign_both_fields_the_use_var() {
26+
let mut x: (u32, u32);
27+
x.0 = 1;
28+
x.1 = 22;
29+
drop(x); //~ ERROR
30+
}
31+
32+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0381]: use of possibly uninitialized variable: `x.0`
2+
--> $DIR/assign_mutable_fields.rs:21:10
3+
|
4+
LL | drop(x.0); //~ ERROR
5+
| ^^^ use of possibly uninitialized `x.0`
6+
7+
error[E0381]: use of possibly uninitialized variable: `x.1`
8+
--> $DIR/assign_mutable_fields.rs:22:10
9+
|
10+
LL | drop(x.1); //~ ERROR
11+
| ^^^ use of possibly uninitialized `x.1`
12+
13+
error[E0381]: use of possibly uninitialized variable: `x`
14+
--> $DIR/assign_mutable_fields.rs:29:10
15+
|
16+
LL | drop(x); //~ ERROR
17+
| ^ use of possibly uninitialized `x`
18+
19+
error: aborting due to 3 previous errors
20+
21+
For more information about this error, try `rustc --explain E0381`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
2+
--> $DIR/reassignment_immutable_fields.rs:17:5
3+
|
4+
LL | let x: (u32, u32);
5+
| - help: consider changing this to be mutable: `mut x`
6+
LL | x.0 = 1; //~ ERROR
7+
| ^^^^^^^ cannot assign
8+
9+
error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
10+
--> $DIR/reassignment_immutable_fields.rs:18:5
11+
|
12+
LL | let x: (u32, u32);
13+
| - help: consider changing this to be mutable: `mut x`
14+
LL | x.0 = 1; //~ ERROR
15+
LL | x.1 = 22; //~ ERROR
16+
| ^^^^^^^^ cannot assign
17+
18+
error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
19+
--> $DIR/reassignment_immutable_fields.rs:25:5
20+
|
21+
LL | let x: (u32, u32);
22+
| - help: consider changing this to be mutable: `mut x`
23+
LL | x.0 = 1; //~ ERROR
24+
| ^^^^^^^ cannot assign
25+
26+
error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
27+
--> $DIR/reassignment_immutable_fields.rs:26:5
28+
|
29+
LL | let x: (u32, u32);
30+
| - help: consider changing this to be mutable: `mut x`
31+
LL | x.0 = 1; //~ ERROR
32+
LL | x.1 = 22; //~ ERROR
33+
| ^^^^^^^^ cannot assign
34+
35+
error[E0381]: use of possibly uninitialized variable: `x`
36+
--> $DIR/reassignment_immutable_fields.rs:27:10
37+
|
38+
LL | drop(x); //~ ERROR
39+
| ^ use of possibly uninitialized `x`
40+
41+
error: aborting due to 5 previous errors
42+
43+
Some errors occurred: E0381, E0594.
44+
For more information about an error, try `rustc --explain E0381`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This test is currently disallowed, but we hope someday to support it.
12+
//
13+
// FIXME(#21232)
14+
15+
fn assign_both_fields_and_use() {
16+
let x: (u32, u32);
17+
x.0 = 1; //~ ERROR
18+
x.1 = 22; //~ ERROR
19+
drop(x.0); //~ ERROR
20+
drop(x.1); //~ ERROR
21+
}
22+
23+
fn assign_both_fields_the_use_var() {
24+
let x: (u32, u32);
25+
x.0 = 1; //~ ERROR
26+
x.1 = 22; //~ ERROR
27+
drop(x); //~ ERROR
28+
}
29+
30+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
error[E0594]: cannot assign to field `x.0` of immutable binding
2+
--> $DIR/reassignment_immutable_fields.rs:17:5
3+
|
4+
LL | let x: (u32, u32);
5+
| - consider changing this to `mut x`
6+
LL | x.0 = 1; //~ ERROR
7+
| ^^^^^^^ cannot mutably borrow field of immutable binding
8+
9+
error[E0594]: cannot assign to field `x.1` of immutable binding
10+
--> $DIR/reassignment_immutable_fields.rs:18:5
11+
|
12+
LL | let x: (u32, u32);
13+
| - consider changing this to `mut x`
14+
LL | x.0 = 1; //~ ERROR
15+
LL | x.1 = 22; //~ ERROR
16+
| ^^^^^^^^ cannot mutably borrow field of immutable binding
17+
18+
error[E0381]: use of possibly uninitialized variable: `x.0`
19+
--> $DIR/reassignment_immutable_fields.rs:19:10
20+
|
21+
LL | drop(x.0); //~ ERROR
22+
| ^^^ use of possibly uninitialized `x.0`
23+
24+
error[E0381]: use of possibly uninitialized variable: `x.1`
25+
--> $DIR/reassignment_immutable_fields.rs:20:10
26+
|
27+
LL | drop(x.1); //~ ERROR
28+
| ^^^ use of possibly uninitialized `x.1`
29+
30+
error[E0594]: cannot assign to field `x.0` of immutable binding
31+
--> $DIR/reassignment_immutable_fields.rs:25:5
32+
|
33+
LL | let x: (u32, u32);
34+
| - consider changing this to `mut x`
35+
LL | x.0 = 1; //~ ERROR
36+
| ^^^^^^^ cannot mutably borrow field of immutable binding
37+
38+
error[E0594]: cannot assign to field `x.1` of immutable binding
39+
--> $DIR/reassignment_immutable_fields.rs:26:5
40+
|
41+
LL | let x: (u32, u32);
42+
| - consider changing this to `mut x`
43+
LL | x.0 = 1; //~ ERROR
44+
LL | x.1 = 22; //~ ERROR
45+
| ^^^^^^^^ cannot mutably borrow field of immutable binding
46+
47+
error[E0381]: use of possibly uninitialized variable: `x`
48+
--> $DIR/reassignment_immutable_fields.rs:27:10
49+
|
50+
LL | drop(x); //~ ERROR
51+
| ^ use of possibly uninitialized `x`
52+
53+
error: aborting due to 7 previous errors
54+
55+
Some errors occurred: E0381, E0594.
56+
For more information about an error, try `rustc --explain E0381`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0594]: cannot assign to `x.a`, as `x` is not declared as mutable
2+
--> $DIR/reassignment_immutable_fields_overlapping.rs:22:5
3+
|
4+
LL | let x: Foo;
5+
| - help: consider changing this to be mutable: `mut x`
6+
LL | x.a = 1; //~ ERROR
7+
| ^^^^^^^ cannot assign
8+
9+
error[E0594]: cannot assign to `x.b`, as `x` is not declared as mutable
10+
--> $DIR/reassignment_immutable_fields_overlapping.rs:23:5
11+
|
12+
LL | let x: Foo;
13+
| - help: consider changing this to be mutable: `mut x`
14+
LL | x.a = 1; //~ ERROR
15+
LL | x.b = 22; //~ ERROR
16+
| ^^^^^^^^ cannot assign
17+
18+
error: aborting due to 2 previous errors
19+
20+
For more information about this error, try `rustc --explain E0594`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This should never be allowed -- `foo.a` and `foo.b` are
12+
// overlapping, so since `x` is not `mut` we should not permit
13+
// reassignment.
14+
15+
union Foo {
16+
a: u32,
17+
b: u32,
18+
}
19+
20+
unsafe fn overlapping_fields() {
21+
let x: Foo;
22+
x.a = 1; //~ ERROR
23+
x.b = 22; //~ ERROR
24+
}
25+
26+
fn main() { }

0 commit comments

Comments
 (0)