summary refs log tree commit diff
path: root/src/librustc_mir/dataflow/impls/borrowed_locals.rs
blob: 0f7f37f2db8b47b10de129eae036b792750fabd0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
pub use super::*;

use rustc::mir::*;
use rustc::mir::visit::Visitor;
use crate::dataflow::{BitDenotation, GenKillSet};

/// This calculates if any part of a MIR local could have previously been borrowed.
/// This means that once a local has been borrowed, its bit will be set
/// from that point and onwards, until we see a StorageDead statement for the local,
/// at which points there is no memory associated with the local, so it cannot be borrowed.
/// This is used to compute which locals are live during a yield expression for
/// immovable generators.
#[derive(Copy, Clone)]
pub struct HaveBeenBorrowedLocals<'a, 'tcx> {
    body: &'a Body<'tcx>,
}

impl<'a, 'tcx> HaveBeenBorrowedLocals<'a, 'tcx> {
    pub fn new(body: &'a Body<'tcx>)
               -> Self {
        HaveBeenBorrowedLocals { body }
    }

    pub fn body(&self) -> &Body<'tcx> {
        self.body
    }
}

impl<'a, 'tcx> BitDenotation<'tcx> for HaveBeenBorrowedLocals<'a, 'tcx> {
    type Idx = Local;
    fn name() -> &'static str { "has_been_borrowed_locals" }
    fn bits_per_block(&self) -> usize {
        self.body.local_decls.len()
    }

    fn start_block_effect(&self, _on_entry: &mut BitSet<Local>) {
        // Nothing is borrowed on function entry
    }

    fn statement_effect(&self,
                        trans: &mut GenKillSet<Local>,
                        loc: Location) {
        let stmt = &self.body[loc.block].statements[loc.statement_index];

        BorrowedLocalsVisitor {
            trans,
        }.visit_statement(stmt, loc);

        // StorageDead invalidates all borrows and raw pointers to a local
        match stmt.kind {
            StatementKind::StorageDead(l) => trans.kill(l),
            _ => (),
        }
    }

    fn terminator_effect(&self,
                         trans: &mut GenKillSet<Local>,
                         loc: Location) {
        let terminator = self.body[loc.block].terminator();
        BorrowedLocalsVisitor {
            trans,
        }.visit_terminator(terminator, loc);
        match &terminator.kind {
            // Drop terminators borrows the location
            TerminatorKind::Drop { location, .. } |
            TerminatorKind::DropAndReplace { location, .. } => {
                if let Some(local) = find_local(location) {
                    trans.gen(local);
                }
            }
            _ => (),
        }
    }

    fn propagate_call_return(
        &self,
        _in_out: &mut BitSet<Local>,
        _call_bb: mir::BasicBlock,
        _dest_bb: mir::BasicBlock,
        _dest_place: &mir::Place<'tcx>,
    ) {
        // Nothing to do when a call returns successfully
    }
}

impl<'a, 'tcx> BottomValue for HaveBeenBorrowedLocals<'a, 'tcx> {
    // bottom = unborrowed
    const BOTTOM_VALUE: bool = false;
}

struct BorrowedLocalsVisitor<'gk> {
    trans: &'gk mut GenKillSet<Local>,
}

fn find_local<'tcx>(place: &Place<'tcx>) -> Option<Local> {
    place.iterate(|place_base, place_projection| {
        for proj in place_projection {
            if proj.elem == ProjectionElem::Deref {
                return None;
            }
        }

        if let PlaceBase::Local(local) = place_base {
            Some(*local)
        } else {
            None
        }
    })
}

impl<'tcx> Visitor<'tcx> for BorrowedLocalsVisitor<'_> {
    fn visit_rvalue(&mut self,
                    rvalue: &Rvalue<'tcx>,
                    location: Location) {
        if let Rvalue::Ref(_, _, ref place) = *rvalue {
            if let Some(local) = find_local(place) {
                self.trans.gen(local);
            }
        }

        self.super_rvalue(rvalue, location)
    }
}