Skip to content

Commit 5a2b590

Browse files
committed
Track unused mutable variables across closures
1 parent 3e423d0 commit 5a2b590

File tree

7 files changed

+189
-49
lines changed

7 files changed

+189
-49
lines changed

src/librustc/ich/impls_mir.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,11 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Literal<'gcx> {
563563

564564
impl_stable_hash_for!(struct mir::Location { block, statement_index });
565565

566+
impl_stable_hash_for!(struct mir::BorrowCheckResult<'tcx> {
567+
closure_requirements,
568+
used_mut_upvars
569+
});
570+
566571
impl_stable_hash_for!(struct mir::ClosureRegionRequirements<'tcx> {
567572
num_external_vids,
568573
outlives_requirements

src/librustc/mir/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_data_structures::indexed_vec::{IndexVec, Idx};
2121
use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
2222
use rustc_data_structures::control_flow_graph::{GraphPredecessors, GraphSuccessors};
2323
use rustc_data_structures::control_flow_graph::ControlFlowGraph;
24+
use rustc_data_structures::small_vec::SmallVec;
2425
use rustc_serialize as serialize;
2526
use hir::def::CtorKind;
2627
use hir::def_id::DefId;
@@ -2043,6 +2044,12 @@ pub struct GeneratorLayout<'tcx> {
20432044
pub fields: Vec<LocalDecl<'tcx>>,
20442045
}
20452046

2047+
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
2048+
pub struct BorrowCheckResult<'gcx> {
2049+
pub closure_requirements: Option<ClosureRegionRequirements<'gcx>>,
2050+
pub used_mut_upvars: SmallVec<[Field; 8]>,
2051+
}
2052+
20462053
/// After we borrow check a closure, we are left with various
20472054
/// requirements that we have inferred between the free regions that
20482055
/// appear in the closure's signature or on its field types. These

src/librustc/ty/maps/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ define_maps! { <'tcx>
211211

212212
/// Borrow checks the function body. If this is a closure, returns
213213
/// additional requirements that the closure's creator must verify.
214-
[] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements<'tcx>>,
214+
[] fn mir_borrowck: MirBorrowCheck(DefId) -> mir::BorrowCheckResult<'tcx>,
215215

216216
/// Gets a complete map from all types to their inherent impls.
217217
/// Not meant to be used directly outside of coherence.

src/librustc_mir/borrow_check/mod.rs

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,16 @@ use rustc::infer::InferCtxt;
1818
use rustc::ty::{self, ParamEnv, TyCtxt};
1919
use rustc::ty::maps::Providers;
2020
use rustc::lint::builtin::UNUSED_MUT;
21-
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, ClearCrossCrate, Local};
22-
use rustc::mir::{Location, Place, Mir, Mutability, Operand, Projection, ProjectionElem};
23-
use rustc::mir::{Rvalue, Field, Statement, StatementKind, Terminator, TerminatorKind};
24-
use rustc::mir::ClosureRegionRequirements;
21+
use rustc::mir::{AssertMessage, AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
22+
use rustc::mir::{ClearCrossCrate, Local, Location, Place, Mir, Mutability, Operand};
23+
use rustc::mir::{Projection, ProjectionElem, Rvalue, Field, Statement, StatementKind};
24+
use rustc::mir::{Terminator, TerminatorKind};
2525

2626
use rustc_data_structures::control_flow_graph::dominators::Dominators;
2727
use rustc_data_structures::fx::FxHashSet;
2828
use rustc_data_structures::indexed_set::IdxSetBuf;
2929
use rustc_data_structures::indexed_vec::Idx;
30+
use rustc_data_structures::small_vec::SmallVec;
3031

3132
use std::rc::Rc;
3233

@@ -70,12 +71,15 @@ pub fn provide(providers: &mut Providers) {
7071
fn mir_borrowck<'a, 'tcx>(
7172
tcx: TyCtxt<'a, 'tcx, 'tcx>,
7273
def_id: DefId,
73-
) -> Option<ClosureRegionRequirements<'tcx>> {
74+
) -> BorrowCheckResult<'tcx> {
7475
let input_mir = tcx.mir_validated(def_id);
7576
debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
7677

7778
if !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.use_mir_borrowck() {
78-
return None;
79+
return BorrowCheckResult {
80+
closure_requirements: None,
81+
used_mut_upvars: SmallVec::new(),
82+
};
7983
}
8084

8185
let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
@@ -91,7 +95,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
9195
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
9296
input_mir: &Mir<'gcx>,
9397
def_id: DefId,
94-
) -> Option<ClosureRegionRequirements<'gcx>> {
98+
) -> BorrowCheckResult<'gcx> {
9599
let tcx = infcx.tcx;
96100
let attributes = tcx.get_attrs(def_id);
97101
let param_env = tcx.param_env(def_id);
@@ -239,6 +243,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
239243
moved_error_reported: FxHashSet(),
240244
nonlexical_regioncx: opt_regioncx,
241245
used_mut: FxHashSet(),
246+
used_mut_upvars: SmallVec::new(),
242247
nonlexical_cause_info: None,
243248
borrow_set,
244249
dominators,
@@ -254,7 +259,28 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
254259

255260
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
256261

257-
opt_closure_req
262+
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
263+
264+
for local in mbcx.mir.mut_vars_iter().filter(|local| !mbcx.used_mut.contains(local)) {
265+
if let ClearCrossCrate::Set(ref vsi) = mbcx.mir.visibility_scope_info {
266+
let source_info = mbcx.mir.local_decls[local].source_info;
267+
let mut_span = tcx.sess.codemap().span_until_non_whitespace(source_info.span);
268+
269+
tcx.struct_span_lint_node(
270+
UNUSED_MUT,
271+
vsi[source_info.scope].lint_root,
272+
source_info.span,
273+
"variable does not need to be mutable"
274+
)
275+
.span_suggestion_short(mut_span, "remove this `mut`", "".to_owned())
276+
.emit();
277+
}
278+
}
279+
280+
BorrowCheckResult {
281+
closure_requirements: opt_closure_req,
282+
used_mut_upvars: mbcx.used_mut_upvars,
283+
}
258284
}
259285

260286
#[allow(dead_code)]
@@ -292,6 +318,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
292318
/// This field keeps track of all the local variables that are declared mut and are mutated.
293319
/// Used for the warning issued by an unused mutable local variable.
294320
used_mut: FxHashSet<Local>,
321+
/// If the function we're checking is a closure, then we'll need to report back the list of
322+
/// mutable upvars that have been used. This field keeps track of them.
323+
used_mut_upvars: SmallVec<[Field; 8]>,
295324
/// Non-lexical region inference context, if NLL is enabled. This
296325
/// contains the results from region inference and lets us e.g.
297326
/// find out which CFG points are contained in each borrow region.
@@ -439,22 +468,6 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
439468

440469
self.check_activations(location, span, flow_state);
441470

442-
for local in self.mir.mut_vars_iter().filter(|local| !self.used_mut.contains(local)) {
443-
if let ClearCrossCrate::Set(ref vsi) = self.mir.visibility_scope_info {
444-
let source_info = self.mir.local_decls[local].source_info;
445-
let mut_span = self.tcx.sess.codemap().span_until_non_whitespace(source_info.span);
446-
447-
self.tcx.struct_span_lint_node(
448-
UNUSED_MUT,
449-
vsi[source_info.scope].lint_root,
450-
source_info.span,
451-
"variable does not need to be mutable"
452-
)
453-
.span_suggestion_short(mut_span, "remove this `mut`", "".to_owned())
454-
.emit();
455-
}
456-
}
457-
458471
match term.kind {
459472
TerminatorKind::SwitchInt {
460473
ref discr,
@@ -1118,9 +1131,33 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
11181131
// `NullOp::Box`?
11191132
}
11201133

1121-
Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands {
1122-
self.consume_operand(context, (operand, span), flow_state);
1123-
},
1134+
Rvalue::Aggregate(ref aggregate_kind, ref operands) => {
1135+
// We need to report back the list of mutable upvars that were
1136+
// moved into the closure and subsequently used by the closure,
1137+
// in order to populate our used_mut set.
1138+
if let AggregateKind::Closure(def_id, _) = &**aggregate_kind {
1139+
let BorrowCheckResult { used_mut_upvars, .. } = self.tcx.mir_borrowck(*def_id);
1140+
for field in used_mut_upvars {
1141+
match operands[field.index()] {
1142+
Operand::Move(Place::Local(local)) => {
1143+
self.used_mut.insert(local);
1144+
}
1145+
Operand::Move(Place::Projection(ref proj)) => {
1146+
if let Some(field) = self.is_upvar_field_projection(&proj.base) {
1147+
self.used_mut_upvars.push(field);
1148+
}
1149+
}
1150+
Operand::Move(Place::Static(..)) |
1151+
Operand::Copy(..) |
1152+
Operand::Constant(..) => {}
1153+
}
1154+
}
1155+
}
1156+
1157+
for operand in operands {
1158+
self.consume_operand(context, (operand, span), flow_state);
1159+
}
1160+
}
11241161
}
11251162
}
11261163

@@ -1652,8 +1689,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16521689
err.emit();
16531690
},
16541691
Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
1655-
if let Place::Local(local) = *place {
1656-
self.used_mut.insert(local);
1692+
match place {
1693+
Place::Local(local) => {
1694+
self.used_mut.insert(*local);
1695+
}
1696+
Place::Projection(ref proj) => {
1697+
if let Some(field) = self.is_upvar_field_projection(&proj.base) {
1698+
self.used_mut_upvars.push(field);
1699+
}
1700+
}
1701+
Place::Static(..) => {}
16571702
}
16581703
if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) {
16591704
error_reported = true;

src/librustc_mir/borrow_check/nll/type_check/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1426,7 +1426,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
14261426
// these extra requirements are basically like where
14271427
// clauses on the struct.
14281428
AggregateKind::Closure(def_id, substs) => {
1429-
if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) {
1429+
if let Some(closure_region_requirements) =
1430+
tcx.mir_borrowck(*def_id).closure_requirements
1431+
{
14301432
closure_region_requirements.apply_requirements(
14311433
self.infcx,
14321434
self.body_id,

src/test/compile-fail/lint-unused-mut-variables.rs

Lines changed: 43 additions & 18 deletions
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: lexical nll
12+
#![cfg_attr(nll, feature(nll))]
13+
1114
// Exercise the unused_mut attribute in some positive and negative cases
1215

1316
#![allow(unused_assignments)]
@@ -18,53 +21,68 @@
1821

1922
fn main() {
2023
// negative cases
21-
let mut a = 3; //~ ERROR: variable does not need to be mutable
22-
let mut a = 2; //~ ERROR: variable does not need to be mutable
23-
let mut b = 3; //~ ERROR: variable does not need to be mutable
24-
let mut a = vec![3]; //~ ERROR: variable does not need to be mutable
25-
let (mut a, b) = (1, 2); //~ ERROR: variable does not need to be mutable
26-
let mut a; //~ ERROR: variable does not need to be mutable
24+
let mut a = 3; //[lexical]~ ERROR: variable does not need to be mutable
25+
//[nll]~^ ERROR: variable does not need to be mutable
26+
let mut a = 2; //[lexical]~ ERROR: variable does not need to be mutable
27+
//[nll]~^ ERROR: variable does not need to be mutable
28+
let mut b = 3; //[lexical]~ ERROR: variable does not need to be mutable
29+
//[nll]~^ ERROR: variable does not need to be mutable
30+
let mut a = vec![3]; //[lexical]~ ERROR: variable does not need to be mutable
31+
//[nll]~^ ERROR: variable does not need to be mutable
32+
let (mut a, b) = (1, 2); //[lexical]~ ERROR: variable does not need to be mutable
33+
//[nll]~^ ERROR: variable does not need to be mutable
34+
let mut a; //[lexical]~ ERROR: variable does not need to be mutable
35+
//[nll]~^ ERROR: variable does not need to be mutable
2736
a = 3;
2837

29-
let mut b; //~ ERROR: variable does not need to be mutable
38+
let mut b; //[lexical]~ ERROR: variable does not need to be mutable
39+
//[nll]~^ ERROR: variable does not need to be mutable
3040
if true {
3141
b = 3;
3242
} else {
3343
b = 4;
3444
}
3545

3646
match 30 {
37-
mut x => {} //~ ERROR: variable does not need to be mutable
47+
mut x => {} //[lexical]~ ERROR: variable does not need to be mutable
48+
//[nll]~^ ERROR: variable does not need to be mutable
3849
}
3950
match (30, 2) {
40-
(mut x, 1) | //~ ERROR: variable does not need to be mutable
51+
(mut x, 1) | //[lexical]~ ERROR: variable does not need to be mutable
52+
//[nll]~^ ERROR: variable does not need to be mutable
4153
(mut x, 2) |
4254
(mut x, 3) => {
4355
}
4456
_ => {}
4557
}
4658

47-
let x = |mut y: isize| 10; //~ ERROR: variable does not need to be mutable
48-
fn what(mut foo: isize) {} //~ ERROR: variable does not need to be mutable
59+
let x = |mut y: isize| 10; //[lexical]~ ERROR: variable does not need to be mutable
60+
//[nll]~^ ERROR: variable does not need to be mutable
61+
fn what(mut foo: isize) {} //[lexical]~ ERROR: variable does not need to be mutable
62+
//[nll]~^ ERROR: variable does not need to be mutable
4963

50-
let mut a = &mut 5; //~ ERROR: variable does not need to be mutable
64+
let mut a = &mut 5; //[lexical]~ ERROR: variable does not need to be mutable
65+
//[nll]~^ ERROR: variable does not need to be mutable
5166
*a = 4;
5267

5368
let mut a = 5;
54-
let mut b = (&mut a,);
55-
*b.0 = 4; //~^ ERROR: variable does not need to be mutable
69+
let mut b = (&mut a,); //[lexical]~ ERROR: variable does not need to be mutable
70+
*b.0 = 4; //[nll]~^ ERROR: variable does not need to be mutable
5671

57-
let mut x = &mut 1; //~ ERROR: variable does not need to be mutable
72+
let mut x = &mut 1; //[lexical]~ ERROR: variable does not need to be mutable
73+
//[nll]~^ ERROR: variable does not need to be mutable
5874
let mut f = || {
5975
*x += 1;
6076
};
6177
f();
6278

6379
fn mut_ref_arg(mut arg : &mut [u8]) -> &mut [u8] {
64-
&mut arg[..] //~^ ERROR: variable does not need to be mutable
80+
&mut arg[..] //[lexical]~^ ERROR: variable does not need to be mutable
81+
//[nll]~^^ ERROR: variable does not need to be mutable
6582
}
6683

67-
let mut v : &mut Vec<()> = &mut vec![]; //~ ERROR: variable does not need to be mutable
84+
let mut v : &mut Vec<()> = &mut vec![]; //[lexical]~ ERROR: variable does not need to be mutable
85+
//[nll]~^ ERROR: variable does not need to be mutable
6886
v.push(());
6987

7088
// positive cases
@@ -76,6 +94,12 @@ fn main() {
7694
callback(|| {
7795
a.push(3);
7896
});
97+
let mut a = Vec::new();
98+
callback(|| {
99+
callback(|| {
100+
a.push(3);
101+
});
102+
});
79103
let (mut a, b) = (1, 2);
80104
a = 34;
81105

@@ -116,5 +140,6 @@ fn foo(mut a: isize) {
116140
fn bar() {
117141
#[allow(unused_mut)]
118142
let mut a = 3;
119-
let mut b = vec![2]; //~ ERROR: variable does not need to be mutable
143+
let mut b = vec![2]; //[lexical]~ ERROR: variable does not need to be mutable
144+
//[nll]~^ ERROR: variable does not need to be mutable
120145
}

0 commit comments

Comments
 (0)