use rustc_middle::mir::traversal::ReversePostorderIter;
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
-use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable};
use rustc_span::Span;
let mut rpo = traversal::reverse_postorder(body);
let ccx = ConstCx::new(tcx, body);
- let (temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo);
+ let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo);
- let promotable_candidates = validate_candidates(&ccx, &temps, &all_candidates);
+ let promotable_candidates = validate_candidates(&ccx, &mut temps, &all_candidates);
let promoted = promote_candidates(body, tcx, temps, promotable_candidates);
self.promoted_fragments.set(promoted);
/// One direct assignment and any number of direct uses.
/// A borrow of this temp is promotable if the assigned
/// value is qualified as constant.
- Defined { location: Location, uses: usize },
+ Defined { location: Location, uses: usize, valid: Result<(), ()> },
/// Any other combination of assignments/uses.
Unpromotable,
/// This temp was part of an rvalue which got extracted
match context {
PlaceContext::MutatingUse(MutatingUseContext::Store)
| PlaceContext::MutatingUse(MutatingUseContext::Call) => {
- *temp = TempState::Defined { location, uses: 0 };
+ *temp = TempState::Defined { location, uses: 0, valid: Err(()) };
return;
}
_ => { /* mark as unpromotable below */ }
/// This wraps an `Item`, and has access to all fields of that `Item` via `Deref` coercion.
struct Validator<'a, 'tcx> {
ccx: &'a ConstCx<'a, 'tcx>,
- temps: &'a IndexVec<Local, TempState>,
+ temps: &'a mut IndexVec<Local, TempState>,
}
impl<'a, 'tcx> std::ops::Deref for Validator<'a, 'tcx> {
struct Unpromotable;
impl<'tcx> Validator<'_, 'tcx> {
- fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
+ fn validate_candidate(&mut self, candidate: Candidate) -> Result<(), Unpromotable> {
let loc = candidate.location;
let statement = &self.body[loc.block].statements[loc.statement_index];
match &statement.kind {
}
// FIXME(eddyb) maybe cache this?
- fn qualif_local<Q: qualifs::Qualif>(&self, local: Local) -> bool {
+ fn qualif_local<Q: qualifs::Qualif>(&mut self, local: Local) -> bool {
if let TempState::Defined { location: loc, .. } = self.temps[local] {
let num_stmts = self.body[loc.block].statements.len();
}
}
- // FIXME(eddyb) maybe cache this?
- fn validate_local(&self, local: Local) -> Result<(), Unpromotable> {
- if let TempState::Defined { location: loc, .. } = self.temps[local] {
- let block = &self.body[loc.block];
- let num_stmts = block.statements.len();
-
- if loc.statement_index < num_stmts {
- let statement = &block.statements[loc.statement_index];
- match &statement.kind {
- StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs),
- _ => {
- span_bug!(
- statement.source_info.span,
- "{:?} is not an assignment",
- statement
- );
- }
- }
- } else {
- let terminator = block.terminator();
- match &terminator.kind {
- TerminatorKind::Call { func, args, .. } => self.validate_call(func, args),
- TerminatorKind::Yield { .. } => Err(Unpromotable),
- kind => {
- span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
+ fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> {
+ if let TempState::Defined { location: loc, uses, valid } = self.temps[local] {
+ valid.or_else(|_| {
+ let ok = {
+ let block = &self.body[loc.block];
+ let num_stmts = block.statements.len();
+
+ if loc.statement_index < num_stmts {
+ let statement = &block.statements[loc.statement_index];
+ match &statement.kind {
+ StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs),
+ _ => {
+ span_bug!(
+ statement.source_info.span,
+ "{:?} is not an assignment",
+ statement
+ );
+ }
+ }
+ } else {
+ let terminator = block.terminator();
+ match &terminator.kind {
+ TerminatorKind::Call { func, args, .. } => {
+ self.validate_call(func, args)
+ }
+ TerminatorKind::Yield { .. } => Err(Unpromotable),
+ kind => {
+ span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
+ }
+ }
}
- }
- }
+ };
+ self.temps[local] = match ok {
+ Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) },
+ Err(_) => TempState::Unpromotable,
+ };
+ ok
+ })
} else {
Err(Unpromotable)
}
}
- fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> {
+ fn validate_place(&mut self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> {
match place.last_projection() {
None => self.validate_local(place.local),
Some((place_base, elem)) => {
}
}
- fn validate_operand(&self, operand: &Operand<'tcx>) -> Result<(), Unpromotable> {
+ fn validate_operand(&mut self, operand: &Operand<'tcx>) -> Result<(), Unpromotable> {
match operand {
Operand::Copy(place) | Operand::Move(place) => self.validate_place(place.as_ref()),
}
}
- fn validate_ref(&self, kind: BorrowKind, place: &Place<'tcx>) -> Result<(), Unpromotable> {
+ fn validate_ref(&mut self, kind: BorrowKind, place: &Place<'tcx>) -> Result<(), Unpromotable> {
match kind {
// Reject these borrow types just to be safe.
// FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase.
Ok(())
}
- fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
+ fn validate_rvalue(&mut self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
match rvalue {
Rvalue::Use(operand) | Rvalue::Repeat(operand, _) => {
self.validate_operand(operand)?;
Rvalue::ThreadLocalRef(_) => return Err(Unpromotable),
- Rvalue::Cast(kind, operand, cast_ty) => {
- if matches!(kind, CastKind::Misc) {
- let operand_ty = operand.ty(self.body, self.tcx);
- let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
- let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast");
- if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
- // ptr-to-int casts are not possible in consts and thus not promotable
- return Err(Unpromotable);
- }
- // int-to-ptr casts are fine, they just use the integer value at pointer type.
- }
+ // ptr-to-int casts are not possible in consts and thus not promotable
+ Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => return Err(Unpromotable),
+ // all ohter casts including int-to-ptr casts are fine, they just use the integer value
+ // at pointer type.
+ Rvalue::Cast(_, operand, _) => {
self.validate_operand(operand)?;
}
}
fn validate_call(
- &self,
+ &mut self,
callee: &Operand<'tcx>,
args: &[Operand<'tcx>],
) -> Result<(), Unpromotable> {
// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
pub fn validate_candidates(
ccx: &ConstCx<'_, '_>,
- temps: &IndexVec<Local, TempState>,
+ temps: &mut IndexVec<Local, TempState>,
candidates: &[Candidate],
) -> Vec<Candidate> {
- let validator = Validator { ccx, temps };
+ let mut validator = Validator { ccx, temps };
candidates
.iter()
fn promote_temp(&mut self, temp: Local) -> Local {
let old_keep_original = self.keep_original;
let loc = match self.temps[temp] {
- TempState::Defined { location, uses } if uses > 0 => {
+ TempState::Defined { location, uses, .. } if uses > 0 => {
if uses > 1 {
self.keep_original = true;
}
} else {
let terminator = self.source[loc.block].terminator_mut();
let target = match terminator.kind {
- TerminatorKind::Call { destination: Some((_, target)), .. } => target,
+ TerminatorKind::Call { target: Some(target), .. } => target,
ref kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
func,
args,
cleanup: None,
- destination: Some((Place::from(new_temp), new_target)),
+ destination: Place::from(new_temp),
+ target: Some(new_target),
from_hir_call,
fn_span,
},
{
if let Operand::Constant(box Constant { literal, .. }) = func {
if let ty::FnDef(def_id, _) = *literal.ty().kind() {
- if let Some((destination_place, _)) = destination {
- if destination_place == place {
- if ccx.tcx.is_const_fn(def_id) {
- return true;
- }
+ if destination == place {
+ if ccx.tcx.is_const_fn(def_id) {
+ return true;
}
}
}