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