]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_mir/src/borrow_check/used_muts.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / used_muts.rs
1 use rustc_middle::mir::visit::{PlaceContext, Visitor};
2 use rustc_middle::mir::{
3 Local, Location, Place, Statement, StatementKind, Terminator, TerminatorKind,
4 };
5
6 use rustc_data_structures::fx::FxHashSet;
7
8 use crate::borrow_check::MirBorrowckCtxt;
9
10 impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
11 /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
12 /// of the `unused_mut` lint.
13 ///
14 /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
15 /// used from borrow checking. This function looks for assignments into these locals from
16 /// user-declared locals and adds those user-defined locals to the `used_mut` set. This can
17 /// occur due to a rare case involving upvars in closures.
18 ///
19 /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
20 /// (not arguments) that have not already been marked as being used.
21 /// This function then looks for assignments from statements or the terminator into the locals
22 /// from this set and removes them from the set. This leaves only those locals that have not
23 /// been assigned to - this set is used as a proxy for locals that were not initialized due to
24 /// unreachable code. These locals are then considered "used" to silence the lint for them.
25 /// See #55344 for context.
26 crate fn gather_used_muts(
27 &mut self,
28 temporary_used_locals: FxHashSet<Local>,
29 mut never_initialized_mut_locals: FxHashSet<Local>,
30 ) {
31 {
32 let mut visitor = GatherUsedMutsVisitor {
33 temporary_used_locals,
34 never_initialized_mut_locals: &mut never_initialized_mut_locals,
35 mbcx: self,
36 };
37 visitor.visit_body(&visitor.mbcx.body);
38 }
39
40 // Take the union of the existed `used_mut` set with those variables we've found were
41 // never initialized.
42 debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
43 self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
44 }
45 }
46
47 /// MIR visitor for collecting used mutable variables.
48 /// The 'visit lifetime represents the duration of the MIR walk.
49 struct GatherUsedMutsVisitor<'visit, 'cx, 'tcx> {
50 temporary_used_locals: FxHashSet<Local>,
51 never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
52 mbcx: &'visit mut MirBorrowckCtxt<'cx, 'tcx>,
53 }
54
55 impl GatherUsedMutsVisitor<'_, '_, '_> {
56 fn remove_never_initialized_mut_locals(&mut self, into: Place<'_>) {
57 // Remove any locals that we found were initialized from the
58 // `never_initialized_mut_locals` set. At the end, the only remaining locals will
59 // be those that were never initialized - we will consider those as being used as
60 // they will either have been removed by unreachable code optimizations; or linted
61 // as unused variables.
62 self.never_initialized_mut_locals.remove(&into.local);
63 }
64 }
65
66 impl<'visit, 'cx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'tcx> {
67 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
68 debug!("visit_terminator: terminator={:?}", terminator);
69 match &terminator.kind {
70 TerminatorKind::Call { destination: Some((into, _)), .. } => {
71 self.remove_never_initialized_mut_locals(*into);
72 }
73 TerminatorKind::DropAndReplace { place, .. } => {
74 self.remove_never_initialized_mut_locals(*place);
75 }
76 _ => {}
77 }
78
79 self.super_terminator(terminator, location);
80 }
81
82 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
83 if let StatementKind::Assign(box (into, _)) = &statement.kind {
84 debug!(
85 "visit_statement: statement={:?} local={:?} \
86 never_initialized_mut_locals={:?}",
87 statement, into.local, self.never_initialized_mut_locals
88 );
89 self.remove_never_initialized_mut_locals(*into);
90 }
91
92 self.super_statement(statement, location);
93 }
94
95 fn visit_local(&mut self, local: &Local, place_context: PlaceContext, location: Location) {
96 if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
97 // Propagate the Local assigned at this Location as a used mutable local variable
98 for moi in &self.mbcx.move_data.loc_map[location] {
99 let mpi = &self.mbcx.move_data.moves[*moi].path;
100 let path = &self.mbcx.move_data.move_paths[*mpi];
101 debug!(
102 "assignment of {:?} to {:?}, adding {:?} to used mutable set",
103 path.place, local, path.place
104 );
105 if let Some(user_local) = path.place.as_local() {
106 self.mbcx.used_mut.insert(user_local);
107 }
108 }
109 }
110 }
111 }