//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.
-use rustc::hir::def_id::DefId;
use rustc::mir::traversal::ReversePostorder;
use rustc::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc::mir::*;
use rustc::ty::cast::CastTy;
use rustc::ty::subst::InternalSubsts;
use rustc::ty::{self, List, TyCtxt, TypeFoldable};
+use rustc_hir::def_id::DefId;
+use rustc_span::symbol::sym;
use rustc_span::{Span, DUMMY_SP};
use syntax::ast::LitKind;
-use syntax::symbol::sym;
use rustc_index::vec::{Idx, IndexVec};
use rustc_target::spec::abi::Abi;
use std::cell::Cell;
-use std::{iter, mem, usize};
+use std::{cmp, iter, mem, usize};
+use crate::const_eval::{is_const_fn, is_unstable_const_fn};
use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstKind, Item};
use crate::transform::{MirPass, MirSource};
/// errors when promotion of `#[rustc_args_required_const]` arguments fails.
///
/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
-/// newly created `StaticKind::Promoted`.
+/// newly created `Constant`.
#[derive(Default)]
pub struct PromoteTemps<'tcx> {
pub promoted_fragments: Cell<IndexVec<Promoted, BodyAndCache<'tcx>>>,
// We can only promote interior borrows of promotable temps (non-temps
// don't get promoted anyway).
- let base = match place.base {
- PlaceBase::Local(local) => local,
- _ => return Err(Unpromotable),
- };
- self.validate_local(base)?;
+ self.validate_local(place.local)?;
if place.projection.contains(&ProjectionElem::Deref) {
return Err(Unpromotable);
}
let mut has_mut_interior =
- self.qualif_local::<qualifs::HasMutInterior>(base);
+ self.qualif_local::<qualifs::HasMutInterior>(place.local);
// HACK(eddyb) this should compute the same thing as
// `<HasMutInterior as Qualif>::in_projection` from
// `check_consts::qualifs` but without recursion.
// FIXME(eddyb) this is probably excessive, with
// the exception of `union` member accesses.
let ty =
- Place::ty_from(&place.base, proj_base, *self.body, self.tcx)
+ Place::ty_from(&place.local, proj_base, *self.body, self.tcx)
.projection_ty(self.tcx, elem)
.ty;
if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) {
if has_mut_interior {
return Err(Unpromotable);
}
- if self.qualif_local::<qualifs::NeedsDrop>(base) {
+ if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
return Err(Unpromotable);
}
fn validate_place(&self, place: PlaceRef<'_, 'tcx>) -> Result<(), Unpromotable> {
match place {
- PlaceRef { base: PlaceBase::Local(local), projection: [] } => {
- self.validate_local(*local)
- }
- PlaceRef { base: PlaceBase::Static(_), projection: [] } => {
- bug!("qualifying already promoted MIR")
- }
- PlaceRef { base: _, projection: [proj_base @ .., elem] } => {
+ PlaceRef { local, projection: [] } => self.validate_local(*local),
+ PlaceRef { local: _, projection: [proj_base @ .., elem] } => {
match *elem {
ProjectionElem::Deref | ProjectionElem::Downcast(..) => {
return Err(Unpromotable);
ProjectionElem::Field(..) => {
if self.const_kind.is_none() {
let base_ty =
- Place::ty_from(place.base, proj_base, *self.body, self.tcx).ty;
+ Place::ty_from(place.local, proj_base, *self.body, self.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No promotion of union field accesses.
if def.is_union() {
}
}
- self.validate_place(PlaceRef { base: place.base, projection: proj_base })
+ self.validate_place(PlaceRef { local: place.local, projection: proj_base })
}
}
}
// Raw reborrows can come from reference to pointer coercions,
// so are allowed.
if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
- let base_ty = Place::ty_from(&place.base, proj_base, *self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, *self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.kind {
- return self
- .validate_place(PlaceRef { base: &place.base, projection: proj_base });
+ return self.validate_place(PlaceRef {
+ local: &place.local,
+ projection: proj_base,
+ });
}
}
Err(Unpromotable)
// Special-case reborrows to be more like a copy of the reference.
let mut place = place.as_ref();
if let [proj_base @ .., ProjectionElem::Deref] = &place.projection {
- let base_ty = Place::ty_from(&place.base, proj_base, *self.body, self.tcx).ty;
+ let base_ty = Place::ty_from(&place.local, proj_base, *self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.kind {
- place = PlaceRef { base: &place.base, projection: proj_base };
+ place = PlaceRef { local: &place.local, projection: proj_base };
}
}
// HACK(eddyb) this should compute the same thing as
// `<HasMutInterior as Qualif>::in_projection` from
// `check_consts::qualifs` but without recursion.
- let mut has_mut_interior = match place.base {
- PlaceBase::Local(local) => self.qualif_local::<qualifs::HasMutInterior>(*local),
- PlaceBase::Static(_) => false,
- };
+ let mut has_mut_interior =
+ self.qualif_local::<qualifs::HasMutInterior>(*place.local);
if has_mut_interior {
let mut place_projection = place.projection;
// FIXME(eddyb) use a forward loop instead of a reverse one.
while let [proj_base @ .., elem] = place_projection {
// FIXME(eddyb) this is probably excessive, with
// the exception of `union` member accesses.
- let ty = Place::ty_from(place.base, proj_base, *self.body, self.tcx)
+ let ty = Place::ty_from(place.local, proj_base, *self.body, self.tcx)
.projection_ty(self.tcx, elem)
.ty;
if ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) {
let is_const_fn = match fn_ty.kind {
ty::FnDef(def_id, _) => {
- self.tcx.is_const_fn(def_id)
- || self.tcx.is_unstable_const_fn(def_id).is_some()
+ is_const_fn(self.tcx, def_id)
+ || is_unstable_const_fn(self.tcx, def_id).is_some()
|| is_lang_panic_fn(self.tcx, self.def_id)
}
_ => false,
source: &'a mut BodyAndCache<'tcx>,
promoted: BodyAndCache<'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.
candidate: Candidate,
next_promoted_id: usize,
) -> Option<BodyAndCache<'tcx>> {
- let mut operand = {
+ let mut rvalue = {
let promoted = &mut self.promoted;
let promoted_id = Promoted::new(next_promoted_id);
let tcx = self.tcx;
- let mut promoted_place = |ty, span| {
+ let mut promoted_operand = |ty, span| {
promoted.span = span;
promoted.local_decls[RETURN_PLACE] = LocalDecl::new_return_place(ty, span);
- Place {
- base: PlaceBase::Static(box Static {
- kind: StaticKind::Promoted(
- promoted_id,
+
+ Operand::Constant(Box::new(Constant {
+ span,
+ user_ty: None,
+ literal: tcx.mk_const(ty::Const {
+ ty,
+ val: ty::ConstKind::Unevaluated(
+ def_id,
InternalSubsts::identity_for_item(tcx, def_id),
+ Some(promoted_id),
),
- ty,
- def_id,
}),
- projection: List::empty(),
- }
+ }))
};
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(box (_, Rvalue::Ref(_, _, ref mut place))) => {
+ StatementKind::Assign(box (
+ _,
+ Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
+ )) => {
// Use the underlying local for this (necessarily interior) borrow.
- let ty = place.base.ty(local_decls).ty;
+ let ty = local_decls.local_decls()[place.local].ty;
let span = statement.source_info.span;
- Operand::Move(Place {
- base: mem::replace(&mut place.base, promoted_place(ty, span).base),
- projection: List::empty(),
- })
+ let ref_ty = tcx.mk_ref(
+ tcx.lifetimes.re_static,
+ ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
+ );
+
+ *region = tcx.lifetimes.re_static;
+
+ let mut projection = vec![PlaceElem::Deref];
+ projection.extend(place.projection);
+ place.projection = tcx.intern_place_elems(&projection);
+
+ // 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);
+
+ let promoted_ref_statement = Statement {
+ source_info: statement.source_info,
+ kind: StatementKind::Assign(Box::new((
+ Place::from(promoted_ref),
+ Rvalue::Use(promoted_operand(ref_ty, span)),
+ ))),
+ };
+ self.extra_statements.push((loc, promoted_ref_statement));
+
+ Rvalue::Ref(
+ tcx.lifetimes.re_static,
+ borrow_kind,
+ Place {
+ local: mem::replace(&mut place.local, promoted_ref),
+ projection: List::empty(),
+ },
+ )
}
_ => bug!(),
}
StatementKind::Assign(box (_, Rvalue::Repeat(ref mut operand, _))) => {
let ty = operand.ty(local_decls, self.tcx);
let span = statement.source_info.span;
- mem::replace(operand, Operand::Copy(promoted_place(ty, span)))
+
+ Rvalue::Use(mem::replace(operand, promoted_operand(ty, span)))
}
_ => bug!(),
}
TerminatorKind::Call { ref mut args, .. } => {
let ty = args[index].ty(local_decls, self.tcx);
let span = terminator.source_info.span;
- let operand = Operand::Copy(promoted_place(ty, span));
- mem::replace(&mut args[index], operand)
+
+ Rvalue::Use(mem::replace(&mut args[index], promoted_operand(ty, span)))
}
// We expected a `TerminatorKind::Call` for which we'd like to promote an
// argument. `qualify_consts` saw a `TerminatorKind::Call` here, but
};
assert_eq!(self.new_block(), START_BLOCK);
- self.visit_operand(
- &mut operand,
+ self.visit_rvalue(
+ &mut rvalue,
Location { block: BasicBlock::new(0), statement_index: usize::MAX },
);
let span = self.promoted.span;
- self.assign(RETURN_PLACE, Rvalue::Use(operand), span);
+ self.assign(RETURN_PLACE, rvalue, span);
Some(self.promoted)
}
}
let mut promotions = IndexVec::new();
+ let mut extra_statements = vec![];
for candidate in candidates.into_iter().rev() {
match candidate {
Candidate::Repeat(Location { block, statement_index })
let initial_locals =
iter::once(LocalDecl::new_return_place(tcx.types.never, body.span)).collect();
+ let mut promoted = Body::new(
+ IndexVec::new(),
+ // FIXME: maybe try to filter this to avoid blowing up
+ // memory usage?
+ body.source_scopes.clone(),
+ initial_locals,
+ IndexVec::new(),
+ 0,
+ vec![],
+ body.span,
+ vec![],
+ body.generator_kind,
+ );
+ promoted.ignore_interior_mut_in_const_validation = true;
+
let promoter = Promoter {
- promoted: BodyAndCache::new(Body::new(
- IndexVec::new(),
- // FIXME: maybe try to filter this to avoid blowing up
- // memory usage?
- body.source_scopes.clone(),
- initial_locals,
- IndexVec::new(),
- 0,
- vec![],
- body.span,
- vec![],
- body.generator_kind,
- )),
+ promoted: BodyAndCache::new(promoted),
tcx,
source: body,
temps: &mut temps,
+ extra_statements: &mut extra_statements,
keep_original: false,
};
}
}
+ // 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 {
+ body[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 body.basic_blocks_mut() {