rustc_borrowck/polonius/legacy/
loan_kills.rs

1use rustc_middle::mir::visit::Visitor;
2use rustc_middle::mir::{
3    Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind,
4    Terminator, TerminatorKind,
5};
6use rustc_middle::ty::TyCtxt;
7use tracing::debug;
8
9use super::{PoloniusFacts, PoloniusLocationTable};
10use crate::borrow_set::BorrowSet;
11use crate::places_conflict;
12
13/// Emit `loan_killed_at` and `cfg_edge` facts at the same time.
14pub(super) fn emit_loan_kills<'tcx>(
15    tcx: TyCtxt<'tcx>,
16    facts: &mut PoloniusFacts,
17    body: &Body<'tcx>,
18    location_table: &PoloniusLocationTable,
19    borrow_set: &BorrowSet<'tcx>,
20) {
21    let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, facts, body };
22    for (bb, data) in body.basic_blocks.iter_enumerated() {
23        visitor.visit_basic_block_data(bb, data);
24    }
25}
26
27struct LoanKillsGenerator<'a, 'tcx> {
28    tcx: TyCtxt<'tcx>,
29    facts: &'a mut PoloniusFacts,
30    location_table: &'a PoloniusLocationTable,
31    borrow_set: &'a BorrowSet<'tcx>,
32    body: &'a Body<'tcx>,
33}
34
35impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> {
36    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
37        // Also record CFG facts here.
38        self.facts.cfg_edge.push((
39            self.location_table.start_index(location),
40            self.location_table.mid_index(location),
41        ));
42
43        self.facts.cfg_edge.push((
44            self.location_table.mid_index(location),
45            self.location_table.start_index(location.successor_within_block()),
46        ));
47
48        // If there are borrows on this now dead local, we need to record them as `killed`.
49        if let StatementKind::StorageDead(local) = statement.kind {
50            self.record_killed_borrows_for_local(local, location);
51        }
52
53        self.super_statement(statement, location);
54    }
55
56    fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
57        // When we see `X = ...`, then kill borrows of
58        // `(*X).foo` and so forth.
59        self.record_killed_borrows_for_place(*place, location);
60        self.super_assign(place, rvalue, location);
61    }
62
63    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
64        // Also record CFG facts here.
65        self.facts.cfg_edge.push((
66            self.location_table.start_index(location),
67            self.location_table.mid_index(location),
68        ));
69
70        let successor_blocks = terminator.successors();
71        self.facts.cfg_edge.reserve(successor_blocks.size_hint().0);
72        for successor_block in successor_blocks {
73            self.facts.cfg_edge.push((
74                self.location_table.mid_index(location),
75                self.location_table.start_index(successor_block.start_location()),
76            ));
77        }
78
79        // A `Call` terminator's return value can be a local which has borrows,
80        // so we need to record those as `killed` as well.
81        if let TerminatorKind::Call { destination, .. } = terminator.kind {
82            self.record_killed_borrows_for_place(destination, location);
83        }
84
85        self.super_terminator(terminator, location);
86    }
87}
88
89impl<'tcx> LoanKillsGenerator<'_, 'tcx> {
90    /// Records the borrows on the specified place as `killed`. For example, when assigning to a
91    /// local, or on a call's return destination.
92    fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
93        // Depending on the `Place` we're killing:
94        // - if it's a local, or a single deref of a local,
95        //   we kill all the borrows on the local.
96        // - if it's a deeper projection, we have to filter which
97        //   of the borrows are killed: the ones whose `borrowed_place`
98        //   conflicts with the `place`.
99        match place.as_ref() {
100            PlaceRef { local, projection: &[] }
101            | PlaceRef { local, projection: &[ProjectionElem::Deref] } => {
102                debug!(
103                    "Recording `killed` facts for borrows of local={:?} at location={:?}",
104                    local, location
105                );
106
107                self.record_killed_borrows_for_local(local, location);
108            }
109
110            PlaceRef { local, projection: &[.., _] } => {
111                // Kill conflicting borrows of the innermost local.
112                debug!(
113                    "Recording `killed` facts for borrows of \
114                            innermost projected local={:?} at location={:?}",
115                    local, location
116                );
117
118                if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
119                    for &borrow_index in borrow_indices {
120                        let places_conflict = places_conflict::places_conflict(
121                            self.tcx,
122                            self.body,
123                            self.borrow_set[borrow_index].borrowed_place,
124                            place,
125                            places_conflict::PlaceConflictBias::NoOverlap,
126                        );
127
128                        if places_conflict {
129                            let location_index = self.location_table.mid_index(location);
130                            self.facts.loan_killed_at.push((borrow_index, location_index));
131                        }
132                    }
133                }
134            }
135        }
136    }
137
138    /// Records the borrows on the specified local as `killed`.
139    fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
140        if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
141            let location_index = self.location_table.mid_index(location);
142            self.facts.loan_killed_at.reserve(borrow_indices.len());
143            for &borrow_index in borrow_indices {
144                self.facts.loan_killed_at.push((borrow_index, location_index));
145            }
146        }
147    }
148}