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
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::hir;
use rustc::mir::ProjectionElem;
use rustc::mir::{Local, Mir, Place, Mutability};
use rustc::ty::{self, TyCtxt};
use borrow_check::borrow_set::LocalsStateAtExit;

/// Extension methods for the `Place` type.
crate trait PlaceExt<'tcx> {
    /// Returns true if we can safely ignore borrows of this place.
    /// This is true whenever there is no action that the user can do
    /// to the place `self` that would invalidate the borrow. This is true
    /// for borrows of raw pointer dereferents as well as shared references.
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
        ) -> bool;

    /// If this is a place like `x.f.g`, returns the local
    /// `x`. Returns `None` if this is based in a static.
    fn root_local(&self) -> Option<Local>;
}

impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
    fn ignore_borrow(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        mir: &Mir<'tcx>,
        locals_state_at_exit: &LocalsStateAtExit,
    ) -> bool {
        match self {
            Place::Promoted(_) => false,

            // If a local variable is immutable, then we only need to track borrows to guard
            // against two kinds of errors:
            // * The variable being dropped while still borrowed (e.g., because the fn returns
            //   a reference to a local variable)
            // * The variable being moved while still borrowed
            //
            // In particular, the variable cannot be mutated -- the "access checks" will fail --
            // so we don't have to worry about mutation while borrowed.
            Place::Local(index) => {
                match locals_state_at_exit {
                    LocalsStateAtExit::AllAreInvalidated => false,
                    LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => {
                        let ignore = !has_storage_dead_or_moved.contains(*index) &&
                            mir.local_decls[*index].mutability == Mutability::Not;
                        debug!("ignore_borrow: local {:?} => {:?}", index, ignore);
                        ignore
                    }
                }
            }
            Place::Static(static_) => {
                tcx.is_static(static_.def_id) == Some(hir::Mutability::MutMutable)
            }
            Place::Projection(proj) => match proj.elem {
                ProjectionElem::Field(..)
                | ProjectionElem::Downcast(..)
                | ProjectionElem::Subslice { .. }
                | ProjectionElem::ConstantIndex { .. }
                | ProjectionElem::Index(_) => proj.base.ignore_borrow(
                    tcx, mir, locals_state_at_exit),

                ProjectionElem::Deref => {
                    let ty = proj.base.ty(mir, tcx).to_ty(tcx);
                    match ty.sty {
                        // For both derefs of raw pointers and `&T`
                        // references, the original path is `Copy` and
                        // therefore not significant.  In particular,
                        // there is nothing the user can do to the
                        // original path that would invalidate the
                        // newly created reference -- and if there
                        // were, then the user could have copied the
                        // original path into a new variable and
                        // borrowed *that* one, leaving the original
                        // path unborrowed.
                        ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true,
                        _ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit),
                    }
                }
            },
        }
    }

    fn root_local(&self) -> Option<Local> {
        let mut p = self;
        loop {
            match p {
                Place::Projection(pi) => p = &pi.base,
                Place::Promoted(_) |
                Place::Static(_) => return None,
                Place::Local(l) => return Some(*l),
            }
        }
    }
}