Skip to content

Commit

Permalink
Add a MIR transform to remove fake reads
Browse files Browse the repository at this point in the history
As we are now creating borrows of places that may not be valid for
borrow checking matches, these have to be removed to avoid generating
broken code.
  • Loading branch information
matthewjasper committed Sep 24, 2018
1 parent 1a6ed02 commit f71f733
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/librustc_mir/lib.rs
Expand Up @@ -14,7 +14,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
*/

#![cfg_attr(not(stage0), feature(nll))]
#![feature(nll)]
#![feature(in_band_lifetimes)]
#![feature(impl_header_lifetime_elision)]
#![feature(slice_patterns)]
Expand Down
62 changes: 61 additions & 1 deletion src/librustc_mir/transform/cleanup_post_borrowck.rs
Expand Up @@ -33,7 +33,8 @@
use rustc_data_structures::fx::FxHashSet;

use rustc::middle::region;
use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind};
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};
Expand Down Expand Up @@ -135,3 +136,62 @@ impl<'tcx> MutVisitor<'tcx> for DeleteAscribeUserType {
self.super_statement(block, statement, location);
}
}

pub struct CleanFakeReadsAndBorrows;

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 {
fake_borrow_temporaries: FxHashSet(),
};
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);
}
}
5 changes: 4 additions & 1 deletion src/librustc_mir/transform/mod.rs
Expand Up @@ -237,9 +237,12 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
no_landing_pads::NoLandingPads,
simplify_branches::SimplifyBranches::new("initial"),
remove_noop_landing_pads::RemoveNoopLandingPads,
simplify::SimplifyCfg::new("early-opt"),
// Remove all `AscribeUserType` statements.
cleanup_post_borrowck::CleanAscribeUserType,
// Remove all `FakeRead` statements and the borrows that are only
// used for checking matches
cleanup_post_borrowck::CleanFakeReadsAndBorrows,
simplify::SimplifyCfg::new("early-opt"),

// These next passes must be executed together
add_call_guards::CriticalCallEdges,
Expand Down
122 changes: 122 additions & 0 deletions src/test/mir-opt/remove_fake_borrows.rs
@@ -0,0 +1,122 @@
// Test that the fake borrows for matches are removed after borrow checking.

// ignore-wasm32-bare

#![feature(nll)]

fn match_guard(x: Option<&&i32>) -> i32 {
match x {
Some(0) if true => 0,
_ => 1,
}
}

fn main() {
match_guard(None);
}

// END RUST SOURCE

// START rustc.match_guard.CleanFakeReadsAndBorrows.before.mir
// bb0: {
// FakeRead(ForMatchedPlace, _1);
// _2 = discriminant(_1);
// _3 = &shallow _1;
// _4 = &shallow ((_1 as Some).0: &'<empty> &'<empty> i32);
// _5 = &shallow (*((_1 as Some).0: &'<empty> &'<empty> i32));
// _6 = &shallow (*(*((_1 as Some).0: &'<empty> &'<empty> i32)));
// switchInt(move _2) -> [1isize: bb6, otherwise: bb4];
// }
// bb1: {
// _0 = const 0i32;
// goto -> bb9;
// }
// bb2: {
// _0 = const 1i32;
// goto -> bb9;
// }
// bb3: {
// FakeRead(ForMatchGuard, _3);
// FakeRead(ForMatchGuard, _4);
// FakeRead(ForMatchGuard, _5);
// FakeRead(ForMatchGuard, _6);
// goto -> bb7;
// }
// bb4: {
// FakeRead(ForMatchGuard, _3);
// FakeRead(ForMatchGuard, _4);
// FakeRead(ForMatchGuard, _5);
// FakeRead(ForMatchGuard, _6);
// goto -> bb2;
// }
// bb5: {
// unreachable;
// }
// bb6: {
// switchInt((*(*((_1 as Some).0: &'<empty> &'<empty> i32)))) -> [0i32: bb3, otherwise: bb4];
// }
// bb7: {
// goto -> bb1;
// }
// bb8: {
// goto -> bb4;
// }
// bb9: {
// return;
// }
// bb10: {
// resume;
// }
// END rustc.match_guard.CleanFakeReadsAndBorrows.before.mir

// START rustc.match_guard.CleanFakeReadsAndBorrows.after.mir
// bb0: {
// nop;
// _2 = discriminant(_1);
// nop;
// nop;
// nop;
// nop;
// switchInt(move _2) -> [1isize: bb6, otherwise: bb4];
// }
// bb1: {
// _0 = const 0i32;
// goto -> bb9;
// }
// bb2: {
// _0 = const 1i32;
// goto -> bb9;
// }
// bb3: {
// nop;
// nop;
// nop;
// nop;
// goto -> bb7;
// }
// bb4: {
// nop;
// nop;
// nop;
// nop;
// goto -> bb2;
// }
// bb5: {
// unreachable;
// }
// bb6: {
// switchInt((*(*((_1 as Some).0: &'<empty> &'<empty> i32)))) -> [0i32: bb3, otherwise: bb4];
// }
// bb7: {
// goto -> bb1;
// }
// bb8: {
// goto -> bb4;
// }
// bb9: {
// return;
// }
// bb10: {
// resume;
// }
// END rustc.match_guard.CleanFakeReadsAndBorrows.after.mir

0 comments on commit f71f733

Please sign in to comment.