summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_transform/src/const_prop.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src/const_prop.rs')
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs112
1 files changed, 41 insertions, 71 deletions
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 1bb45341e..1ba1951af 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -7,7 +7,7 @@ use rustc_const_eval::const_eval::CheckAlignment;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_index::bit_set::BitSet;
-use rustc_index::vec::{IndexSlice, IndexVec};
+use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::visit::{
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
@@ -18,13 +18,12 @@ use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeVisi
use rustc_span::{def_id::DefId, Span, DUMMY_SP};
use rustc_target::abi::{self, Align, HasDataLayout, Size, TargetDataLayout};
use rustc_target::spec::abi::Abi as CallAbi;
-use rustc_trait_selection::traits;
use crate::MirPass;
use rustc_const_eval::interpret::{
- self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame,
- ImmTy, Immediate, InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer,
- Scalar, StackPopCleanup,
+ self, compile_time_machine, AllocId, ConstAllocation, ConstValue, Frame, ImmTy, Immediate,
+ InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, PlaceTy, Pointer, Scalar,
+ StackPopCleanup,
};
/// The maximum number of bytes that we'll allocate space for a local or the return value.
@@ -84,42 +83,6 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
return;
}
- // Check if it's even possible to satisfy the 'where' clauses
- // for this item.
- // This branch will never be taken for any normal function.
- // However, it's possible to `#!feature(trivial_bounds)]` to write
- // a function with impossible to satisfy clauses, e.g.:
- // `fn foo() where String: Copy {}`
- //
- // We don't usually need to worry about this kind of case,
- // since we would get a compilation error if the user tried
- // to call it. However, since we can do const propagation
- // even without any calls to the function, we need to make
- // sure that it even makes sense to try to evaluate the body.
- // If there are unsatisfiable where clauses, then all bets are
- // off, and we just give up.
- //
- // We manually filter the predicates, skipping anything that's not
- // "global". We are in a potentially generic context
- // (e.g. we are evaluating a function without substituting generic
- // parameters, so this filtering serves two purposes:
- //
- // 1. We skip evaluating any predicates that we would
- // never be able prove are unsatisfiable (e.g. `<T as Foo>`
- // 2. We avoid trying to normalize predicates involving generic
- // parameters (e.g. `<T as Foo>::MyItem`). This can confuse
- // the normalization code (leading to cycle errors), since
- // it's usually never invoked in this way.
- let predicates = tcx
- .predicates_of(def_id.to_def_id())
- .predicates
- .iter()
- .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
- if traits::impossible_predicates(tcx, traits::elaborate(tcx, predicates).collect()) {
- trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id);
- return;
- }
-
trace!("ConstProp starting for {:?}", def_id);
let dummy_body = &Body::new(
@@ -428,7 +391,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
/// Returns the value, if any, of evaluating `c`.
fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<OpTy<'tcx>> {
// FIXME we need to revisit this for #67176
- if c.needs_subst() {
+ if c.has_param() {
return None;
}
@@ -501,16 +464,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
return None;
}
- // Do not try creating references, nor any types with potentially-complex
- // invariants. This avoids an issue where checking validity would do a
- // bunch of work generating a nice message about the invariant violation,
- // only to not show it to anyone (since this isn't the lint).
- Rvalue::Cast(CastKind::Transmute, op, dst_ty) if !dst_ty.is_primitive() => {
- trace!("skipping Transmute of {:?} to {:?}", op, dst_ty);
-
- return None;
- }
-
// There's no other checking to do at this time.
Rvalue::Aggregate(..)
| Rvalue::Use(..)
@@ -527,7 +480,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
// FIXME we need to revisit this for #67176
- if rvalue.needs_subst() {
+ if rvalue.has_param() {
return None;
}
if !rvalue
@@ -628,18 +581,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
trace!("attempting to replace {:?} with {:?}", rval, value);
- if let Err(e) = self.ecx.const_validate_operand(
- value,
- vec![],
- // FIXME: is ref tracking too expensive?
- // FIXME: what is the point of ref tracking if we do not even check the tracked refs?
- &mut interpret::RefTracking::empty(),
- CtfeValidationMode::Regular,
- ) {
- trace!("validation error, attempt failed: {:?}", e);
- return;
- }
-
// FIXME> figure out what to do when read_immediate_raw fails
let imm = self.ecx.read_immediate_raw(value).ok();
@@ -773,13 +714,22 @@ impl CanConstProp {
}
}
-impl Visitor<'_> for CanConstProp {
+impl<'tcx> Visitor<'tcx> for CanConstProp {
+ fn visit_place(&mut self, place: &Place<'tcx>, mut context: PlaceContext, loc: Location) {
+ use rustc_middle::mir::visit::PlaceContext::*;
+
+ // Dereferencing just read the addess of `place.local`.
+ if place.projection.first() == Some(&PlaceElem::Deref) {
+ context = NonMutatingUse(NonMutatingUseContext::Copy);
+ }
+
+ self.visit_local(place.local, context, loc);
+ self.visit_projection(place.as_ref(), context, loc);
+ }
+
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
use rustc_middle::mir::visit::PlaceContext::*;
match context {
- // Projections are fine, because `&mut foo.x` will be caught by
- // `MutatingUseContext::Borrow` elsewhere.
- MutatingUse(MutatingUseContext::Projection)
// These are just stores, where the storing is not propagatable, but there may be later
// mutations of the same local via `Store`
| MutatingUse(MutatingUseContext::Call)
@@ -810,7 +760,7 @@ impl Visitor<'_> for CanConstProp {
NonMutatingUse(NonMutatingUseContext::Copy)
| NonMutatingUse(NonMutatingUseContext::Move)
| NonMutatingUse(NonMutatingUseContext::Inspect)
- | NonMutatingUse(NonMutatingUseContext::Projection)
+ | NonMutatingUse(NonMutatingUseContext::PlaceMention)
| NonUse(_) => {}
// These could be propagated with a smarter analysis or just some careful thinking about
@@ -825,9 +775,11 @@ impl Visitor<'_> for CanConstProp {
| NonMutatingUse(NonMutatingUseContext::AddressOf)
| MutatingUse(MutatingUseContext::Borrow)
| MutatingUse(MutatingUseContext::AddressOf) => {
- trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
+ trace!("local {:?} can't be propagated because it's used: {:?}", local, context);
self.can_const_prop[local] = ConstPropMode::NoPropagation;
}
+ MutatingUse(MutatingUseContext::Projection)
+ | NonMutatingUse(NonMutatingUseContext::Projection) => bug!("visit_place should not pass {context:?} for {local:?}"),
}
}
}
@@ -853,6 +805,24 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
}
}
+ fn process_projection_elem(
+ &mut self,
+ elem: PlaceElem<'tcx>,
+ _: Location,
+ ) -> Option<PlaceElem<'tcx>> {
+ if let PlaceElem::Index(local) = elem
+ && let Some(value) = self.get_const(local.into())
+ && self.should_const_prop(&value)
+ && let interpret::Operand::Immediate(interpret::Immediate::Scalar(scalar)) = *value
+ && let Ok(offset) = scalar.to_target_usize(&self.tcx)
+ && let Some(min_length) = offset.checked_add(1)
+ {
+ Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false })
+ } else {
+ None
+ }
+ }
+
fn visit_assign(
&mut self,
place: &mut Place<'tcx>,