blob: 4d3b422ab28171770a340aa99bfead908262e78e [file] [log] [blame]
// Copyright 2017 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.
//! This module provides two passes:
//!
//! - `CleanEndRegions`, that reduces the set of `EndRegion` statements
//! in the MIR.
//! - `CleanAscribeUserType`, that replaces all `AscribeUserType` statements
//! with `Nop`.
//!
//! The `CleanEndRegions` "pass" is actually implemented as two
//! traversals (aka visits) of the input MIR. The first traversal,
//! `GatherBorrowedRegions`, finds all of the regions in the MIR
//! that are involved in a borrow.
//!
//! The second traversal, `DeleteTrivialEndRegions`, walks over the
//! MIR and removes any `EndRegion` that is applied to a region that
//! was not seen in the previous pass.
//!
//! The `CleanAscribeUserType` pass runs at a distinct time from the
//! `CleanEndRegions` pass. It is important that the `CleanAscribeUserType`
//! pass runs after the MIR borrowck so that the NLL type checker can
//! perform the type assertion when it encounters the `AscribeUserType`
//! statements.
use rustc_data_structures::fx::FxHashSet;
use rustc::middle::region;
use rustc::mir::{BasicBlock, FakeReadCause, Local, Location, Mir, Place};
use rustc::mir::{Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, Visitor, TyContext};
use rustc::ty::{Ty, RegionKind, TyCtxt};
use transform::{MirPass, MirSource};
pub struct CleanEndRegions;
#[derive(Default)]
struct GatherBorrowedRegions {
seen_regions: FxHashSet<region::Scope>,
}
struct DeleteTrivialEndRegions<'a> {
seen_regions: &'a FxHashSet<region::Scope>,
}
impl MirPass for CleanEndRegions {
fn run_pass<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
_source: MirSource,
mir: &mut Mir<'tcx>) {
if !tcx.emit_end_regions() { return; }
let mut gather = GatherBorrowedRegions::default();
gather.visit_mir(mir);
let mut delete = DeleteTrivialEndRegions { seen_regions: &mut gather.seen_regions };
delete.visit_mir(mir);
}
}
impl<'tcx> Visitor<'tcx> for GatherBorrowedRegions {
fn visit_rvalue(&mut self,
rvalue: &Rvalue<'tcx>,
location: Location) {
// Gather regions that are used for borrows
if let Rvalue::Ref(r, _, _) = *rvalue {
if let RegionKind::ReScope(ce) = *r {
self.seen_regions.insert(ce);
}
}
self.super_rvalue(rvalue, location);
}
fn visit_ty(&mut self, ty: &Ty<'tcx>, _: TyContext) {
// Gather regions that occur in types
for re in ty.walk().flat_map(|t| t.regions()) {
match *re {
RegionKind::ReScope(ce) => { self.seen_regions.insert(ce); }
_ => {},
}
}
self.super_ty(ty);
}
}
impl<'a, 'tcx> MutVisitor<'tcx> for DeleteTrivialEndRegions<'a> {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
let mut delete_it = false;
if let StatementKind::EndRegion(ref region_scope) = statement.kind {
if !self.seen_regions.contains(region_scope) {
delete_it = true;
}
}
if delete_it {
statement.make_nop();
}
self.super_statement(block, statement, location);
}
}
pub struct CleanAscribeUserType;
pub struct DeleteAscribeUserType;
impl MirPass for CleanAscribeUserType {
fn run_pass<'a, 'tcx>(&self,
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
_source: MirSource,
mir: &mut Mir<'tcx>) {
let mut delete = DeleteAscribeUserType;
delete.visit_mir(mir);
}
}
impl<'tcx> MutVisitor<'tcx> for DeleteAscribeUserType {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
if let StatementKind::AscribeUserType(..) = statement.kind {
statement.make_nop();
}
self.super_statement(block, statement, location);
}
}
pub struct CleanFakeReadsAndBorrows;
#[derive(Default)]
pub struct DeleteAndRecordFakeReads {
fake_borrow_temporaries: FxHashSet<Local>,
}
pub struct DeleteFakeBorrows {
fake_borrow_temporaries: FxHashSet<Local>,
}
// Removes any FakeReads from the MIR
impl MirPass for CleanFakeReadsAndBorrows {
fn run_pass<'a, 'tcx>(&self,
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
_source: MirSource,
mir: &mut Mir<'tcx>) {
let mut delete_reads = DeleteAndRecordFakeReads::default();
delete_reads.visit_mir(mir);
let mut delete_borrows = DeleteFakeBorrows {
fake_borrow_temporaries: delete_reads.fake_borrow_temporaries,
};
delete_borrows.visit_mir(mir);
}
}
impl<'tcx> MutVisitor<'tcx> for DeleteAndRecordFakeReads {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
if let StatementKind::FakeRead(cause, ref place) = statement.kind {
if let FakeReadCause::ForMatchGuard = cause {
match *place {
Place::Local(local) => self.fake_borrow_temporaries.insert(local),
_ => bug!("Fake match guard read of non-local: {:?}", place),
};
}
statement.make_nop();
}
self.super_statement(block, statement, location);
}
}
impl<'tcx> MutVisitor<'tcx> for DeleteFakeBorrows {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
if let StatementKind::Assign(Place::Local(local), _) = statement.kind {
if self.fake_borrow_temporaries.contains(&local) {
statement.make_nop();
}
}
self.super_statement(block, statement, location);
}
}