2021-01-05 19:53:07 +01:00
|
|
|
use super::*;
|
2018-01-29 08:48:56 +01:00
|
|
|
|
2021-01-05 19:53:07 +01:00
|
|
|
use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
|
2020-03-29 16:41:09 +02:00
|
|
|
use rustc_middle::mir::visit::Visitor;
|
|
|
|
|
use rustc_middle::mir::*;
|
|
|
|
|
use rustc_middle::ty::{ParamEnv, TyCtxt};
|
2020-02-12 13:36:47 -08:00
|
|
|
use rustc_span::DUMMY_SP;
|
|
|
|
|
|
|
|
|
|
pub type MaybeMutBorrowedLocals<'mir, 'tcx> = MaybeBorrowedLocals<MutBorrow<'mir, 'tcx>>;
|
|
|
|
|
|
|
|
|
|
/// A dataflow analysis that tracks whether a pointer or reference could possibly exist that points
|
|
|
|
|
/// to a given local.
|
|
|
|
|
///
|
|
|
|
|
/// The `K` parameter determines what kind of borrows are tracked. By default,
|
|
|
|
|
/// `MaybeBorrowedLocals` looks for *any* borrow of a local. If you are only interested in borrows
|
|
|
|
|
/// that might allow mutation, use the `MaybeMutBorrowedLocals` type alias instead.
|
|
|
|
|
///
|
|
|
|
|
/// At present, this is used as a very limited form of alias analysis. For example,
|
|
|
|
|
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
|
|
|
|
|
/// immovable generators. `MaybeMutBorrowedLocals` is used during const checking to prove that a
|
|
|
|
|
/// local has not been mutated via indirect assignment (e.g., `*p = 42`), the side-effects of a
|
|
|
|
|
/// function call or inline assembly.
|
|
|
|
|
pub struct MaybeBorrowedLocals<K = AnyBorrow> {
|
|
|
|
|
kind: K,
|
2020-02-13 13:57:01 -08:00
|
|
|
ignore_borrow_on_drop: bool,
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
impl MaybeBorrowedLocals {
|
|
|
|
|
/// A dataflow analysis that records whether a pointer or reference exists that may alias the
|
|
|
|
|
/// given local.
|
2020-02-13 13:56:19 -08:00
|
|
|
pub fn all_borrows() -> Self {
|
2020-02-13 13:57:01 -08:00
|
|
|
MaybeBorrowedLocals { kind: AnyBorrow, ignore_borrow_on_drop: false }
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
impl MaybeMutBorrowedLocals<'mir, 'tcx> {
|
|
|
|
|
/// A dataflow analysis that records whether a pointer or reference exists that may *mutably*
|
|
|
|
|
/// alias the given local.
|
2020-02-13 13:56:19 -08:00
|
|
|
///
|
|
|
|
|
/// This includes `&mut` and pointers derived from an `&mut`, as well as shared borrows of
|
|
|
|
|
/// types with interior mutability.
|
|
|
|
|
pub fn mut_borrows_only(
|
2020-02-12 13:36:47 -08:00
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
|
body: &'mir mir::Body<'tcx>,
|
|
|
|
|
param_env: ParamEnv<'tcx>,
|
|
|
|
|
) -> Self {
|
2020-02-13 13:57:01 -08:00
|
|
|
MaybeBorrowedLocals {
|
|
|
|
|
kind: MutBorrow { body, tcx, param_env },
|
|
|
|
|
ignore_borrow_on_drop: false,
|
|
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
impl<K> MaybeBorrowedLocals<K> {
|
2020-02-13 13:57:01 -08:00
|
|
|
/// During dataflow analysis, ignore the borrow that may occur when a place is dropped.
|
|
|
|
|
///
|
|
|
|
|
/// Drop terminators may call custom drop glue (`Drop::drop`), which takes `&mut self` as a
|
|
|
|
|
/// parameter. In the general case, a drop impl could launder that reference into the
|
|
|
|
|
/// surrounding environment through a raw pointer, thus creating a valid `*mut` pointing to the
|
|
|
|
|
/// dropped local. We are not yet willing to declare this particular case UB, so we must treat
|
|
|
|
|
/// all dropped locals as mutably borrowed for now. See discussion on [#61069].
|
|
|
|
|
///
|
|
|
|
|
/// In some contexts, we know that this borrow will never occur. For example, during
|
|
|
|
|
/// const-eval, custom drop glue cannot be run. Code that calls this should document the
|
2020-02-17 13:43:13 -08:00
|
|
|
/// assumptions that justify ignoring `Drop` terminators in this way.
|
2020-02-13 13:57:01 -08:00
|
|
|
///
|
|
|
|
|
/// [#61069]: https://github.com/rust-lang/rust/pull/61069
|
|
|
|
|
pub fn unsound_ignore_borrow_on_drop(self) -> Self {
|
|
|
|
|
MaybeBorrowedLocals { ignore_borrow_on_drop: true, ..self }
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
fn transfer_function<'a, T>(&'a self, trans: &'a mut T) -> TransferFunction<'a, T, K> {
|
2020-02-13 13:57:01 -08:00
|
|
|
TransferFunction {
|
|
|
|
|
kind: &self.kind,
|
|
|
|
|
trans,
|
|
|
|
|
ignore_borrow_on_drop: self.ignore_borrow_on_drop,
|
|
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
impl<K> AnalysisDomain<'tcx> for MaybeBorrowedLocals<K>
|
|
|
|
|
where
|
|
|
|
|
K: BorrowAnalysisKind<'tcx>,
|
|
|
|
|
{
|
2020-08-28 13:26:25 -07:00
|
|
|
type Domain = BitSet<Local>;
|
2020-02-12 13:36:47 -08:00
|
|
|
const NAME: &'static str = K::ANALYSIS_NAME;
|
|
|
|
|
|
2020-08-28 13:26:25 -07:00
|
|
|
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
|
|
|
|
|
// bottom = unborrowed
|
|
|
|
|
BitSet::new_empty(body.local_decls().len())
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
|
|
|
|
|
2020-08-28 13:26:25 -07:00
|
|
|
fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
|
2020-02-12 13:36:47 -08:00
|
|
|
// No locals are aliased on function entry
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<K> GenKillAnalysis<'tcx> for MaybeBorrowedLocals<K>
|
|
|
|
|
where
|
|
|
|
|
K: BorrowAnalysisKind<'tcx>,
|
|
|
|
|
{
|
2020-08-28 13:26:25 -07:00
|
|
|
type Idx = Local;
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
fn statement_effect(
|
|
|
|
|
&self,
|
|
|
|
|
trans: &mut impl GenKill<Self::Idx>,
|
|
|
|
|
statement: &mir::Statement<'tcx>,
|
|
|
|
|
location: Location,
|
|
|
|
|
) {
|
|
|
|
|
self.transfer_function(trans).visit_statement(statement, location);
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2020-02-12 13:36:47 -08:00
|
|
|
|
|
|
|
|
fn terminator_effect(
|
|
|
|
|
&self,
|
|
|
|
|
trans: &mut impl GenKill<Self::Idx>,
|
|
|
|
|
terminator: &mir::Terminator<'tcx>,
|
|
|
|
|
location: Location,
|
|
|
|
|
) {
|
|
|
|
|
self.transfer_function(trans).visit_terminator(terminator, location);
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
fn call_return_effect(
|
|
|
|
|
&self,
|
|
|
|
|
_trans: &mut impl GenKill<Self::Idx>,
|
|
|
|
|
_block: mir::BasicBlock,
|
|
|
|
|
_func: &mir::Operand<'tcx>,
|
|
|
|
|
_args: &[mir::Operand<'tcx>],
|
2020-03-31 13:54:20 -03:00
|
|
|
_dest_place: mir::Place<'tcx>,
|
2020-02-12 13:36:47 -08:00
|
|
|
) {
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
/// A `Visitor` that defines the transfer function for `MaybeBorrowedLocals`.
|
|
|
|
|
struct TransferFunction<'a, T, K> {
|
|
|
|
|
trans: &'a mut T,
|
|
|
|
|
kind: &'a K,
|
2020-02-13 13:57:01 -08:00
|
|
|
ignore_borrow_on_drop: bool,
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
2018-07-31 21:54:30 +02:00
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
impl<T, K> Visitor<'tcx> for TransferFunction<'a, T, K>
|
|
|
|
|
where
|
|
|
|
|
T: GenKill<Local>,
|
|
|
|
|
K: BorrowAnalysisKind<'tcx>,
|
|
|
|
|
{
|
|
|
|
|
fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) {
|
|
|
|
|
self.super_statement(stmt, location);
|
|
|
|
|
|
|
|
|
|
// When we reach a `StorageDead` statement, we can assume that any pointers to this memory
|
|
|
|
|
// are now invalid.
|
|
|
|
|
if let StatementKind::StorageDead(local) = stmt.kind {
|
|
|
|
|
self.trans.kill(local);
|
2018-07-31 21:54:30 +02:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
|
|
|
|
|
self.super_rvalue(rvalue, location);
|
|
|
|
|
|
|
|
|
|
match rvalue {
|
|
|
|
|
mir::Rvalue::AddressOf(mt, borrowed_place) => {
|
2020-03-31 12:19:29 -03:00
|
|
|
if !borrowed_place.is_indirect() && self.kind.in_address_of(*mt, *borrowed_place) {
|
2020-02-12 13:36:47 -08:00
|
|
|
self.trans.gen(borrowed_place.local);
|
2019-05-23 04:39:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-12 13:36:47 -08:00
|
|
|
|
|
|
|
|
mir::Rvalue::Ref(_, kind, borrowed_place) => {
|
2020-03-31 12:19:29 -03:00
|
|
|
if !borrowed_place.is_indirect() && self.kind.in_ref(*kind, *borrowed_place) {
|
2020-02-12 13:36:47 -08:00
|
|
|
self.trans.gen(borrowed_place.local);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mir::Rvalue::Cast(..)
|
2021-09-06 18:33:23 +01:00
|
|
|
| mir::Rvalue::ShallowInitBox(..)
|
2020-02-12 13:36:47 -08:00
|
|
|
| mir::Rvalue::Use(..)
|
2020-05-02 21:44:25 +02:00
|
|
|
| mir::Rvalue::ThreadLocalRef(..)
|
2020-02-12 13:36:47 -08:00
|
|
|
| mir::Rvalue::Repeat(..)
|
|
|
|
|
| mir::Rvalue::Len(..)
|
|
|
|
|
| mir::Rvalue::BinaryOp(..)
|
|
|
|
|
| mir::Rvalue::CheckedBinaryOp(..)
|
|
|
|
|
| mir::Rvalue::NullaryOp(..)
|
|
|
|
|
| mir::Rvalue::UnaryOp(..)
|
|
|
|
|
| mir::Rvalue::Discriminant(..)
|
|
|
|
|
| mir::Rvalue::Aggregate(..) => {}
|
2019-05-23 04:39:49 +02:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
|
|
|
|
|
self.super_terminator(terminator, location);
|
|
|
|
|
|
|
|
|
|
match terminator.kind {
|
2020-06-10 09:56:54 +02:00
|
|
|
mir::TerminatorKind::Drop { place: dropped_place, .. }
|
|
|
|
|
| mir::TerminatorKind::DropAndReplace { place: dropped_place, .. } => {
|
2020-02-13 13:57:01 -08:00
|
|
|
// See documentation for `unsound_ignore_borrow_on_drop` for an explanation.
|
|
|
|
|
if !self.ignore_borrow_on_drop {
|
|
|
|
|
self.trans.gen(dropped_place.local);
|
|
|
|
|
}
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TerminatorKind::Abort
|
|
|
|
|
| TerminatorKind::Assert { .. }
|
|
|
|
|
| TerminatorKind::Call { .. }
|
2020-06-02 09:15:24 +02:00
|
|
|
| TerminatorKind::FalseEdge { .. }
|
2020-02-12 13:36:47 -08:00
|
|
|
| TerminatorKind::FalseUnwind { .. }
|
|
|
|
|
| TerminatorKind::GeneratorDrop
|
|
|
|
|
| TerminatorKind::Goto { .. }
|
2020-02-14 18:17:50 +00:00
|
|
|
| TerminatorKind::InlineAsm { .. }
|
2020-02-12 13:36:47 -08:00
|
|
|
| TerminatorKind::Resume
|
|
|
|
|
| TerminatorKind::Return
|
|
|
|
|
| TerminatorKind::SwitchInt { .. }
|
|
|
|
|
| TerminatorKind::Unreachable
|
|
|
|
|
| TerminatorKind::Yield { .. } => {}
|
|
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
pub struct AnyBorrow;
|
|
|
|
|
|
|
|
|
|
pub struct MutBorrow<'mir, 'tcx> {
|
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
|
body: &'mir Body<'tcx>,
|
|
|
|
|
param_env: ParamEnv<'tcx>,
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
impl MutBorrow<'mir, 'tcx> {
|
2020-02-17 13:39:50 -08:00
|
|
|
/// `&` and `&raw` only allow mutation if the borrowed place is `!Freeze`.
|
|
|
|
|
///
|
|
|
|
|
/// This assumes that it is UB to take the address of a struct field whose type is
|
|
|
|
|
/// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of
|
|
|
|
|
/// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will
|
|
|
|
|
/// have to check the type of the borrowed **local** instead of the borrowed **place**
|
|
|
|
|
/// below. See [rust-lang/unsafe-code-guidelines#134].
|
|
|
|
|
///
|
|
|
|
|
/// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
|
2020-03-31 12:19:29 -03:00
|
|
|
fn shared_borrow_allows_mutation(&self, place: Place<'tcx>) -> bool {
|
2020-06-21 11:20:48 +02:00
|
|
|
!place.ty(self.body, self.tcx).ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env)
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
pub trait BorrowAnalysisKind<'tcx> {
|
|
|
|
|
const ANALYSIS_NAME: &'static str;
|
|
|
|
|
|
2020-03-31 12:19:29 -03:00
|
|
|
fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool;
|
|
|
|
|
fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool;
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 13:36:47 -08:00
|
|
|
impl BorrowAnalysisKind<'tcx> for AnyBorrow {
|
|
|
|
|
const ANALYSIS_NAME: &'static str = "maybe_borrowed_locals";
|
|
|
|
|
|
2020-03-31 12:19:29 -03:00
|
|
|
fn in_ref(&self, _: mir::BorrowKind, _: Place<'_>) -> bool {
|
2020-02-12 13:36:47 -08:00
|
|
|
true
|
|
|
|
|
}
|
2020-03-31 12:19:29 -03:00
|
|
|
fn in_address_of(&self, _: Mutability, _: Place<'_>) -> bool {
|
2020-02-12 13:36:47 -08:00
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl BorrowAnalysisKind<'tcx> for MutBorrow<'mir, 'tcx> {
|
|
|
|
|
const ANALYSIS_NAME: &'static str = "maybe_mut_borrowed_locals";
|
|
|
|
|
|
2020-03-31 12:19:29 -03:00
|
|
|
fn in_ref(&self, kind: mir::BorrowKind, place: Place<'tcx>) -> bool {
|
2020-02-12 13:36:47 -08:00
|
|
|
match kind {
|
|
|
|
|
mir::BorrowKind::Mut { .. } => true,
|
|
|
|
|
mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
|
|
|
|
|
self.shared_borrow_allows_mutation(place)
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-12 13:36:47 -08:00
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
|
2020-03-31 12:19:29 -03:00
|
|
|
fn in_address_of(&self, mt: Mutability, place: Place<'tcx>) -> bool {
|
2020-02-12 13:36:47 -08:00
|
|
|
match mt {
|
|
|
|
|
Mutability::Mut => true,
|
|
|
|
|
Mutability::Not => self.shared_borrow_allows_mutation(place),
|
|
|
|
|
}
|
2018-01-29 08:48:56 +01:00
|
|
|
}
|
|
|
|
|
}
|