]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/used_muts.rs
Rollup merge of #55507 - fhartwig:size_of_intrinsic_docs, r=frewsxcv
[rust.git] / src / librustc_mir / borrow_check / used_muts.rs
1 // Copyright 2018 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::mir::visit::{PlaceContext, Visitor};
12 use rustc::mir::{BasicBlock, Local, Location, Place, Statement, StatementKind, TerminatorKind};
13
14 use rustc_data_structures::fx::FxHashSet;
15
16 use borrow_check::MirBorrowckCtxt;
17
18 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
19     /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
20     /// of the `unused_mut` lint.
21     ///
22     /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
23     ///  used from borrow checking. This function looks for assignments into these locals from
24     ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
25     ///  occur due to a rare case involving upvars in closures.
26     ///
27     /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
28     ///  (not arguments) that have not already been marked as being used.
29     ///  This function then looks for assignments from statements or the terminator into the locals
30     ///  from this set and removes them from the set. This leaves only those locals that have not
31     ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
32     ///  unreachable code. These locals are then considered "used" to silence the lint for them.
33     ///  See #55344 for context.
34     crate fn gather_used_muts(
35         &mut self,
36         temporary_used_locals: FxHashSet<Local>,
37         mut never_initialized_mut_locals: FxHashSet<Local>,
38     ) {
39         {
40             let mut visitor = GatherUsedMutsVisitor {
41                 temporary_used_locals,
42                 never_initialized_mut_locals: &mut never_initialized_mut_locals,
43                 mbcx: self,
44             };
45             visitor.visit_mir(visitor.mbcx.mir);
46         }
47
48         // Take the union of the existed `used_mut` set with those variables we've found were
49         // never initialized.
50         debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
51         self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
52     }
53 }
54
55 /// MIR visitor for collecting used mutable variables.
56 /// The 'visit lifetime represents the duration of the MIR walk.
57 struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
58     temporary_used_locals: FxHashSet<Local>,
59     never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
60     mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
61 }
62
63 impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
64     fn visit_terminator_kind(
65         &mut self,
66         _block: BasicBlock,
67         kind: &TerminatorKind<'tcx>,
68         _location: Location,
69     ) {
70         debug!("visit_terminator_kind: kind={:?}", kind);
71         match &kind {
72             TerminatorKind::Call { destination: Some((into, _)), .. } => {
73                 if let Some(local) = into.base_local() {
74                     debug!(
75                         "visit_terminator_kind: kind={:?} local={:?} \
76                          never_initialized_mut_locals={:?}",
77                         kind, local, self.never_initialized_mut_locals
78                     );
79                     let _ = self.never_initialized_mut_locals.remove(&local);
80                 }
81             },
82             _ => {},
83         }
84     }
85
86     fn visit_statement(
87         &mut self,
88         _block: BasicBlock,
89         statement: &Statement<'tcx>,
90         _location: Location,
91     ) {
92         match &statement.kind {
93             StatementKind::Assign(into, _) => {
94                 // Remove any locals that we found were initialized from the
95                 // `never_initialized_mut_locals` set. At the end, the only remaining locals will
96                 // be those that were never initialized - we will consider those as being used as
97                 // they will either have been removed by unreachable code optimizations; or linted
98                 // as unused variables.
99                 if let Some(local) = into.base_local() {
100                     debug!(
101                         "visit_statement: statement={:?} local={:?} \
102                          never_initialized_mut_locals={:?}",
103                         statement, local, self.never_initialized_mut_locals
104                     );
105                     let _ = self.never_initialized_mut_locals.remove(&local);
106                 }
107             },
108             _ => {},
109         }
110     }
111
112     fn visit_local(
113         &mut self,
114         local: &Local,
115         place_context: PlaceContext<'tcx>,
116         location: Location,
117     ) {
118         if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
119             // Propagate the Local assigned at this Location as a used mutable local variable
120             for moi in &self.mbcx.move_data.loc_map[location] {
121                 let mpi = &self.mbcx.move_data.moves[*moi].path;
122                 let path = &self.mbcx.move_data.move_paths[*mpi];
123                 debug!(
124                     "assignment of {:?} to {:?}, adding {:?} to used mutable set",
125                     path.place, local, path.place
126                 );
127                 if let Place::Local(user_local) = path.place {
128                     self.mbcx.used_mut.insert(user_local);
129                 }
130             }
131         }
132     }
133 }