Skip to content

Commit 4df1278

Browse files
committed
rustc: Remove used_mut_nodes from TyCtxt
This updates the borrowck query to return a result, and this result is then used to incrementally check for unused mutable nodes given sets of all the used mutable nodes. Closes rust-lang#42384
1 parent af7de7b commit 4df1278

File tree

16 files changed

+220
-124
lines changed

16 files changed

+220
-124
lines changed

src/Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub mod lint;
106106

107107
pub mod middle {
108108
pub mod allocator;
109+
pub mod borrowck;
109110
pub mod expr_use_visitor;
110111
pub mod const_val;
111112
pub mod cstore;

src/librustc/lint/builtin.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ declare_lint! {
222222
"unnecessary use of an `unsafe` block"
223223
}
224224

225+
declare_lint! {
226+
pub UNUSED_MUT,
227+
Warn,
228+
"detect mut variables which don't need to be mutable"
229+
}
230+
225231
/// Does nothing as a lint pass, but registers some `Lint`s
226232
/// which are used by other parts of the compiler.
227233
#[derive(Copy, Clone)]
@@ -263,7 +269,8 @@ impl LintPass for HardwiredLints {
263269
PARENTHESIZED_PARAMS_IN_TYPES_AND_MODULES,
264270
LATE_BOUND_LIFETIME_ARGUMENTS,
265271
DEPRECATED,
266-
UNUSED_UNSAFE
272+
UNUSED_UNSAFE,
273+
UNUSED_MUT
267274
)
268275
}
269276
}

src/librustc/middle/borrowck.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
use ich::StableHashingContext;
12+
use hir::HirId;
13+
use util::nodemap::FxHashSet;
14+
15+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher,
16+
StableHasherResult};
17+
18+
pub struct BorrowCheckResult {
19+
pub used_mut_nodes: FxHashSet<HirId>,
20+
}
21+
22+
impl<'gcx> HashStable<StableHashingContext<'gcx>> for BorrowCheckResult {
23+
fn hash_stable<W: StableHasherResult>(&self,
24+
hcx: &mut StableHashingContext<'gcx>,
25+
hasher: &mut StableHasher<W>) {
26+
let BorrowCheckResult {
27+
ref used_mut_nodes,
28+
} = *self;
29+
used_mut_nodes.hash_stable(hcx, hasher);
30+
}
31+
}

src/librustc/ty/context.rs

-6
Original file line numberDiff line numberDiff line change
@@ -898,11 +898,6 @@ pub struct GlobalCtxt<'tcx> {
898898

899899
pub inhabitedness_cache: RefCell<FxHashMap<Ty<'tcx>, DefIdForest>>,
900900

901-
/// Set of nodes which mark locals as mutable which end up getting used at
902-
/// some point. Local variable definitions not in this set can be warned
903-
/// about.
904-
pub used_mut_nodes: RefCell<NodeSet>,
905-
906901
/// Caches the results of trait selection. This cache is used
907902
/// for things that do not have to do with the parameters in scope.
908903
pub selection_cache: traits::SelectionCache<'tcx>,
@@ -1185,7 +1180,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
11851180
rcache: RefCell::new(FxHashMap()),
11861181
normalized_cache: RefCell::new(FxHashMap()),
11871182
inhabitedness_cache: RefCell::new(FxHashMap()),
1188-
used_mut_nodes: RefCell::new(NodeSet()),
11891183
selection_cache: traits::SelectionCache::new(),
11901184
evaluation_cache: traits::EvaluationCache::new(),
11911185
rvalue_promotable_to_static: RefCell::new(NodeMap()),

src/librustc/ty/maps/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use hir::def::{Def, Export};
1515
use hir::{self, TraitCandidate, ItemLocalId};
1616
use hir::svh::Svh;
1717
use lint;
18+
use middle::borrowck::BorrowCheckResult;
1819
use middle::const_val;
1920
use middle::cstore::{ExternCrate, LinkagePreference, NativeLibrary,
2021
ExternBodyNestedBodies};
@@ -183,7 +184,7 @@ define_maps! { <'tcx>
183184

184185
[] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),
185186

186-
[] fn borrowck: BorrowCheck(DefId) -> (),
187+
[] fn borrowck: BorrowCheck(DefId) -> Rc<BorrowCheckResult>,
187188
// FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead?
188189
[] fn mir_borrowck: MirBorrowCheck(DefId) -> (),
189190

src/librustc_borrowck/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ syntax = { path = "../libsyntax" }
1515
syntax_pos = { path = "../libsyntax_pos" }
1616
graphviz = { path = "../libgraphviz" }
1717
rustc = { path = "../librustc" }
18+
rustc_back = { path = "../librustc_back" }
1819
rustc_mir = { path = "../librustc_mir" }
1920
rustc_errors = { path = "../librustc_errors" }

src/librustc_borrowck/borrowck/check_loans.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
770770
let lp = opt_loan_path(&assignee_cmt).unwrap();
771771
self.move_data.each_assignment_of(assignment_id, &lp, |assign| {
772772
if assignee_cmt.mutbl.is_mutable() {
773-
self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
773+
let hir_id = self.bccx.tcx.hir.node_to_hir_id(local_id);
774+
self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
774775
} else {
775776
self.bccx.report_reassigned_immutable_variable(
776777
assignment_span,

src/librustc_borrowck/borrowck/gather_loans/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -442,13 +442,13 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
442442
wrapped_path = match current_path.kind {
443443
LpVar(local_id) => {
444444
if !through_borrow {
445-
self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
445+
let hir_id = self.bccx.tcx.hir.node_to_hir_id(local_id);
446+
self.bccx.used_mut_nodes.borrow_mut().insert(hir_id);
446447
}
447448
None
448449
}
449450
LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
450-
let local_id = self.tcx().hir.hir_to_node_id(var_id);
451-
self.tcx().used_mut_nodes.borrow_mut().insert(local_id);
451+
self.bccx.used_mut_nodes.borrow_mut().insert(var_id);
452452
None
453453
}
454454
LpExtend(ref base, mc::McInherited, LpDeref(pointer_kind)) |

src/librustc_borrowck/borrowck/mod.rs

+37-6
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ pub use self::MovedValueUseKind::*;
2020

2121
use self::InteriorKind::*;
2222

23+
use rustc::hir::HirId;
2324
use rustc::hir::map as hir_map;
2425
use rustc::hir::map::blocks::FnLikeNode;
2526
use rustc::cfg;
2627
use rustc::middle::dataflow::DataFlowContext;
2728
use rustc::middle::dataflow::BitwiseOperator;
2829
use rustc::middle::dataflow::DataFlowOperator;
2930
use rustc::middle::dataflow::KillFrom;
31+
use rustc::middle::borrowck::BorrowCheckResult;
3032
use rustc::hir::def_id::{DefId, DefIndex};
3133
use rustc::middle::expr_use_visitor as euv;
3234
use rustc::middle::mem_categorization as mc;
@@ -37,7 +39,9 @@ use rustc::middle::free_region::RegionRelations;
3739
use rustc::ty::{self, Ty, TyCtxt};
3840
use rustc::ty::maps::Providers;
3941
use rustc_mir::util::borrowck_errors::{BorrowckErrors, Origin};
42+
use rustc::util::nodemap::FxHashSet;
4043

44+
use std::cell::RefCell;
4145
use std::fmt;
4246
use std::rc::Rc;
4347
use std::hash::{Hash, Hasher};
@@ -54,6 +58,8 @@ pub mod gather_loans;
5458

5559
pub mod move_data;
5660

61+
mod unused;
62+
5763
#[derive(Clone, Copy)]
5864
pub struct LoanDataFlowOperator;
5965

@@ -79,7 +85,9 @@ pub struct AnalysisData<'a, 'tcx: 'a> {
7985
pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
8086
}
8187

82-
fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
88+
fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId)
89+
-> Rc<BorrowCheckResult>
90+
{
8391
debug!("borrowck(body_owner_def_id={:?})", owner_def_id);
8492

8593
let owner_id = tcx.hir.as_local_node_id(owner_def_id).unwrap();
@@ -91,7 +99,9 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
9199
// those things (notably the synthesized constructors from
92100
// tuple structs/variants) do not have an associated body
93101
// and do not need borrowchecking.
94-
return;
102+
return Rc::new(BorrowCheckResult {
103+
used_mut_nodes: FxHashSet(),
104+
})
95105
}
96106
_ => { }
97107
}
@@ -100,7 +110,14 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
100110
let tables = tcx.typeck_tables_of(owner_def_id);
101111
let region_scope_tree = tcx.region_scope_tree(owner_def_id);
102112
let body = tcx.hir.body(body_id);
103-
let bccx = &mut BorrowckCtxt { tcx, tables, region_scope_tree, owner_def_id, body };
113+
let mut bccx = BorrowckCtxt {
114+
tcx,
115+
tables,
116+
region_scope_tree,
117+
owner_def_id,
118+
body,
119+
used_mut_nodes: RefCell::new(FxHashSet()),
120+
};
104121

105122
// Eventually, borrowck will always read the MIR, but at the
106123
// moment we do not. So, for now, we always force MIR to be
@@ -118,14 +135,19 @@ fn borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, owner_def_id: DefId) {
118135
if let Some(AnalysisData { all_loans,
119136
loans: loan_dfcx,
120137
move_data: flowed_moves }) =
121-
build_borrowck_dataflow_data(bccx, false, body_id,
138+
build_borrowck_dataflow_data(&mut bccx, false, body_id,
122139
|bccx| {
123140
cfg = Some(cfg::CFG::new(bccx.tcx, &body));
124141
cfg.as_mut().unwrap()
125142
})
126143
{
127-
check_loans::check_loans(bccx, &loan_dfcx, &flowed_moves, &all_loans, body);
144+
check_loans::check_loans(&mut bccx, &loan_dfcx, &flowed_moves, &all_loans, body);
128145
}
146+
unused::check(&mut bccx, body);
147+
148+
Rc::new(BorrowCheckResult {
149+
used_mut_nodes: bccx.used_mut_nodes.into_inner(),
150+
})
129151
}
130152

131153
fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tcx>,
@@ -198,7 +220,14 @@ pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
198220
let tables = tcx.typeck_tables_of(owner_def_id);
199221
let region_scope_tree = tcx.region_scope_tree(owner_def_id);
200222
let body = tcx.hir.body(body_id);
201-
let mut bccx = BorrowckCtxt { tcx, tables, region_scope_tree, owner_def_id, body };
223+
let mut bccx = BorrowckCtxt {
224+
tcx,
225+
tables,
226+
region_scope_tree,
227+
owner_def_id,
228+
body,
229+
used_mut_nodes: RefCell::new(FxHashSet()),
230+
};
202231

203232
let dataflow_data = build_borrowck_dataflow_data(&mut bccx, true, body_id, |_| cfg);
204233
(bccx, dataflow_data.unwrap())
@@ -219,6 +248,8 @@ pub struct BorrowckCtxt<'a, 'tcx: 'a> {
219248
owner_def_id: DefId,
220249

221250
body: &'tcx hir::Body,
251+
252+
used_mut_nodes: RefCell<FxHashSet<HirId>>,
222253
}
223254

224255
impl<'b, 'tcx: 'b> BorrowckErrors for BorrowckCtxt<'b, 'tcx> {
+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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+
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
12+
use rustc::hir::{self, HirId};
13+
use rustc::lint::builtin::UNUSED_MUT;
14+
use rustc::ty;
15+
use rustc::util::nodemap::{FxHashMap, FxHashSet};
16+
use rustc_back::slice;
17+
use syntax::ptr::P;
18+
19+
use borrowck::BorrowckCtxt;
20+
21+
pub fn check<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, body: &'tcx hir::Body) {
22+
let mut used_mut = bccx.used_mut_nodes.borrow().clone();
23+
UsedMutFinder {
24+
bccx,
25+
set: &mut used_mut,
26+
}.visit_expr(&body.value);
27+
let mut cx = UnusedMutCx { bccx, used_mut };
28+
for arg in body.arguments.iter() {
29+
cx.check_unused_mut_pat(slice::ref_slice(&arg.pat));
30+
}
31+
cx.visit_expr(&body.value);
32+
}
33+
34+
struct UsedMutFinder<'a, 'tcx: 'a> {
35+
bccx: &'a BorrowckCtxt<'a, 'tcx>,
36+
set: &'a mut FxHashSet<HirId>,
37+
}
38+
39+
struct UnusedMutCx<'a, 'tcx: 'a> {
40+
bccx: &'a BorrowckCtxt<'a, 'tcx>,
41+
used_mut: FxHashSet<HirId>,
42+
}
43+
44+
impl<'a, 'tcx> UnusedMutCx<'a, 'tcx> {
45+
fn check_unused_mut_pat(&self, pats: &[P<hir::Pat>]) {
46+
let tcx = self.bccx.tcx;
47+
let mut mutables = FxHashMap();
48+
for p in pats {
49+
p.each_binding(|_, id, span, path1| {
50+
let name = path1.node;
51+
52+
// Skip anything that looks like `_foo`
53+
if name.as_str().starts_with("_") {
54+
return
55+
}
56+
57+
// Skip anything that looks like `&foo` or `&mut foo`, only look
58+
// for by-value bindings
59+
let hir_id = tcx.hir.node_to_hir_id(id);
60+
let bm = match self.bccx.tables.pat_binding_modes().get(hir_id) {
61+
Some(&bm) => bm,
62+
None => span_bug!(span, "missing binding mode"),
63+
};
64+
match bm {
65+
ty::BindByValue(hir::MutMutable) => {}
66+
_ => return,
67+
}
68+
69+
mutables.entry(name).or_insert(Vec::new()).push((id, hir_id, span));
70+
});
71+
}
72+
73+
for (_name, ids) in mutables {
74+
// If any id for this name was used mutably then consider them all
75+
// ok, so move on to the next
76+
if ids.iter().any(|&(_, ref id, _)| self.used_mut.contains(id)) {
77+
continue
78+
}
79+
80+
let mut_span = tcx.sess.codemap().span_until_char(ids[0].2, ' ');
81+
82+
// Ok, every name wasn't used mutably, so issue a warning that this
83+
// didn't need to be mutable.
84+
tcx.struct_span_lint_node(UNUSED_MUT,
85+
ids[0].0,
86+
ids[0].2,
87+
"variable does not need to be mutable")
88+
.span_suggestion_short(mut_span, "remove this `mut`", "".to_owned())
89+
.emit();
90+
}
91+
}
92+
}
93+
94+
impl<'a, 'tcx> Visitor<'tcx> for UnusedMutCx<'a, 'tcx> {
95+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
96+
NestedVisitorMap::OnlyBodies(&self.bccx.tcx.hir)
97+
}
98+
99+
fn visit_arm(&mut self, arm: &hir::Arm) {
100+
self.check_unused_mut_pat(&arm.pats)
101+
}
102+
103+
fn visit_local(&mut self, local: &hir::Local) {
104+
self.check_unused_mut_pat(slice::ref_slice(&local.pat));
105+
}
106+
}
107+
108+
impl<'a, 'tcx> Visitor<'tcx> for UsedMutFinder<'a, 'tcx> {
109+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
110+
NestedVisitorMap::OnlyBodies(&self.bccx.tcx.hir)
111+
}
112+
113+
fn visit_nested_body(&mut self, id: hir::BodyId) {
114+
let def_id = self.bccx.tcx.hir.body_owner_def_id(id);
115+
self.set.extend(self.bccx.tcx.borrowck(def_id).used_mut_nodes.iter().cloned());
116+
self.visit_body(self.bccx.tcx.hir.body(id));
117+
}
118+
}

src/librustc_borrowck/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#[macro_use] extern crate syntax;
2323
extern crate syntax_pos;
2424
extern crate rustc_errors as errors;
25+
extern crate rustc_back;
2526

2627
// for "clarity", rename the graphviz crate to dot; graphviz within `borrowck`
2728
// refers to the borrowck-specific graphviz adapter traits.

0 commit comments

Comments
 (0)