mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	 5350edb9e8
			
		
	
	
		5350edb9e8
		
	
	
	
	
		
			
			Because it's almost always static. This makes `impl IntoDiagnosticArg for DiagnosticArgValue` trivial, which is nice. There are a few diagnostics constructed in `compiler/rustc_mir_build/src/check_unsafety.rs` and `compiler/rustc_mir_transform/src/errors.rs` that now need symbols converted to `String` with `to_string` instead of `&str` with `as_str`, but that' no big deal, and worth it for the simplifications elsewhere.
		
			
				
	
	
		
			162 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Propagates constants for early reporting of statically known
 | |
| //! assertion failures
 | |
| 
 | |
| use rustc_index::bit_set::BitSet;
 | |
| use rustc_index::IndexVec;
 | |
| use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
 | |
| use rustc_middle::mir::*;
 | |
| use rustc_middle::ty::{ParamEnv, TyCtxt};
 | |
| use rustc_target::abi::Size;
 | |
| 
 | |
| /// The maximum number of bytes that we'll allocate space for a local or the return value.
 | |
| /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
 | |
| /// Severely regress performance.
 | |
| const MAX_ALLOC_LIMIT: u64 = 1024;
 | |
| 
 | |
| /// Macro for machine-specific `InterpError` without allocation.
 | |
| /// (These will never be shown to the user, but they help diagnose ICEs.)
 | |
| pub(crate) macro throw_machine_stop_str($($tt:tt)*) {{
 | |
|     // We make a new local type for it. The type itself does not carry any information,
 | |
|     // but its vtable (for the `MachineStopType` trait) does.
 | |
|     #[derive(Debug)]
 | |
|     struct Zst;
 | |
|     // Printing this type shows the desired string.
 | |
|     impl std::fmt::Display for Zst {
 | |
|         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | |
|             write!(f, $($tt)*)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     impl rustc_middle::mir::interpret::MachineStopType for Zst {
 | |
|         fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
 | |
|             self.to_string().into()
 | |
|         }
 | |
| 
 | |
|         fn add_args(
 | |
|             self: Box<Self>,
 | |
|             _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue),
 | |
|         ) {}
 | |
|     }
 | |
|     throw_machine_stop!(Zst)
 | |
| }}
 | |
| 
 | |
| /// The mode that `ConstProp` is allowed to run in for a given `Local`.
 | |
| #[derive(Clone, Copy, Debug, PartialEq)]
 | |
| pub enum ConstPropMode {
 | |
|     /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
 | |
|     FullConstProp,
 | |
|     /// The `Local` can only be propagated into and from its own block.
 | |
|     OnlyInsideOwnBlock,
 | |
|     /// The `Local` cannot be part of propagation at all. Any statement
 | |
|     /// referencing it either for reading or writing will not get propagated.
 | |
|     NoPropagation,
 | |
| }
 | |
| 
 | |
| pub struct CanConstProp {
 | |
|     can_const_prop: IndexVec<Local, ConstPropMode>,
 | |
|     // False at the beginning. Once set, no more assignments are allowed to that local.
 | |
|     found_assignment: BitSet<Local>,
 | |
| }
 | |
| 
 | |
| impl CanConstProp {
 | |
|     /// Returns true if `local` can be propagated
 | |
|     pub fn check<'tcx>(
 | |
|         tcx: TyCtxt<'tcx>,
 | |
|         param_env: ParamEnv<'tcx>,
 | |
|         body: &Body<'tcx>,
 | |
|     ) -> IndexVec<Local, ConstPropMode> {
 | |
|         let mut cpv = CanConstProp {
 | |
|             can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
 | |
|             found_assignment: BitSet::new_empty(body.local_decls.len()),
 | |
|         };
 | |
|         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
 | |
|             let ty = body.local_decls[local].ty;
 | |
|             match tcx.layout_of(param_env.and(ty)) {
 | |
|                 Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
 | |
|                 // Either the layout fails to compute, then we can't use this local anyway
 | |
|                 // or the local is too large, then we don't want to.
 | |
|                 _ => {
 | |
|                     *val = ConstPropMode::NoPropagation;
 | |
|                     continue;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         // Consider that arguments are assigned on entry.
 | |
|         for arg in body.args_iter() {
 | |
|             cpv.found_assignment.insert(arg);
 | |
|         }
 | |
|         cpv.visit_body(body);
 | |
|         cpv.can_const_prop
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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 {
 | |
|             // 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)
 | |
|             | MutatingUse(MutatingUseContext::AsmOutput)
 | |
|             | MutatingUse(MutatingUseContext::Deinit)
 | |
|             // Actual store that can possibly even propagate a value
 | |
|             | MutatingUse(MutatingUseContext::Store)
 | |
|             | MutatingUse(MutatingUseContext::SetDiscriminant) => {
 | |
|                 if !self.found_assignment.insert(local) {
 | |
|                     match &mut self.can_const_prop[local] {
 | |
|                         // If the local can only get propagated in its own block, then we don't have
 | |
|                         // to worry about multiple assignments, as we'll nuke the const state at the
 | |
|                         // end of the block anyway, and inside the block we overwrite previous
 | |
|                         // states as applicable.
 | |
|                         ConstPropMode::OnlyInsideOwnBlock => {}
 | |
|                         ConstPropMode::NoPropagation => {}
 | |
|                         other @ ConstPropMode::FullConstProp => {
 | |
|                             trace!(
 | |
|                                 "local {:?} can't be propagated because of multiple assignments. Previous state: {:?}",
 | |
|                                 local, other,
 | |
|                             );
 | |
|                             *other = ConstPropMode::OnlyInsideOwnBlock;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             // Reading constants is allowed an arbitrary number of times
 | |
|             NonMutatingUse(NonMutatingUseContext::Copy)
 | |
|             | NonMutatingUse(NonMutatingUseContext::Move)
 | |
|             | NonMutatingUse(NonMutatingUseContext::Inspect)
 | |
|             | NonMutatingUse(NonMutatingUseContext::PlaceMention)
 | |
|             | NonUse(_) => {}
 | |
| 
 | |
|             // These could be propagated with a smarter analysis or just some careful thinking about
 | |
|             // whether they'd be fine right now.
 | |
|             MutatingUse(MutatingUseContext::Yield)
 | |
|             | MutatingUse(MutatingUseContext::Drop)
 | |
|             | MutatingUse(MutatingUseContext::Retag)
 | |
|             // These can't ever be propagated under any scheme, as we can't reason about indirect
 | |
|             // mutation.
 | |
|             | NonMutatingUse(NonMutatingUseContext::SharedBorrow)
 | |
|             | NonMutatingUse(NonMutatingUseContext::FakeBorrow)
 | |
|             | NonMutatingUse(NonMutatingUseContext::AddressOf)
 | |
|             | MutatingUse(MutatingUseContext::Borrow)
 | |
|             | MutatingUse(MutatingUseContext::AddressOf) => {
 | |
|                 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:?}"),
 | |
|         }
 | |
|     }
 | |
| }
 |