use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
-use rustc::ty::TyCtxt;
+use rustc::ty::{self, TyCtxt};
use syntax_pos::Span;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
-use std::iter;
-use std::mem;
-use std::usize;
+use std::{cmp, iter, mem, usize};
/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
}
struct Promoter<'a, 'tcx: 'a> {
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: &'a mut Mir<'tcx>,
promoted: Mir<'tcx>,
temps: &'a mut IndexVec<Local, TempState>,
+ extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>,
/// If true, all nested temps are also kept in the
/// source MIR, not moved to the promoted MIR.
}
fn promote_candidate(mut self, candidate: Candidate) {
- let span = self.promoted.span;
- let new_operand = Operand::Constant(box Constant {
- span,
- ty: self.promoted.return_ty(),
- literal: Literal::Promoted {
+ let mut rvalue = {
+ let promoted = &mut self.promoted;
+ let literal = Literal::Promoted {
index: Promoted::new(self.source.promoted.len())
- }
- });
- let mut rvalue = match candidate {
- Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
- let ref mut statement = self.source[bb].statements[stmt_idx];
- match statement.kind {
- StatementKind::Assign(_, ref mut rvalue) => {
- mem::replace(rvalue, Rvalue::Use(new_operand))
+ };
+ let operand = |ty, span| {
+ promoted.span = span;
+ promoted.local_decls[RETURN_PLACE] =
+ LocalDecl::new_return_place(ty, span);
+ Operand::Constant(box Constant {
+ span,
+ ty,
+ literal
+ })
+ };
+ let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
+ match candidate {
+ Candidate::Ref(loc) => {
+ let ref mut statement = blocks[loc.block].statements[loc.statement_index];
+ match statement.kind {
+ StatementKind::Assign(_, Rvalue::Ref(r, bk, ref mut place)) => {
+ let ty = place.ty(local_decls, self.tcx).to_ty(self.tcx);
+ let ref_ty = self.tcx.mk_ref(r,
+ ty::TypeAndMut {
+ ty,
+ mutbl: bk.to_mutbl_lossy()
+ }
+ );
+ let span = statement.source_info.span;
+
+ // Create a temp to hold the promoted reference.
+ // This is because `*r` requires `r` to be a local,
+ // otherwise we would use the `promoted` directly.
+ let mut promoted_ref = LocalDecl::new_temp(ref_ty, span);
+ promoted_ref.source_info = statement.source_info;
+ let promoted_ref = local_decls.push(promoted_ref);
+ assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
+ self.extra_statements.push((loc, Statement {
+ source_info: statement.source_info,
+ kind: StatementKind::Assign(
+ Place::Local(promoted_ref),
+ Rvalue::Use(operand(ref_ty, span)),
+ )
+ }));
+ let promoted_place = Place::Local(promoted_ref).deref();
+
+ Rvalue::Ref(r, bk, mem::replace(place, promoted_place))
+ }
+ _ => bug!()
}
- _ => bug!()
}
- }
- Candidate::Argument { bb, index } => {
- match self.source[bb].terminator_mut().kind {
- TerminatorKind::Call { ref mut args, .. } => {
- Rvalue::Use(mem::replace(&mut args[index], new_operand))
+ Candidate::Argument { bb, index } => {
+ let terminator = blocks[bb].terminator_mut();
+ match terminator.kind {
+ TerminatorKind::Call { ref mut args, .. } => {
+ let ty = args[index].ty(local_decls, self.tcx);
+ let span = terminator.source_info.span;
+ Rvalue::Use(mem::replace(&mut args[index], operand(ty, span)))
+ }
+ _ => bug!()
}
- _ => bug!()
}
}
};
+
+ assert_eq!(self.new_block(), START_BLOCK);
self.visit_rvalue(&mut rvalue, Location {
block: BasicBlock::new(0),
statement_index: usize::MAX
});
+ let span = self.promoted.span;
self.assign(RETURN_PLACE, rvalue, span);
self.source.promoted.push(self.promoted);
}
candidates: Vec<Candidate>) {
// Visit candidates in reverse, in case they're nested.
debug!("promote_candidates({:?})", candidates);
+
+ let mut extra_statements = vec![];
for candidate in candidates.into_iter().rev() {
- let (span, ty) = match candidate {
- Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
- let statement = &mir[bb].statements[stmt_idx];
- let dest = match statement.kind {
- StatementKind::Assign(ref dest, _) => dest,
- _ => {
- span_bug!(statement.source_info.span,
- "expected assignment to promote");
- }
- };
- if let Place::Local(index) = *dest {
- if temps[index] == TempState::PromotedOut {
- // Already promoted.
- continue;
+ match candidate {
+ Candidate::Ref(Location { block, statement_index }) => {
+ match mir[block].statements[statement_index].kind {
+ StatementKind::Assign(Place::Local(local), _) => {
+ if temps[local] == TempState::PromotedOut {
+ // Already promoted.
+ continue;
+ }
}
+ _ => {}
}
- (statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx))
- }
- Candidate::Argument { bb, index } => {
- let terminator = mir[bb].terminator();
- let ty = match terminator.kind {
- TerminatorKind::Call { ref args, .. } => {
- args[index].ty(mir, tcx)
- }
- _ => {
- span_bug!(terminator.source_info.span,
- "expected call argument to promote");
- }
- };
- (terminator.source_info.span, ty)
}
- };
+ Candidate::Argument { .. } => {}
+ }
+
- // Declare return place local
- let initial_locals = iter::once(LocalDecl::new_return_place(ty, span))
- .collect();
+ // Declare return place local so that `Mir::new` doesn't complain.
+ let initial_locals = iter::once(
+ LocalDecl::new_return_place(tcx.types.never, mir.span)
+ ).collect();
let mut promoter = Promoter {
promoted: Mir::new(
initial_locals,
0,
vec![],
- span
+ mir.span
),
+ tcx,
source: mir,
temps: &mut temps,
+ extra_statements: &mut extra_statements,
keep_original: false
};
- assert_eq!(promoter.new_block(), START_BLOCK);
promoter.promote_candidate(candidate);
}
+ // Insert each of `extra_statements` before its indicated location, which
+ // has to be done in reverse location order, to not invalidate the rest.
+ extra_statements.sort_by_key(|&(loc, _)| cmp::Reverse(loc));
+ for (loc, statement) in extra_statements {
+ mir[loc.block].statements.insert(loc.statement_index, statement);
+ }
+
// Eliminate assignments to, and drops of promoted temps.
let promoted = |index: Local| temps[index] == TempState::PromotedOut;
for block in mir.basic_blocks_mut() {