1 //! Propagates constants for early reporting of statically known
4 use crate::const_prop
::CanConstProp
;
5 use crate::const_prop
::ConstPropMachine
;
6 use crate::const_prop
::ConstPropMode
;
8 use rustc_const_eval
::const_eval
::ConstEvalErr
;
9 use rustc_const_eval
::interpret
::Immediate
;
10 use rustc_const_eval
::interpret
::{
11 self, InterpCx
, InterpResult
, LocalState
, LocalValue
, MemoryKind
, OpTy
, Scalar
, StackPopCleanup
,
13 use rustc_hir
::def
::DefKind
;
15 use rustc_index
::bit_set
::BitSet
;
16 use rustc_index
::vec
::IndexVec
;
17 use rustc_middle
::mir
::visit
::Visitor
;
18 use rustc_middle
::mir
::{
19 AssertKind
, BinOp
, Body
, Constant
, Local
, LocalDecl
, Location
, Operand
, Place
, Rvalue
,
20 SourceInfo
, SourceScope
, SourceScopeData
, Statement
, StatementKind
, Terminator
, TerminatorKind
,
23 use rustc_middle
::ty
::layout
::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}
;
24 use rustc_middle
::ty
::InternalSubsts
;
25 use rustc_middle
::ty
::{self, ConstInt, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitable}
;
26 use rustc_session
::lint
;
28 use rustc_target
::abi
::{HasDataLayout, Size, TargetDataLayout}
;
29 use rustc_trait_selection
::traits
;
32 /// The maximum number of bytes that we'll allocate space for a local or the return value.
33 /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
34 /// Severely regress performance.
35 const MAX_ALLOC_LIMIT
: u64 = 1024;
38 impl<'tcx
> MirLint
<'tcx
> for ConstProp
{
39 fn run_lint(&self, tcx
: TyCtxt
<'tcx
>, body
: &Body
<'tcx
>) {
40 // will be evaluated by miri and produce its errors there
41 if body
.source
.promoted
.is_some() {
45 let def_id
= body
.source
.def_id().expect_local();
46 let is_fn_like
= tcx
.def_kind(def_id
).is_fn_like();
47 let is_assoc_const
= tcx
.def_kind(def_id
) == DefKind
::AssocConst
;
49 // Only run const prop on functions, methods, closures and associated constants
50 if !is_fn_like
&& !is_assoc_const
{
51 // skip anon_const/statics/consts because they'll be evaluated by miri anyway
52 trace
!("ConstProp skipped for {:?}", def_id
);
56 let is_generator
= tcx
.type_of(def_id
.to_def_id()).is_generator();
57 // FIXME(welseywiser) const prop doesn't work on generators because of query cycles
58 // computing their layout.
60 trace
!("ConstProp skipped for generator {:?}", def_id
);
64 // Check if it's even possible to satisfy the 'where' clauses
66 // This branch will never be taken for any normal function.
67 // However, it's possible to `#!feature(trivial_bounds)]` to write
68 // a function with impossible to satisfy clauses, e.g.:
69 // `fn foo() where String: Copy {}`
71 // We don't usually need to worry about this kind of case,
72 // since we would get a compilation error if the user tried
73 // to call it. However, since we can do const propagation
74 // even without any calls to the function, we need to make
75 // sure that it even makes sense to try to evaluate the body.
76 // If there are unsatisfiable where clauses, then all bets are
77 // off, and we just give up.
79 // We manually filter the predicates, skipping anything that's not
80 // "global". We are in a potentially generic context
81 // (e.g. we are evaluating a function without substituting generic
82 // parameters, so this filtering serves two purposes:
84 // 1. We skip evaluating any predicates that we would
85 // never be able prove are unsatisfiable (e.g. `<T as Foo>`
86 // 2. We avoid trying to normalize predicates involving generic
87 // parameters (e.g. `<T as Foo>::MyItem`). This can confuse
88 // the normalization code (leading to cycle errors), since
89 // it's usually never invoked in this way.
91 .predicates_of(def_id
.to_def_id())
94 .filter_map(|(p
, _
)| if p
.is_global() { Some(*p) }
else { None }
);
95 if traits
::impossible_predicates(
97 traits
::elaborate_predicates(tcx
, predicates
).map(|o
| o
.predicate
).collect(),
99 trace
!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id
);
103 trace
!("ConstProp starting for {:?}", def_id
);
105 let dummy_body
= &Body
::new(
107 (*body
.basic_blocks
).clone(),
108 body
.source_scopes
.clone(),
109 body
.local_decls
.clone(),
114 body
.generator_kind(),
115 body
.tainted_by_errors
,
118 // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
119 // constants, instead of just checking for const-folding succeeding.
120 // That would require a uniform one-def no-mutation analysis
121 // and RPO (or recursing when needing the value of a local).
122 let mut optimization_finder
= ConstPropagator
::new(body
, dummy_body
, tcx
);
123 optimization_finder
.visit_body(body
);
125 trace
!("ConstProp done for {:?}", def_id
);
129 /// Finds optimization opportunities on the MIR.
130 struct ConstPropagator
<'mir
, 'tcx
> {
131 ecx
: InterpCx
<'mir
, 'tcx
, ConstPropMachine
<'mir
, 'tcx
>>,
133 param_env
: ParamEnv
<'tcx
>,
134 source_scopes
: &'mir IndexVec
<SourceScope
, SourceScopeData
<'tcx
>>,
135 local_decls
: &'mir IndexVec
<Local
, LocalDecl
<'tcx
>>,
136 // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
137 // the last known `SourceInfo` here and just keep revisiting it.
138 source_info
: Option
<SourceInfo
>,
141 impl<'tcx
> LayoutOfHelpers
<'tcx
> for ConstPropagator
<'_
, 'tcx
> {
142 type LayoutOfResult
= Result
<TyAndLayout
<'tcx
>, LayoutError
<'tcx
>>;
145 fn handle_layout_err(&self, err
: LayoutError
<'tcx
>, _
: Span
, _
: Ty
<'tcx
>) -> LayoutError
<'tcx
> {
150 impl HasDataLayout
for ConstPropagator
<'_
, '_
> {
152 fn data_layout(&self) -> &TargetDataLayout
{
153 &self.tcx
.data_layout
157 impl<'tcx
> ty
::layout
::HasTyCtxt
<'tcx
> for ConstPropagator
<'_
, 'tcx
> {
159 fn tcx(&self) -> TyCtxt
<'tcx
> {
164 impl<'tcx
> ty
::layout
::HasParamEnv
<'tcx
> for ConstPropagator
<'_
, 'tcx
> {
166 fn param_env(&self) -> ty
::ParamEnv
<'tcx
> {
171 impl<'mir
, 'tcx
> ConstPropagator
<'mir
, 'tcx
> {
174 dummy_body
: &'mir Body
<'tcx
>,
176 ) -> ConstPropagator
<'mir
, 'tcx
> {
177 let def_id
= body
.source
.def_id();
178 let substs
= &InternalSubsts
::identity_for_item(tcx
, def_id
);
179 let param_env
= tcx
.param_env_reveal_all_normalized(def_id
);
181 let can_const_prop
= CanConstProp
::check(tcx
, param_env
, body
);
182 let mut only_propagate_inside_block_locals
= BitSet
::new_empty(can_const_prop
.len());
183 for (l
, mode
) in can_const_prop
.iter_enumerated() {
184 if *mode
== ConstPropMode
::OnlyInsideOwnBlock
{
185 only_propagate_inside_block_locals
.insert(l
);
188 let mut ecx
= InterpCx
::new(
190 tcx
.def_span(def_id
),
192 ConstPropMachine
::new(only_propagate_inside_block_locals
, can_const_prop
),
196 .layout_of(body
.bound_return_ty().subst(tcx
, substs
))
198 // Don't bother allocating memory for large values.
199 // I don't know how return types can seem to be unsized but this happens in the
200 // `type/type-unsatisfiable.rs` test.
201 .filter(|ret_layout
| {
202 !ret_layout
.is_unsized() && ret_layout
.size
< Size
::from_bytes(MAX_ALLOC_LIMIT
)
204 .unwrap_or_else(|| ecx
.layout_of(tcx
.types
.unit
).unwrap());
207 .allocate(ret_layout
, MemoryKind
::Stack
)
208 .expect("couldn't perform small allocation")
211 ecx
.push_stack_frame(
212 Instance
::new(def_id
, substs
),
215 StackPopCleanup
::Root { cleanup: false }
,
217 .expect("failed to push initial stack frame");
223 source_scopes
: &dummy_body
.source_scopes
,
224 local_decls
: &dummy_body
.local_decls
,
229 fn get_const(&self, place
: Place
<'tcx
>) -> Option
<OpTy
<'tcx
>> {
230 let op
= match self.ecx
.eval_place_to_op(place
, None
) {
232 if matches
!(*op
, interpret
::Operand
::Immediate(Immediate
::Uninit
)) {
233 // Make sure nobody accidentally uses this value.
239 trace
!("get_const failed: {}", e
);
244 // Try to read the local as an immediate so that if it is representable as a scalar, we can
245 // handle it as such, but otherwise, just return the value as is.
246 Some(match self.ecx
.read_immediate_raw(&op
) {
247 Ok(Ok(imm
)) => imm
.into(),
252 /// Remove `local` from the pool of `Locals`. Allows writing to them,
253 /// but not reading from them anymore.
254 fn remove_const(ecx
: &mut InterpCx
<'mir
, 'tcx
, ConstPropMachine
<'mir
, 'tcx
>>, local
: Local
) {
255 ecx
.frame_mut().locals
[local
] = LocalState
{
256 value
: LocalValue
::Live(interpret
::Operand
::Immediate(interpret
::Immediate
::Uninit
)),
257 layout
: Cell
::new(None
),
261 fn lint_root(&self, source_info
: SourceInfo
) -> Option
<HirId
> {
262 source_info
.scope
.lint_root(self.source_scopes
)
265 fn use_ecx
<F
, T
>(&mut self, source_info
: SourceInfo
, f
: F
) -> Option
<T
>
267 F
: FnOnce(&mut Self) -> InterpResult
<'tcx
, T
>,
269 // Overwrite the PC -- whatever the interpreter does to it does not make any sense anyway.
270 self.ecx
.frame_mut().loc
= Err(source_info
.span
);
272 Ok(val
) => Some(val
),
274 trace
!("InterpCx operation failed: {:?}", error
);
275 // Some errors shouldn't come up because creating them causes
276 // an allocation, which we should avoid. When that happens,
277 // dedicated error variants should be introduced instead.
279 !error
.kind().formatted_string(),
280 "const-prop encountered formatting error: {}",
288 /// Returns the value, if any, of evaluating `c`.
292 _source_info
: SourceInfo
,
293 ) -> Option
<OpTy
<'tcx
>> {
294 // FIXME we need to revisit this for #67176
299 match self.ecx
.const_to_op(&c
.literal
, None
) {
302 let tcx
= self.ecx
.tcx
.at(c
.span
);
303 let err
= ConstEvalErr
::new(&self.ecx
, error
, Some(c
.span
));
304 err
.report_as_error(tcx
, "erroneous constant used");
310 /// Returns the value, if any, of evaluating `place`.
311 fn eval_place(&mut self, place
: Place
<'tcx
>, source_info
: SourceInfo
) -> Option
<OpTy
<'tcx
>> {
312 trace
!("eval_place(place={:?})", place
);
313 self.use_ecx(source_info
, |this
| this
.ecx
.eval_place_to_op(place
, None
))
316 /// Returns the value, if any, of evaluating `op`. Calls upon `eval_constant`
317 /// or `eval_place`, depending on the variant of `Operand` used.
318 fn eval_operand(&mut self, op
: &Operand
<'tcx
>, source_info
: SourceInfo
) -> Option
<OpTy
<'tcx
>> {
320 Operand
::Constant(ref c
) => self.eval_constant(c
, source_info
),
321 Operand
::Move(place
) | Operand
::Copy(place
) => self.eval_place(place
, source_info
),
325 fn report_assert_as_lint(
327 lint
: &'
static lint
::Lint
,
328 source_info
: SourceInfo
,
329 message
: &'
static str,
330 panic
: AssertKind
<impl std
::fmt
::Debug
>,
332 if let Some(lint_root
) = self.lint_root(source_info
) {
333 self.tcx
.struct_span_lint_hir(lint
, lint_root
, source_info
.span
, message
, |lint
| {
334 lint
.span_label(source_info
.span
, format
!("{:?}", panic
))
343 source_info
: SourceInfo
,
345 if let (val
, true) = self.use_ecx(source_info
, |this
| {
346 let val
= this
.ecx
.read_immediate(&this
.ecx
.eval_operand(arg
, None
)?
)?
;
347 let (_res
, overflow
, _ty
) = this
.ecx
.overflowing_unary_op(op
, &val
)?
;
350 // `AssertKind` only has an `OverflowNeg` variant, so make sure that is
351 // appropriate to use.
352 assert_eq
!(op
, UnOp
::Neg
, "Neg is the only UnOp that can overflow");
353 self.report_assert_as_lint(
354 lint
::builtin
::ARITHMETIC_OVERFLOW
,
356 "this arithmetic operation will overflow",
357 AssertKind
::OverflowNeg(val
.to_const_int()),
368 left
: &Operand
<'tcx
>,
369 right
: &Operand
<'tcx
>,
370 source_info
: SourceInfo
,
372 let r
= self.use_ecx(source_info
, |this
| {
373 this
.ecx
.read_immediate(&this
.ecx
.eval_operand(right
, None
)?
)
375 let l
= self.use_ecx(source_info
, |this
| {
376 this
.ecx
.read_immediate(&this
.ecx
.eval_operand(left
, None
)?
)
378 // Check for exceeding shifts *even if* we cannot evaluate the LHS.
379 if op
== BinOp
::Shr
|| op
== BinOp
::Shl
{
381 // We need the type of the LHS. We cannot use `place_layout` as that is the type
382 // of the result, which for checked binops is not the same!
383 let left_ty
= left
.ty(self.local_decls
, self.tcx
);
384 let left_size
= self.ecx
.layout_of(left_ty
).ok()?
.size
;
385 let right_size
= r
.layout
.size
;
386 let r_bits
= r
.to_scalar().to_bits(right_size
).ok();
387 if r_bits
.map_or(false, |b
| b
>= left_size
.bits() as u128
) {
388 debug
!("check_binary_op: reporting assert for {:?}", source_info
);
389 self.report_assert_as_lint(
390 lint
::builtin
::ARITHMETIC_OVERFLOW
,
392 "this arithmetic operation will overflow",
393 AssertKind
::Overflow(
396 Some(l
) => l
.to_const_int(),
397 // Invent a dummy value, the diagnostic ignores it anyway
398 None
=> ConstInt
::new(
399 ScalarInt
::try_from_uint(1_u8, left_size
).unwrap(),
401 left_ty
.is_ptr_sized_integral(),
411 if let (Some(l
), Some(r
)) = (l
, r
) {
412 // The remaining operators are handled through `overflowing_binary_op`.
413 if self.use_ecx(source_info
, |this
| {
414 let (_res
, overflow
, _ty
) = this
.ecx
.overflowing_binary_op(op
, &l
, &r
)?
;
417 self.report_assert_as_lint(
418 lint
::builtin
::ARITHMETIC_OVERFLOW
,
420 "this arithmetic operation will overflow",
421 AssertKind
::Overflow(op
, l
.to_const_int(), r
.to_const_int()),
431 rvalue
: &Rvalue
<'tcx
>,
432 source_info
: SourceInfo
,
435 // Perform any special handling for specific Rvalue types.
436 // Generally, checks here fall into one of two categories:
437 // 1. Additional checking to provide useful lints to the user
438 // - In this case, we will do some validation and then fall through to the
439 // end of the function which evals the assignment.
440 // 2. Working around bugs in other parts of the compiler
441 // - In this case, we'll return `None` from this function to stop evaluation.
443 // Additional checking: give lints to the user if an overflow would occur.
444 // We do this here and not in the `Assert` terminator as that terminator is
445 // only sometimes emitted (overflow checks can be disabled), but we want to always
447 Rvalue
::UnaryOp(op
, arg
) => {
448 trace
!("checking UnaryOp(op = {:?}, arg = {:?})", op
, arg
);
449 self.check_unary_op(*op
, arg
, source_info
)?
;
451 Rvalue
::BinaryOp(op
, box (left
, right
)) => {
452 trace
!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op
, left
, right
);
453 self.check_binary_op(*op
, left
, right
, source_info
)?
;
455 Rvalue
::CheckedBinaryOp(op
, box (left
, right
)) => {
457 "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})",
462 self.check_binary_op(*op
, left
, right
, source_info
)?
;
465 // Do not try creating references (#67862)
466 Rvalue
::AddressOf(_
, place
) | Rvalue
::Ref(_
, _
, place
) => {
467 trace
!("skipping AddressOf | Ref for {:?}", place
);
469 // This may be creating mutable references or immutable references to cells.
470 // If that happens, the pointed to value could be mutated via that reference.
471 // Since we aren't tracking references, the const propagator loses track of what
472 // value the local has right now.
473 // Thus, all locals that have their reference taken
474 // must not take part in propagation.
475 Self::remove_const(&mut self.ecx
, place
.local
);
479 Rvalue
::ThreadLocalRef(def_id
) => {
480 trace
!("skipping ThreadLocalRef({:?})", def_id
);
485 // There's no other checking to do at this time.
486 Rvalue
::Aggregate(..)
488 | Rvalue
::CopyForDeref(..)
492 | Rvalue
::ShallowInitBox(..)
493 | Rvalue
::Discriminant(..)
494 | Rvalue
::NullaryOp(..) => {}
497 // FIXME we need to revisit this for #67176
498 if rvalue
.needs_subst() {
502 .ty(&self.ecx
.frame().body
.local_decls
, *self.ecx
.tcx
)
503 .is_sized(*self.ecx
.tcx
, self.param_env
)
505 // the interpreter doesn't support unsized locals (only unsized arguments),
506 // but rustc does (in a kinda broken way), so we have to skip them here
510 self.use_ecx(source_info
, |this
| this
.ecx
.eval_rvalue_into_place(rvalue
, place
))
514 impl<'tcx
> Visitor
<'tcx
> for ConstPropagator
<'_
, 'tcx
> {
515 fn visit_body(&mut self, body
: &Body
<'tcx
>) {
516 for (bb
, data
) in body
.basic_blocks
.iter_enumerated() {
517 self.visit_basic_block_data(bb
, data
);
521 fn visit_operand(&mut self, operand
: &Operand
<'tcx
>, location
: Location
) {
522 self.super_operand(operand
, location
);
525 fn visit_constant(&mut self, constant
: &Constant
<'tcx
>, location
: Location
) {
526 trace
!("visit_constant: {:?}", constant
);
527 self.super_constant(constant
, location
);
528 self.eval_constant(constant
, self.source_info
.unwrap());
531 fn visit_statement(&mut self, statement
: &Statement
<'tcx
>, location
: Location
) {
532 trace
!("visit_statement: {:?}", statement
);
533 let source_info
= statement
.source_info
;
534 self.source_info
= Some(source_info
);
535 if let StatementKind
::Assign(box (place
, ref rval
)) = statement
.kind
{
536 let can_const_prop
= self.ecx
.machine
.can_const_prop
[place
.local
];
537 if let Some(()) = self.const_prop(rval
, source_info
, place
) {
538 match can_const_prop
{
539 ConstPropMode
::OnlyInsideOwnBlock
=> {
541 "found local restricted to its block. \
542 Will remove it from const-prop after block is finished. Local: {:?}",
546 ConstPropMode
::OnlyPropagateInto
| ConstPropMode
::NoPropagation
=> {
547 trace
!("can't propagate into {:?}", place
);
548 if place
.local
!= RETURN_PLACE
{
549 Self::remove_const(&mut self.ecx
, place
.local
);
552 ConstPropMode
::FullConstProp
=> {}
555 // Const prop failed, so erase the destination, ensuring that whatever happens
556 // from here on, does not know about the previous value.
557 // This is important in case we have
560 // x = SOME_MUTABLE_STATIC;
561 // // x must now be uninit
563 // FIXME: we overzealously erase the entire local, because that's easier to
566 "propagation into {:?} failed.
567 Nuking the entire site from orbit, it's the only way to be sure",
570 Self::remove_const(&mut self.ecx
, place
.local
);
573 match statement
.kind
{
574 StatementKind
::SetDiscriminant { ref place, .. }
=> {
575 match self.ecx
.machine
.can_const_prop
[place
.local
] {
576 ConstPropMode
::FullConstProp
| ConstPropMode
::OnlyInsideOwnBlock
=> {
578 .use_ecx(source_info
, |this
| this
.ecx
.statement(statement
))
581 trace
!("propped discriminant into {:?}", place
);
583 Self::remove_const(&mut self.ecx
, place
.local
);
586 ConstPropMode
::OnlyPropagateInto
| ConstPropMode
::NoPropagation
=> {
587 Self::remove_const(&mut self.ecx
, place
.local
);
591 StatementKind
::StorageLive(local
) | StatementKind
::StorageDead(local
) => {
592 let frame
= self.ecx
.frame_mut();
593 frame
.locals
[local
].value
=
594 if let StatementKind
::StorageLive(_
) = statement
.kind
{
595 LocalValue
::Live(interpret
::Operand
::Immediate(
596 interpret
::Immediate
::Uninit
,
606 self.super_statement(statement
, location
);
609 fn visit_terminator(&mut self, terminator
: &Terminator
<'tcx
>, location
: Location
) {
610 let source_info
= terminator
.source_info
;
611 self.source_info
= Some(source_info
);
612 self.super_terminator(terminator
, location
);
613 match &terminator
.kind
{
614 TerminatorKind
::Assert { expected, ref msg, ref cond, .. }
=> {
615 if let Some(ref value
) = self.eval_operand(&cond
, source_info
) {
616 trace
!("assertion on {:?} should be {:?}", value
, expected
);
617 let expected
= Scalar
::from_bool(*expected
);
618 let Ok(value_const
) = self.ecx
.read_scalar(&value
) else {
619 // FIXME should be used use_ecx rather than a local match... but we have
620 // quite a few of these read_scalar/read_immediate that need fixing.
623 if expected
!= value_const
{
628 impl<T
: std
::fmt
::Debug
> std
::fmt
::Debug
for DbgVal
<T
> {
629 fn fmt(&self, fmt
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
631 Self::Val(val
) => val
.fmt(fmt
),
632 Self::Underscore
=> fmt
.write_str("_"),
636 let mut eval_to_int
= |op
| {
637 // This can be `None` if the lhs wasn't const propagated and we just
638 // triggered the assert on the value of the rhs.
639 self.eval_operand(op
, source_info
)
640 .and_then(|op
| self.ecx
.read_immediate(&op
).ok())
641 .map_or(DbgVal
::Underscore
, |op
| DbgVal
::Val(op
.to_const_int()))
643 let msg
= match msg
{
644 AssertKind
::DivisionByZero(op
) => {
645 Some(AssertKind
::DivisionByZero(eval_to_int(op
)))
647 AssertKind
::RemainderByZero(op
) => {
648 Some(AssertKind
::RemainderByZero(eval_to_int(op
)))
650 AssertKind
::Overflow(bin_op @
(BinOp
::Div
| BinOp
::Rem
), op1
, op2
) => {
651 // Division overflow is *UB* in the MIR, and different than the
652 // other overflow checks.
653 Some(AssertKind
::Overflow(
659 AssertKind
::BoundsCheck { ref len, ref index }
=> {
660 let len
= eval_to_int(len
);
661 let index
= eval_to_int(index
);
662 Some(AssertKind
::BoundsCheck { len, index }
)
664 // Remaining overflow errors are already covered by checks on the binary operators.
665 AssertKind
::Overflow(..) | AssertKind
::OverflowNeg(_
) => None
,
666 // Need proper const propagator for these.
669 // Poison all places this operand references so that further code
670 // doesn't use the invalid value
672 Operand
::Move(ref place
) | Operand
::Copy(ref place
) => {
673 Self::remove_const(&mut self.ecx
, place
.local
);
675 Operand
::Constant(_
) => {}
677 if let Some(msg
) = msg
{
678 self.report_assert_as_lint(
679 lint
::builtin
::UNCONDITIONAL_PANIC
,
681 "this operation will panic at runtime",
688 // None of these have Operands to const-propagate.
689 TerminatorKind
::Goto { .. }
690 | TerminatorKind
::Resume
691 | TerminatorKind
::Abort
692 | TerminatorKind
::Return
693 | TerminatorKind
::Unreachable
694 | TerminatorKind
::Drop { .. }
695 | TerminatorKind
::DropAndReplace { .. }
696 | TerminatorKind
::Yield { .. }
697 | TerminatorKind
::GeneratorDrop
698 | TerminatorKind
::FalseEdge { .. }
699 | TerminatorKind
::FalseUnwind { .. }
700 | TerminatorKind
::SwitchInt { .. }
701 | TerminatorKind
::Call { .. }
702 | TerminatorKind
::InlineAsm { .. }
=> {}
705 // We remove all Locals which are restricted in propagation to their containing blocks and
706 // which were modified in the current block.
707 // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`.
708 let mut locals
= std
::mem
::take(&mut self.ecx
.machine
.written_only_inside_own_block_locals
);
709 for &local
in locals
.iter() {
710 Self::remove_const(&mut self.ecx
, local
);
713 // Put it back so we reuse the heap of the storage
714 self.ecx
.machine
.written_only_inside_own_block_locals
= locals
;
715 if cfg
!(debug_assertions
) {
716 // Ensure we are correctly erasing locals with the non-debug-assert logic.
717 for local
in self.ecx
.machine
.only_propagate_inside_block_locals
.iter() {
719 self.get_const(local
.into()).is_none()
721 .layout_of(self.local_decls
[local
].ty
)
722 .map_or(true, |layout
| layout
.is_zst())