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