]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/const_prop.rs
Use `Body` everywhere
[rust.git] / src / librustc_mir / transform / const_prop.rs
1 //! Propagates constants for early reporting of statically known
2 //! assertion failures
3
4 use std::borrow::Cow;
5 use std::cell::Cell;
6
7 use rustc_ast::ast::Mutability;
8 use rustc_data_structures::fx::FxHashMap;
9 use rustc_hir::def::DefKind;
10 use rustc_hir::HirId;
11 use rustc_index::vec::IndexVec;
12 use rustc_middle::mir::interpret::{InterpResult, Scalar};
13 use rustc_middle::mir::visit::{
14     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
15 };
16 use rustc_middle::mir::{
17     AggregateKind, AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, Local,
18     LocalDecl, LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope,
19     SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
20 };
21 use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
22 use rustc_middle::ty::subst::{InternalSubsts, Subst};
23 use rustc_middle::ty::{self, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
24 use rustc_session::lint;
25 use rustc_span::{def_id::DefId, Span};
26 use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout};
27 use rustc_trait_selection::traits;
28
29 use crate::const_eval::error_to_const_error;
30 use crate::interpret::{
31     self, intern_const_alloc_recursive, AllocId, Allocation, Frame, ImmTy, Immediate, InternKind,
32     InterpCx, LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy,
33     Pointer, ScalarMaybeUndef, StackPopCleanup,
34 };
35 use crate::transform::{MirPass, MirSource};
36
37 /// The maximum number of bytes that we'll allocate space for a return value.
38 const MAX_ALLOC_LIMIT: u64 = 1024;
39
40 /// Macro for machine-specific `InterpError` without allocation.
41 /// (These will never be shown to the user, but they help diagnose ICEs.)
42 macro_rules! throw_machine_stop_str {
43     ($($tt:tt)*) => {{
44         // We make a new local type for it. The type itself does not carry any information,
45         // but its vtable (for the `MachineStopType` trait) does.
46         struct Zst;
47         // Debug-printing this type shows the desired string.
48         impl std::fmt::Debug for Zst {
49             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50                 write!(f, $($tt)*)
51             }
52         }
53         impl rustc_middle::mir::interpret::MachineStopType for Zst {}
54         throw_machine_stop!(Zst)
55     }};
56 }
57
58 pub struct ConstProp;
59
60 impl<'tcx> MirPass<'tcx> for ConstProp {
61     fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
62         // will be evaluated by miri and produce its errors there
63         if source.promoted.is_some() {
64             return;
65         }
66
67         use rustc_middle::hir::map::blocks::FnLikeNode;
68         let hir_id = tcx
69             .hir()
70             .as_local_hir_id(source.def_id())
71             .expect("Non-local call to local provider is_const_fn");
72
73         let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some();
74         let is_assoc_const = match tcx.def_kind(source.def_id()) {
75             Some(DefKind::AssocConst) => true,
76             _ => false,
77         };
78
79         // Only run const prop on functions, methods, closures and associated constants
80         if !is_fn_like && !is_assoc_const {
81             // skip anon_const/statics/consts because they'll be evaluated by miri anyway
82             trace!("ConstProp skipped for {:?}", source.def_id());
83             return;
84         }
85
86         let is_generator = tcx.type_of(source.def_id()).is_generator();
87         // FIXME(welseywiser) const prop doesn't work on generators because of query cycles
88         // computing their layout.
89         if is_generator {
90             trace!("ConstProp skipped for generator {:?}", source.def_id());
91             return;
92         }
93
94         // Check if it's even possible to satisfy the 'where' clauses
95         // for this item.
96         // This branch will never be taken for any normal function.
97         // However, it's possible to `#!feature(trivial_bounds)]` to write
98         // a function with impossible to satisfy clauses, e.g.:
99         // `fn foo() where String: Copy {}`
100         //
101         // We don't usually need to worry about this kind of case,
102         // since we would get a compilation error if the user tried
103         // to call it. However, since we can do const propagation
104         // even without any calls to the function, we need to make
105         // sure that it even makes sense to try to evaluate the body.
106         // If there are unsatisfiable where clauses, then all bets are
107         // off, and we just give up.
108         //
109         // We manually filter the predicates, skipping anything that's not
110         // "global". We are in a potentially generic context
111         // (e.g. we are evaluating a function without substituting generic
112         // parameters, so this filtering serves two purposes:
113         //
114         // 1. We skip evaluating any predicates that we would
115         // never be able prove are unsatisfiable (e.g. `<T as Foo>`
116         // 2. We avoid trying to normalize predicates involving generic
117         // parameters (e.g. `<T as Foo>::MyItem`). This can confuse
118         // the normalization code (leading to cycle errors), since
119         // it's usually never invoked in this way.
120         let predicates = tcx
121             .predicates_of(source.def_id())
122             .predicates
123             .iter()
124             .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None })
125             .collect();
126         if !traits::normalize_and_test_predicates(
127             tcx,
128             traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(),
129         ) {
130             trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", source.def_id());
131             return;
132         }
133
134         trace!("ConstProp starting for {:?}", source.def_id());
135
136         let dummy_body = &Body::new(
137             body.basic_blocks().clone(),
138             body.source_scopes.clone(),
139             body.local_decls.clone(),
140             Default::default(),
141             body.arg_count,
142             Default::default(),
143             tcx.def_span(source.def_id()),
144             Default::default(),
145             body.generator_kind,
146         );
147
148         // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
149         // constants, instead of just checking for const-folding succeeding.
150         // That would require an uniform one-def no-mutation analysis
151         // and RPO (or recursing when needing the value of a local).
152         let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx, source);
153         optimization_finder.visit_body(body);
154
155         trace!("ConstProp done for {:?}", source.def_id());
156     }
157 }
158
159 struct ConstPropMachine<'mir, 'tcx> {
160     /// The virtual call stack.
161     stack: Vec<Frame<'mir, 'tcx, (), ()>>,
162 }
163
164 impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> {
165     fn new() -> Self {
166         Self { stack: Vec::new() }
167     }
168 }
169
170 impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
171     type MemoryKind = !;
172     type PointerTag = ();
173     type ExtraFnVal = !;
174
175     type FrameExtra = ();
176     type MemoryExtra = ();
177     type AllocExtra = ();
178
179     type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
180
181     const GLOBAL_KIND: Option<!> = None; // no copying of globals from `tcx` to machine memory
182
183     #[inline(always)]
184     fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool {
185         false
186     }
187
188     #[inline(always)]
189     fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
190         false
191     }
192
193     fn find_mir_or_eval_fn(
194         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
195         _instance: ty::Instance<'tcx>,
196         _args: &[OpTy<'tcx>],
197         _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
198         _unwind: Option<BasicBlock>,
199     ) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
200         Ok(None)
201     }
202
203     fn call_extra_fn(
204         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
205         fn_val: !,
206         _args: &[OpTy<'tcx>],
207         _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
208         _unwind: Option<BasicBlock>,
209     ) -> InterpResult<'tcx> {
210         match fn_val {}
211     }
212
213     fn call_intrinsic(
214         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
215         _instance: ty::Instance<'tcx>,
216         _args: &[OpTy<'tcx>],
217         _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
218         _unwind: Option<BasicBlock>,
219     ) -> InterpResult<'tcx> {
220         throw_machine_stop_str!("calling intrinsics isn't supported in ConstProp")
221     }
222
223     fn assert_panic(
224         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
225         _msg: &rustc_middle::mir::AssertMessage<'tcx>,
226         _unwind: Option<rustc_middle::mir::BasicBlock>,
227     ) -> InterpResult<'tcx> {
228         bug!("panics terminators are not evaluated in ConstProp")
229     }
230
231     fn ptr_to_int(_mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx, u64> {
232         throw_unsup!(ReadPointerAsBytes)
233     }
234
235     fn binary_ptr_op(
236         _ecx: &InterpCx<'mir, 'tcx, Self>,
237         _bin_op: BinOp,
238         _left: ImmTy<'tcx>,
239         _right: ImmTy<'tcx>,
240     ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
241         // We can't do this because aliasing of memory can differ between const eval and llvm
242         throw_machine_stop_str!("pointer arithmetic or comparisons aren't supported in ConstProp")
243     }
244
245     #[inline(always)]
246     fn init_allocation_extra<'b>(
247         _memory_extra: &(),
248         _id: AllocId,
249         alloc: Cow<'b, Allocation>,
250         _kind: Option<MemoryKind<!>>,
251     ) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
252         // We do not use a tag so we can just cheaply forward the allocation
253         (alloc, ())
254     }
255
256     #[inline(always)]
257     fn tag_global_base_pointer(_memory_extra: &(), _id: AllocId) -> Self::PointerTag {}
258
259     fn box_alloc(
260         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
261         _dest: PlaceTy<'tcx>,
262     ) -> InterpResult<'tcx> {
263         throw_machine_stop_str!("can't const prop heap allocations")
264     }
265
266     fn access_local(
267         _ecx: &InterpCx<'mir, 'tcx, Self>,
268         frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
269         local: Local,
270     ) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> {
271         let l = &frame.locals[local];
272
273         if l.value == LocalValue::Uninitialized {
274             throw_machine_stop_str!("tried to access an uninitialized local")
275         }
276
277         l.access()
278     }
279
280     fn before_access_global(
281         _memory_extra: &(),
282         _alloc_id: AllocId,
283         allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
284         _static_def_id: Option<DefId>,
285         is_write: bool,
286     ) -> InterpResult<'tcx> {
287         if is_write {
288             throw_machine_stop_str!("can't write to global");
289         }
290         // If the static allocation is mutable, then we can't const prop it as its content
291         // might be different at runtime.
292         if allocation.mutability == Mutability::Mut {
293             throw_machine_stop_str!("can't access mutable globals in ConstProp");
294         }
295
296         Ok(())
297     }
298
299     #[inline(always)]
300     fn init_frame_extra(
301         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
302         frame: Frame<'mir, 'tcx>,
303     ) -> InterpResult<'tcx, Frame<'mir, 'tcx>> {
304         Ok(frame)
305     }
306
307     #[inline(always)]
308     fn stack(
309         ecx: &'a InterpCx<'mir, 'tcx, Self>,
310     ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
311         &ecx.machine.stack
312     }
313
314     #[inline(always)]
315     fn stack_mut(
316         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
317     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
318         &mut ecx.machine.stack
319     }
320 }
321
322 /// Finds optimization opportunities on the MIR.
323 struct ConstPropagator<'mir, 'tcx> {
324     ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
325     tcx: TyCtxt<'tcx>,
326     can_const_prop: IndexVec<Local, ConstPropMode>,
327     param_env: ParamEnv<'tcx>,
328     // FIXME(eddyb) avoid cloning these two fields more than once,
329     // by accessing them through `ecx` instead.
330     source_scopes: IndexVec<SourceScope, SourceScopeData>,
331     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
332     ret: Option<OpTy<'tcx, ()>>,
333     // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
334     // the last known `SourceInfo` here and just keep revisiting it.
335     source_info: Option<SourceInfo>,
336 }
337
338 impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
339     type Ty = Ty<'tcx>;
340     type TyAndLayout = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
341
342     fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout {
343         self.tcx.layout_of(self.param_env.and(ty))
344     }
345 }
346
347 impl<'mir, 'tcx> HasDataLayout for ConstPropagator<'mir, 'tcx> {
348     #[inline]
349     fn data_layout(&self) -> &TargetDataLayout {
350         &self.tcx.data_layout
351     }
352 }
353
354 impl<'mir, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
355     #[inline]
356     fn tcx(&self) -> TyCtxt<'tcx> {
357         self.tcx
358     }
359 }
360
361 impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
362     fn new(
363         body: &Body<'tcx>,
364         dummy_body: &'mir Body<'tcx>,
365         tcx: TyCtxt<'tcx>,
366         source: MirSource<'tcx>,
367     ) -> ConstPropagator<'mir, 'tcx> {
368         let def_id = source.def_id();
369         let substs = &InternalSubsts::identity_for_item(tcx, def_id);
370         let param_env = tcx.param_env(def_id).with_reveal_all();
371
372         let span = tcx.def_span(def_id);
373         let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine::new(), ());
374         let can_const_prop = CanConstProp::check(body);
375
376         let ret = ecx
377             .layout_of(body.return_ty().subst(tcx, substs))
378             .ok()
379             // Don't bother allocating memory for ZST types which have no values
380             // or for large values.
381             .filter(|ret_layout| {
382                 !ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
383             })
384             .map(|ret_layout| ecx.allocate(ret_layout, MemoryKind::Stack));
385
386         ecx.push_stack_frame(
387             Instance::new(def_id, substs),
388             dummy_body,
389             ret.map(Into::into),
390             StackPopCleanup::None { cleanup: false },
391         )
392         .expect("failed to push initial stack frame");
393
394         ConstPropagator {
395             ecx,
396             tcx,
397             param_env,
398             can_const_prop,
399             // FIXME(eddyb) avoid cloning these two fields more than once,
400             // by accessing them through `ecx` instead.
401             source_scopes: body.source_scopes.clone(),
402             //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
403             local_decls: body.local_decls.clone(),
404             ret: ret.map(Into::into),
405             source_info: None,
406         }
407     }
408
409     fn get_const(&self, local: Local) -> Option<OpTy<'tcx>> {
410         if local == RETURN_PLACE {
411             // Try to read the return place as an immediate so that if it is representable as a
412             // scalar, we can handle it as such, but otherwise, just return the value as is.
413             return match self.ret.map(|ret| self.ecx.try_read_immediate(ret)) {
414                 Some(Ok(Ok(imm))) => Some(imm.into()),
415                 _ => self.ret,
416             };
417         }
418
419         self.ecx.access_local(self.ecx.frame(), local, None).ok()
420     }
421
422     fn remove_const(&mut self, local: Local) {
423         self.ecx.frame_mut().locals[local] =
424             LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
425     }
426
427     fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
428         match &self.source_scopes[source_info.scope].local_data {
429             ClearCrossCrate::Set(data) => Some(data.lint_root),
430             ClearCrossCrate::Clear => None,
431         }
432     }
433
434     fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
435     where
436         F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
437     {
438         match f(self) {
439             Ok(val) => Some(val),
440             Err(error) => {
441                 // Some errors shouldn't come up because creating them causes
442                 // an allocation, which we should avoid. When that happens,
443                 // dedicated error variants should be introduced instead.
444                 assert!(
445                     !error.kind.allocates(),
446                     "const-prop encountered allocating error: {}",
447                     error
448                 );
449                 None
450             }
451         }
452     }
453
454     fn eval_constant(&mut self, c: &Constant<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
455         // FIXME we need to revisit this for #67176
456         if c.needs_subst() {
457             return None;
458         }
459
460         match self.ecx.eval_const_to_op(c.literal, None) {
461             Ok(op) => Some(op),
462             Err(error) => {
463                 // Make sure errors point at the constant.
464                 self.ecx.set_span(c.span);
465                 let err = error_to_const_error(&self.ecx, error);
466                 if let Some(lint_root) = self.lint_root(source_info) {
467                     let lint_only = match c.literal.val {
468                         // Promoteds must lint and not error as the user didn't ask for them
469                         ConstKind::Unevaluated(_, _, Some(_)) => true,
470                         // Out of backwards compatibility we cannot report hard errors in unused
471                         // generic functions using associated constants of the generic parameters.
472                         _ => c.literal.needs_subst(),
473                     };
474                     if lint_only {
475                         // Out of backwards compatibility we cannot report hard errors in unused
476                         // generic functions using associated constants of the generic parameters.
477                         err.report_as_lint(
478                             self.ecx.tcx,
479                             "erroneous constant used",
480                             lint_root,
481                             Some(c.span),
482                         );
483                     } else {
484                         err.report_as_error(self.ecx.tcx, "erroneous constant used");
485                     }
486                 } else {
487                     err.report_as_error(self.ecx.tcx, "erroneous constant used");
488                 }
489                 None
490             }
491         }
492     }
493
494     fn eval_place(&mut self, place: Place<'tcx>) -> Option<OpTy<'tcx>> {
495         trace!("eval_place(place={:?})", place);
496         self.use_ecx(|this| this.ecx.eval_place_to_op(place, None))
497     }
498
499     fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<OpTy<'tcx>> {
500         match *op {
501             Operand::Constant(ref c) => self.eval_constant(c, source_info),
502             Operand::Move(place) | Operand::Copy(place) => self.eval_place(place),
503         }
504     }
505
506     fn report_assert_as_lint(
507         &self,
508         lint: &'static lint::Lint,
509         source_info: SourceInfo,
510         message: &'static str,
511         panic: AssertKind<u64>,
512     ) -> Option<()> {
513         let lint_root = self.lint_root(source_info)?;
514         self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| {
515             let mut err = lint.build(message);
516             err.span_label(source_info.span, format!("{:?}", panic));
517             err.emit()
518         });
519         None
520     }
521
522     fn check_unary_op(
523         &mut self,
524         op: UnOp,
525         arg: &Operand<'tcx>,
526         source_info: SourceInfo,
527     ) -> Option<()> {
528         if self.use_ecx(|this| {
529             let val = this.ecx.read_immediate(this.ecx.eval_operand(arg, None)?)?;
530             let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, val)?;
531             Ok(overflow)
532         })? {
533             // `AssertKind` only has an `OverflowNeg` variant, so make sure that is
534             // appropriate to use.
535             assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow");
536             self.report_assert_as_lint(
537                 lint::builtin::ARITHMETIC_OVERFLOW,
538                 source_info,
539                 "this arithmetic operation will overflow",
540                 AssertKind::OverflowNeg,
541             )?;
542         }
543
544         Some(())
545     }
546
547     fn check_binary_op(
548         &mut self,
549         op: BinOp,
550         left: &Operand<'tcx>,
551         right: &Operand<'tcx>,
552         source_info: SourceInfo,
553     ) -> Option<()> {
554         let r =
555             self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?))?;
556         // Check for exceeding shifts *even if* we cannot evaluate the LHS.
557         if op == BinOp::Shr || op == BinOp::Shl {
558             // We need the type of the LHS. We cannot use `place_layout` as that is the type
559             // of the result, which for checked binops is not the same!
560             let left_ty = left.ty(&self.local_decls, self.tcx);
561             let left_size_bits = self.ecx.layout_of(left_ty).ok()?.size.bits();
562             let right_size = r.layout.size;
563             let r_bits = r.to_scalar().ok();
564             // This is basically `force_bits`.
565             let r_bits = r_bits.and_then(|r| r.to_bits_or_ptr(right_size, &self.tcx).ok());
566             if r_bits.map_or(false, |b| b >= left_size_bits as u128) {
567                 self.report_assert_as_lint(
568                     lint::builtin::ARITHMETIC_OVERFLOW,
569                     source_info,
570                     "this arithmetic operation will overflow",
571                     AssertKind::Overflow(op),
572                 )?;
573             }
574         }
575
576         // The remaining operators are handled through `overflowing_binary_op`.
577         if self.use_ecx(|this| {
578             let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
579             let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?;
580             Ok(overflow)
581         })? {
582             self.report_assert_as_lint(
583                 lint::builtin::ARITHMETIC_OVERFLOW,
584                 source_info,
585                 "this arithmetic operation will overflow",
586                 AssertKind::Overflow(op),
587             )?;
588         }
589
590         Some(())
591     }
592
593     fn const_prop(
594         &mut self,
595         rvalue: &Rvalue<'tcx>,
596         place_layout: TyAndLayout<'tcx>,
597         source_info: SourceInfo,
598         place: Place<'tcx>,
599     ) -> Option<()> {
600         // #66397: Don't try to eval into large places as that can cause an OOM
601         if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
602             return None;
603         }
604
605         // Perform any special handling for specific Rvalue types.
606         // Generally, checks here fall into one of two categories:
607         //   1. Additional checking to provide useful lints to the user
608         //        - In this case, we will do some validation and then fall through to the
609         //          end of the function which evals the assignment.
610         //   2. Working around bugs in other parts of the compiler
611         //        - In this case, we'll return `None` from this function to stop evaluation.
612         match rvalue {
613             // Additional checking: give lints to the user if an overflow would occur.
614             // We do this here and not in the `Assert` terminator as that terminator is
615             // only sometimes emitted (overflow checks can be disabled), but we want to always
616             // lint.
617             Rvalue::UnaryOp(op, arg) => {
618                 trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg);
619                 self.check_unary_op(*op, arg, source_info)?;
620             }
621             Rvalue::BinaryOp(op, left, right) => {
622                 trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
623                 self.check_binary_op(*op, left, right, source_info)?;
624             }
625             Rvalue::CheckedBinaryOp(op, left, right) => {
626                 trace!(
627                     "checking CheckedBinaryOp(op = {:?}, left = {:?}, right = {:?})",
628                     op,
629                     left,
630                     right
631                 );
632                 self.check_binary_op(*op, left, right, source_info)?;
633             }
634
635             // Do not try creating references (#67862)
636             Rvalue::Ref(_, _, place_ref) => {
637                 trace!("skipping Ref({:?})", place_ref);
638
639                 return None;
640             }
641
642             _ => {}
643         }
644
645         // FIXME we need to revisit this for #67176
646         if rvalue.needs_subst() {
647             return None;
648         }
649
650         self.use_ecx(|this| {
651             trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
652             this.ecx.eval_rvalue_into_place(rvalue, place)?;
653             Ok(())
654         })
655     }
656
657     fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
658         Operand::Constant(Box::new(Constant {
659             span,
660             user_ty: None,
661             literal: self.tcx.mk_const(*ty::Const::from_scalar(self.tcx, scalar, ty)),
662         }))
663     }
664
665     fn replace_with_const(
666         &mut self,
667         rval: &mut Rvalue<'tcx>,
668         value: OpTy<'tcx>,
669         source_info: SourceInfo,
670     ) {
671         trace!("attepting to replace {:?} with {:?}", rval, value);
672         if let Err(e) = self.ecx.const_validate_operand(
673             value,
674             vec![],
675             // FIXME: is ref tracking too expensive?
676             &mut interpret::RefTracking::empty(),
677             /*may_ref_to_static*/ true,
678         ) {
679             trace!("validation error, attempt failed: {:?}", e);
680             return;
681         }
682
683         // FIXME> figure out what to do when try_read_immediate fails
684         let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value));
685
686         if let Some(Ok(imm)) = imm {
687             match *imm {
688                 interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => {
689                     *rval = Rvalue::Use(self.operand_from_scalar(
690                         scalar,
691                         value.layout.ty,
692                         source_info.span,
693                     ));
694                 }
695                 Immediate::ScalarPair(
696                     ScalarMaybeUndef::Scalar(one),
697                     ScalarMaybeUndef::Scalar(two),
698                 ) => {
699                     // Found a value represented as a pair. For now only do cont-prop if type of
700                     // Rvalue is also a pair with two scalars. The more general case is more
701                     // complicated to implement so we'll do it later.
702                     let ty = &value.layout.ty.kind;
703                     // Only do it for tuples
704                     if let ty::Tuple(substs) = ty {
705                         // Only do it if tuple is also a pair with two scalars
706                         if substs.len() == 2 {
707                             let opt_ty1_ty2 = self.use_ecx(|this| {
708                                 let ty1 = substs[0].expect_ty();
709                                 let ty2 = substs[1].expect_ty();
710                                 let ty_is_scalar = |ty| {
711                                     this.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar())
712                                         == Some(true)
713                                 };
714                                 if ty_is_scalar(ty1) && ty_is_scalar(ty2) {
715                                     Ok(Some((ty1, ty2)))
716                                 } else {
717                                     Ok(None)
718                                 }
719                             });
720
721                             if let Some(Some((ty1, ty2))) = opt_ty1_ty2 {
722                                 *rval = Rvalue::Aggregate(
723                                     Box::new(AggregateKind::Tuple),
724                                     vec![
725                                         self.operand_from_scalar(one, ty1, source_info.span),
726                                         self.operand_from_scalar(two, ty2, source_info.span),
727                                     ],
728                                 );
729                             }
730                         }
731                     }
732                 }
733                 _ => {}
734             }
735         }
736     }
737
738     fn should_const_prop(&mut self, op: OpTy<'tcx>) -> bool {
739         let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level;
740
741         if mir_opt_level == 0 {
742             return false;
743         }
744
745         match *op {
746             interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Scalar(s))) => {
747                 s.is_bits()
748             }
749             interpret::Operand::Immediate(Immediate::ScalarPair(
750                 ScalarMaybeUndef::Scalar(l),
751                 ScalarMaybeUndef::Scalar(r),
752             )) => l.is_bits() && r.is_bits(),
753             interpret::Operand::Indirect(_) if mir_opt_level >= 2 => {
754                 let mplace = op.assert_mem_place(&self.ecx);
755                 intern_const_alloc_recursive(&mut self.ecx, InternKind::ConstProp, mplace, false)
756                     .expect("failed to intern alloc");
757                 true
758             }
759             _ => false,
760         }
761     }
762 }
763
764 /// The mode that `ConstProp` is allowed to run in for a given `Local`.
765 #[derive(Clone, Copy, Debug, PartialEq)]
766 enum ConstPropMode {
767     /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
768     FullConstProp,
769     /// The `Local` can be propagated into but reads cannot be propagated.
770     OnlyPropagateInto,
771     /// No propagation is allowed at all.
772     NoPropagation,
773 }
774
775 struct CanConstProp {
776     can_const_prop: IndexVec<Local, ConstPropMode>,
777     // false at the beginning, once set, there are not allowed to be any more assignments
778     found_assignment: IndexVec<Local, bool>,
779 }
780
781 impl CanConstProp {
782     /// returns true if `local` can be propagated
783     fn check(body: &Body<'_>) -> IndexVec<Local, ConstPropMode> {
784         let mut cpv = CanConstProp {
785             can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
786             found_assignment: IndexVec::from_elem(false, &body.local_decls),
787         };
788         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
789             // cannot use args at all
790             // cannot use locals because if x < y { y - x } else { x - y } would
791             //        lint for x != y
792             // FIXME(oli-obk): lint variables until they are used in a condition
793             // FIXME(oli-obk): lint if return value is constant
794             let local_kind = body.local_kind(local);
795
796             if local_kind == LocalKind::Arg || local_kind == LocalKind::Var {
797                 *val = ConstPropMode::OnlyPropagateInto;
798                 trace!("local {:?} can't be const propagated because it's not a temporary", local);
799             }
800         }
801         cpv.visit_body(&body);
802         cpv.can_const_prop
803     }
804 }
805
806 impl<'tcx> Visitor<'tcx> for CanConstProp {
807     fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
808         use rustc_middle::mir::visit::PlaceContext::*;
809         match context {
810             // Constants must have at most one write
811             // FIXME(oli-obk): we could be more powerful here, if the multiple writes
812             // only occur in independent execution paths
813             MutatingUse(MutatingUseContext::Store) => {
814                 if self.found_assignment[local] {
815                     trace!("local {:?} can't be propagated because of multiple assignments", local);
816                     self.can_const_prop[local] = ConstPropMode::NoPropagation;
817                 } else {
818                     self.found_assignment[local] = true
819                 }
820             }
821             // Reading constants is allowed an arbitrary number of times
822             NonMutatingUse(NonMutatingUseContext::Copy)
823             | NonMutatingUse(NonMutatingUseContext::Move)
824             | NonMutatingUse(NonMutatingUseContext::Inspect)
825             | NonMutatingUse(NonMutatingUseContext::Projection)
826             | MutatingUse(MutatingUseContext::Projection)
827             | NonUse(_) => {}
828             _ => {
829                 trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
830                 self.can_const_prop[local] = ConstPropMode::NoPropagation;
831             }
832         }
833     }
834 }
835
836 impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
837     fn tcx(&self) -> TyCtxt<'tcx> {
838         self.tcx
839     }
840
841     fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
842         trace!("visit_constant: {:?}", constant);
843         self.super_constant(constant, location);
844         self.eval_constant(constant, self.source_info.unwrap());
845     }
846
847     fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
848         trace!("visit_statement: {:?}", statement);
849         let source_info = statement.source_info;
850         self.ecx.set_span(source_info.span);
851         self.source_info = Some(source_info);
852         if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind {
853             let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
854             if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
855                 if let Some(local) = place.as_local() {
856                     let can_const_prop = self.can_const_prop[local];
857                     if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
858                         if can_const_prop == ConstPropMode::FullConstProp
859                             || can_const_prop == ConstPropMode::OnlyPropagateInto
860                         {
861                             if let Some(value) = self.get_const(local) {
862                                 if self.should_const_prop(value) {
863                                     trace!("replacing {:?} with {:?}", rval, value);
864                                     self.replace_with_const(rval, value, statement.source_info);
865
866                                     if can_const_prop == ConstPropMode::FullConstProp {
867                                         trace!("propagated into {:?}", local);
868                                     }
869                                 }
870                             }
871                         }
872                     }
873                     if self.can_const_prop[local] != ConstPropMode::FullConstProp {
874                         trace!("can't propagate into {:?}", local);
875                         if local != RETURN_PLACE {
876                             self.remove_const(local);
877                         }
878                     }
879                 }
880             }
881         } else {
882             match statement.kind {
883                 StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
884                     let frame = self.ecx.frame_mut();
885                     frame.locals[local].value =
886                         if let StatementKind::StorageLive(_) = statement.kind {
887                             LocalValue::Uninitialized
888                         } else {
889                             LocalValue::Dead
890                         };
891                 }
892                 _ => {}
893             }
894         }
895
896         self.super_statement(statement, location);
897     }
898
899     fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
900         let source_info = terminator.source_info;
901         self.ecx.set_span(source_info.span);
902         self.source_info = Some(source_info);
903         self.super_terminator(terminator, location);
904         match &mut terminator.kind {
905             TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
906                 if let Some(value) = self.eval_operand(&cond, source_info) {
907                     trace!("assertion on {:?} should be {:?}", value, expected);
908                     let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
909                     let value_const = self.ecx.read_scalar(value).unwrap();
910                     if expected != value_const {
911                         // poison all places this operand references so that further code
912                         // doesn't use the invalid value
913                         match cond {
914                             Operand::Move(ref place) | Operand::Copy(ref place) => {
915                                 self.remove_const(place.local);
916                             }
917                             Operand::Constant(_) => {}
918                         }
919                         let msg = match msg {
920                             AssertKind::DivisionByZero => AssertKind::DivisionByZero,
921                             AssertKind::RemainderByZero => AssertKind::RemainderByZero,
922                             AssertKind::BoundsCheck { ref len, ref index } => {
923                                 let len =
924                                     self.eval_operand(len, source_info).expect("len must be const");
925                                 let len = self
926                                     .ecx
927                                     .read_scalar(len)
928                                     .unwrap()
929                                     .to_machine_usize(&self.tcx)
930                                     .unwrap();
931                                 let index = self
932                                     .eval_operand(index, source_info)
933                                     .expect("index must be const");
934                                 let index = self
935                                     .ecx
936                                     .read_scalar(index)
937                                     .unwrap()
938                                     .to_machine_usize(&self.tcx)
939                                     .unwrap();
940                                 AssertKind::BoundsCheck { len, index }
941                             }
942                             // Overflow is are already covered by checks on the binary operators.
943                             AssertKind::Overflow(_) | AssertKind::OverflowNeg => return,
944                             // Need proper const propagator for these.
945                             _ => return,
946                         };
947                         self.report_assert_as_lint(
948                             lint::builtin::UNCONDITIONAL_PANIC,
949                             source_info,
950                             "this operation will panic at runtime",
951                             msg,
952                         );
953                     } else {
954                         if self.should_const_prop(value) {
955                             if let ScalarMaybeUndef::Scalar(scalar) = value_const {
956                                 *cond = self.operand_from_scalar(
957                                     scalar,
958                                     self.tcx.types.bool,
959                                     source_info.span,
960                                 );
961                             }
962                         }
963                     }
964                 }
965             }
966             TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => {
967                 if let Some(value) = self.eval_operand(&discr, source_info) {
968                     if self.should_const_prop(value) {
969                         if let ScalarMaybeUndef::Scalar(scalar) =
970                             self.ecx.read_scalar(value).unwrap()
971                         {
972                             *discr = self.operand_from_scalar(scalar, switch_ty, source_info.span);
973                         }
974                     }
975                 }
976             }
977             //none of these have Operands to const-propagate
978             TerminatorKind::Goto { .. }
979             | TerminatorKind::Resume
980             | TerminatorKind::Abort
981             | TerminatorKind::Return
982             | TerminatorKind::Unreachable
983             | TerminatorKind::Drop { .. }
984             | TerminatorKind::DropAndReplace { .. }
985             | TerminatorKind::Yield { .. }
986             | TerminatorKind::GeneratorDrop
987             | TerminatorKind::FalseEdges { .. }
988             | TerminatorKind::FalseUnwind { .. } => {}
989             //FIXME(wesleywiser) Call does have Operands that could be const-propagated
990             TerminatorKind::Call { .. } => {}
991         }
992     }
993 }