]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/const_prop.rs
Remove PlaceBase enum and make Place base field be local: Local
[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::mir::interpret::{InterpResult, PanicInfo, Scalar};
8 use rustc::mir::visit::{
9     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
10 };
11 use rustc::mir::{
12     read_only, AggregateKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate, Constant,
13     Local, LocalDecl, LocalKind, Location, Operand, Place, ReadOnlyBodyAndCache, Rvalue,
14     SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
15     UnOp, RETURN_PLACE,
16 };
17 use rustc::ty::layout::{
18     HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout,
19 };
20 use rustc::ty::subst::{InternalSubsts, Subst};
21 use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable};
22 use rustc_data_structures::fx::FxHashMap;
23 use rustc_hir::def::DefKind;
24 use rustc_hir::def_id::DefId;
25 use rustc_hir::HirId;
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, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx,
33     LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer,
34     ScalarMaybeUndef, StackPopCleanup,
35 };
36 use crate::transform::{MirPass, MirSource};
37
38 /// The maximum number of bytes that we'll allocate space for a return value.
39 const MAX_ALLOC_LIMIT: u64 = 1024;
40
41 pub struct ConstProp;
42
43 impl<'tcx> MirPass<'tcx> for ConstProp {
44     fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
45         // will be evaluated by miri and produce its errors there
46         if source.promoted.is_some() {
47             return;
48         }
49
50         use rustc::hir::map::blocks::FnLikeNode;
51         let hir_id = tcx
52             .hir()
53             .as_local_hir_id(source.def_id())
54             .expect("Non-local call to local provider is_const_fn");
55
56         let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some();
57         let is_assoc_const = match tcx.def_kind(source.def_id()) {
58             Some(DefKind::AssocConst) => true,
59             _ => false,
60         };
61
62         // Only run const prop on functions, methods, closures and associated constants
63         if !is_fn_like && !is_assoc_const {
64             // skip anon_const/statics/consts because they'll be evaluated by miri anyway
65             trace!("ConstProp skipped for {:?}", source.def_id());
66             return;
67         }
68
69         let is_generator = tcx.type_of(source.def_id()).is_generator();
70         // FIXME(welseywiser) const prop doesn't work on generators because of query cycles
71         // computing their layout.
72         if is_generator {
73             trace!("ConstProp skipped for generator {:?}", source.def_id());
74             return;
75         }
76
77         trace!("ConstProp starting for {:?}", source.def_id());
78
79         let dummy_body = &Body::new(
80             body.basic_blocks().clone(),
81             body.source_scopes.clone(),
82             body.local_decls.clone(),
83             Default::default(),
84             body.arg_count,
85             Default::default(),
86             tcx.def_span(source.def_id()),
87             Default::default(),
88             body.generator_kind,
89         );
90
91         // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
92         // constants, instead of just checking for const-folding succeeding.
93         // That would require an uniform one-def no-mutation analysis
94         // and RPO (or recursing when needing the value of a local).
95         let mut optimization_finder =
96             ConstPropagator::new(read_only!(body), dummy_body, tcx, source);
97         optimization_finder.visit_body(body);
98
99         trace!("ConstProp done for {:?}", source.def_id());
100     }
101 }
102
103 struct ConstPropMachine;
104
105 impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
106     type MemoryKinds = !;
107     type PointerTag = ();
108     type ExtraFnVal = !;
109
110     type FrameExtra = ();
111     type MemoryExtra = ();
112     type AllocExtra = ();
113
114     type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
115
116     const STATIC_KIND: Option<!> = None;
117
118     const CHECK_ALIGN: bool = false;
119
120     #[inline(always)]
121     fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
122         false
123     }
124
125     fn find_mir_or_eval_fn(
126         _ecx: &mut InterpCx<'mir, 'tcx, Self>,
127         _span: Span,
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     lint_root: Option<HirId>,
268 }
269
270 impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
271     type Ty = Ty<'tcx>;
272     type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
273
274     fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
275         self.tcx.layout_of(self.param_env.and(ty))
276     }
277 }
278
279 impl<'mir, 'tcx> HasDataLayout for ConstPropagator<'mir, 'tcx> {
280     #[inline]
281     fn data_layout(&self) -> &TargetDataLayout {
282         &self.tcx.data_layout
283     }
284 }
285
286 impl<'mir, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
287     #[inline]
288     fn tcx(&self) -> TyCtxt<'tcx> {
289         self.tcx
290     }
291 }
292
293 impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
294     fn new(
295         body: ReadOnlyBodyAndCache<'_, 'tcx>,
296         dummy_body: &'mir Body<'tcx>,
297         tcx: TyCtxt<'tcx>,
298         source: MirSource<'tcx>,
299     ) -> ConstPropagator<'mir, 'tcx> {
300         let def_id = source.def_id();
301         let substs = &InternalSubsts::identity_for_item(tcx, def_id);
302         let mut param_env = tcx.param_env(def_id);
303
304         // If we're evaluating inside a monomorphic function, then use `Reveal::All` because
305         // we want to see the same instances that codegen will see. This allows us to `resolve()`
306         // specializations.
307         if !substs.needs_subst() {
308             param_env = param_env.with_reveal_all();
309         }
310
311         let span = tcx.def_span(def_id);
312         let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ());
313         let can_const_prop = CanConstProp::check(body);
314
315         let ret = ecx
316             .layout_of(body.return_ty().subst(tcx, substs))
317             .ok()
318             // Don't bother allocating memory for ZST types which have no values
319             // or for large values.
320             .filter(|ret_layout| {
321                 !ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
322             })
323             .map(|ret_layout| ecx.allocate(ret_layout, MemoryKind::Stack));
324
325         ecx.push_stack_frame(
326             Instance::new(def_id, substs),
327             span,
328             dummy_body,
329             ret.map(Into::into),
330             StackPopCleanup::None { cleanup: false },
331         )
332         .expect("failed to push initial stack frame");
333
334         ConstPropagator {
335             ecx,
336             tcx,
337             source,
338             param_env,
339             can_const_prop,
340             // FIXME(eddyb) avoid cloning these two fields more than once,
341             // by accessing them through `ecx` instead.
342             source_scopes: body.source_scopes.clone(),
343             //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
344             local_decls: body.local_decls.clone(),
345             ret: ret.map(Into::into),
346             source_info: None,
347             lint_root: None,
348         }
349     }
350
351     fn get_const(&self, local: Local) -> Option<Const<'tcx>> {
352         if local == RETURN_PLACE {
353             // Try to read the return place as an immediate so that if it is representable as a
354             // scalar, we can handle it as such, but otherwise, just return the value as is.
355             return match self.ret.map(|ret| self.ecx.try_read_immediate(ret)) {
356                 Some(Ok(Ok(imm))) => Some(imm.into()),
357                 _ => self.ret,
358             };
359         }
360
361         self.ecx.access_local(self.ecx.frame(), local, None).ok()
362     }
363
364     fn remove_const(&mut self, local: Local) {
365         self.ecx.frame_mut().locals[local] =
366             LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
367     }
368
369     fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
370         match &self.source_scopes[source_info.scope].local_data {
371             ClearCrossCrate::Set(data) => Some(data.lint_root),
372             ClearCrossCrate::Clear => None,
373         }
374     }
375
376     fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T>
377     where
378         F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
379     {
380         self.ecx.tcx.span = source_info.span;
381         let r = match f(self) {
382             Ok(val) => Some(val),
383             Err(error) => {
384                 use rustc::mir::interpret::{
385                     InterpError::*, UndefinedBehaviorInfo, UnsupportedOpInfo,
386                 };
387                 match error.kind {
388                     MachineStop(_) => bug!("ConstProp does not stop"),
389
390                     // Some error shouldn't come up because creating them causes
391                     // an allocation, which we should avoid. When that happens,
392                     // dedicated error variants should be introduced instead.
393                     // Only test this in debug builds though to avoid disruptions.
394                     Unsupported(UnsupportedOpInfo::Unsupported(_))
395                     | Unsupported(UnsupportedOpInfo::ValidationFailure(_))
396                     | UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
397                     | UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_))
398                         if cfg!(debug_assertions) =>
399                     {
400                         bug!("const-prop encountered allocating error: {:?}", error.kind);
401                     }
402
403                     Unsupported(_)
404                     | UndefinedBehavior(_)
405                     | InvalidProgram(_)
406                     | ResourceExhaustion(_) => {
407                         // Ignore these errors.
408                     }
409                     Panic(_) => {
410                         let diagnostic = error_to_const_error(&self.ecx, error);
411                         diagnostic.report_as_lint(
412                             self.ecx.tcx,
413                             "this expression will panic at runtime",
414                             self.lint_root?,
415                             None,
416                         );
417                     }
418                 }
419                 None
420             }
421         };
422         self.ecx.tcx.span = DUMMY_SP;
423         r
424     }
425
426     fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<Const<'tcx>> {
427         self.ecx.tcx.span = c.span;
428
429         // FIXME we need to revisit this for #67176
430         if c.needs_subst() {
431             return None;
432         }
433
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 {
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),
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 const_prop(
543         &mut self,
544         rvalue: &Rvalue<'tcx>,
545         place_layout: TyLayout<'tcx>,
546         source_info: SourceInfo,
547         place: &Place<'tcx>,
548     ) -> Option<()> {
549         // #66397: Don't try to eval into large places as that can cause an OOM
550         if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
551             return None;
552         }
553
554         // FIXME we need to revisit this for #67176
555         if rvalue.needs_subst() {
556             return None;
557         }
558
559         let overflow_check = self.tcx.sess.overflow_checks();
560
561         // Perform any special handling for specific Rvalue types.
562         // Generally, checks here fall into one of two categories:
563         //   1. Additional checking to provide useful lints to the user
564         //        - In this case, we will do some validation and then fall through to the
565         //          end of the function which evals the assignment.
566         //   2. Working around bugs in other parts of the compiler
567         //        - In this case, we'll return `None` from this function to stop evaluation.
568         match rvalue {
569             // Additional checking: if overflow checks are disabled (which is usually the case in
570             // release mode), then we need to do additional checking here to give lints to the user
571             // if an overflow would occur.
572             Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => {
573                 trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
574                 self.check_unary_op(arg, source_info)?;
575             }
576
577             // Additional checking: check for overflows on integer binary operations and report
578             // them to the user as lints.
579             Rvalue::BinaryOp(op, left, right) => {
580                 trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
581                 self.check_binary_op(*op, left, right, source_info, place_layout, overflow_check)?;
582             }
583
584             // Work around: avoid ICE in miri. FIXME(wesleywiser)
585             // The Miri engine ICEs when taking a reference to an uninitialized unsized
586             // local. There's nothing it can do here: taking a reference needs an allocation
587             // which needs to know the size. Normally that's okay as during execution
588             // (e.g. for CTFE) it can never happen. But here in const_prop
589             // unknown data is uninitialized, so if e.g. a function argument is unsized
590             // and has a reference taken, we get an ICE.
591             Rvalue::Ref(_, _, place_ref) => {
592                 trace!("checking Ref({:?})", place_ref);
593
594                 if let Some(local) = place_ref.as_local() {
595                     let alive = if let LocalValue::Live(_) = self.ecx.frame().locals[local].value {
596                         true
597                     } else {
598                         false
599                     };
600
601                     if !alive {
602                         trace!("skipping Ref({:?}) to uninitialized local", place);
603                         return None;
604                     }
605                 }
606             }
607
608             _ => {}
609         }
610
611         self.use_ecx(source_info, |this| {
612             trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
613             this.ecx.eval_rvalue_into_place(rvalue, place)?;
614             Ok(())
615         })
616     }
617
618     fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
619         Operand::Constant(Box::new(Constant {
620             span,
621             user_ty: None,
622             literal: self.tcx.mk_const(*ty::Const::from_scalar(self.tcx, scalar, ty)),
623         }))
624     }
625
626     fn replace_with_const(
627         &mut self,
628         rval: &mut Rvalue<'tcx>,
629         value: Const<'tcx>,
630         source_info: SourceInfo,
631     ) {
632         trace!("attepting to replace {:?} with {:?}", rval, value);
633         if let Err(e) = self.ecx.validate_operand(
634             value,
635             vec![],
636             // FIXME: is ref tracking too expensive?
637             Some(&mut interpret::RefTracking::empty()),
638         ) {
639             trace!("validation error, attempt failed: {:?}", e);
640             return;
641         }
642
643         // FIXME> figure out what tho do when try_read_immediate fails
644         let imm = self.use_ecx(source_info, |this| this.ecx.try_read_immediate(value));
645
646         if let Some(Ok(imm)) = imm {
647             match *imm {
648                 interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => {
649                     *rval = Rvalue::Use(self.operand_from_scalar(
650                         scalar,
651                         value.layout.ty,
652                         source_info.span,
653                     ));
654                 }
655                 Immediate::ScalarPair(
656                     ScalarMaybeUndef::Scalar(one),
657                     ScalarMaybeUndef::Scalar(two),
658                 ) => {
659                     // Found a value represented as a pair. For now only do cont-prop if type of
660                     // Rvalue is also a pair with two scalars. The more general case is more
661                     // complicated to implement so we'll do it later.
662                     let ty = &value.layout.ty.kind;
663                     // Only do it for tuples
664                     if let ty::Tuple(substs) = ty {
665                         // Only do it if tuple is also a pair with two scalars
666                         if substs.len() == 2 {
667                             let opt_ty1_ty2 = self.use_ecx(source_info, |this| {
668                                 let ty1 = substs[0].expect_ty();
669                                 let ty2 = substs[1].expect_ty();
670                                 let ty_is_scalar = |ty| {
671                                     this.ecx.layout_of(ty).ok().map(|ty| ty.details.abi.is_scalar())
672                                         == Some(true)
673                                 };
674                                 if ty_is_scalar(ty1) && ty_is_scalar(ty2) {
675                                     Ok(Some((ty1, ty2)))
676                                 } else {
677                                     Ok(None)
678                                 }
679                             });
680
681                             if let Some(Some((ty1, ty2))) = opt_ty1_ty2 {
682                                 *rval = Rvalue::Aggregate(
683                                     Box::new(AggregateKind::Tuple),
684                                     vec![
685                                         self.operand_from_scalar(one, ty1, source_info.span),
686                                         self.operand_from_scalar(two, ty2, source_info.span),
687                                     ],
688                                 );
689                             }
690                         }
691                     }
692                 }
693                 _ => {}
694             }
695         }
696     }
697
698     fn should_const_prop(&mut self, op: OpTy<'tcx>) -> bool {
699         let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level;
700
701         if mir_opt_level == 0 {
702             return false;
703         }
704
705         match *op {
706             interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Scalar(s))) => {
707                 s.is_bits()
708             }
709             interpret::Operand::Immediate(Immediate::ScalarPair(
710                 ScalarMaybeUndef::Scalar(l),
711                 ScalarMaybeUndef::Scalar(r),
712             )) => l.is_bits() && r.is_bits(),
713             interpret::Operand::Indirect(_) if mir_opt_level >= 2 => {
714                 let mplace = op.assert_mem_place(&self.ecx);
715                 intern_const_alloc_recursive(&mut self.ecx, None, mplace, false)
716                     .expect("failed to intern alloc");
717                 true
718             }
719             _ => false,
720         }
721     }
722 }
723
724 /// The mode that `ConstProp` is allowed to run in for a given `Local`.
725 #[derive(Clone, Copy, Debug, PartialEq)]
726 enum ConstPropMode {
727     /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
728     FullConstProp,
729     /// The `Local` can be propagated into but reads cannot be propagated.
730     OnlyPropagateInto,
731     /// No propagation is allowed at all.
732     NoPropagation,
733 }
734
735 struct CanConstProp {
736     can_const_prop: IndexVec<Local, ConstPropMode>,
737     // false at the beginning, once set, there are not allowed to be any more assignments
738     found_assignment: IndexVec<Local, bool>,
739 }
740
741 impl CanConstProp {
742     /// returns true if `local` can be propagated
743     fn check(body: ReadOnlyBodyAndCache<'_, '_>) -> IndexVec<Local, ConstPropMode> {
744         let mut cpv = CanConstProp {
745             can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
746             found_assignment: IndexVec::from_elem(false, &body.local_decls),
747         };
748         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
749             // cannot use args at all
750             // cannot use locals because if x < y { y - x } else { x - y } would
751             //        lint for x != y
752             // FIXME(oli-obk): lint variables until they are used in a condition
753             // FIXME(oli-obk): lint if return value is constant
754             let local_kind = body.local_kind(local);
755
756             if local_kind == LocalKind::Arg || local_kind == LocalKind::Var {
757                 *val = ConstPropMode::OnlyPropagateInto;
758                 trace!("local {:?} can't be const propagated because it's not a temporary", local);
759             }
760         }
761         cpv.visit_body(body);
762         cpv.can_const_prop
763     }
764 }
765
766 impl<'tcx> Visitor<'tcx> for CanConstProp {
767     fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
768         use rustc::mir::visit::PlaceContext::*;
769         match context {
770             // Constants must have at most one write
771             // FIXME(oli-obk): we could be more powerful here, if the multiple writes
772             // only occur in independent execution paths
773             MutatingUse(MutatingUseContext::Store) => {
774                 if self.found_assignment[local] {
775                     trace!("local {:?} can't be propagated because of multiple assignments", local);
776                     self.can_const_prop[local] = ConstPropMode::NoPropagation;
777                 } else {
778                     self.found_assignment[local] = true
779                 }
780             }
781             // Reading constants is allowed an arbitrary number of times
782             NonMutatingUse(NonMutatingUseContext::Copy)
783             | NonMutatingUse(NonMutatingUseContext::Move)
784             | NonMutatingUse(NonMutatingUseContext::Inspect)
785             | NonMutatingUse(NonMutatingUseContext::Projection)
786             | MutatingUse(MutatingUseContext::Projection)
787             | NonUse(_) => {}
788             _ => {
789                 trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
790                 self.can_const_prop[local] = ConstPropMode::NoPropagation;
791             }
792         }
793     }
794 }
795
796 impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
797     fn tcx(&self) -> TyCtxt<'tcx> {
798         self.tcx
799     }
800
801     fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
802         trace!("visit_constant: {:?}", constant);
803         self.super_constant(constant, location);
804         self.eval_constant(constant);
805     }
806
807     fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
808         trace!("visit_statement: {:?}", statement);
809         let source_info = statement.source_info;
810         self.source_info = Some(source_info);
811         self.lint_root = self.lint_root(source_info);
812         if let StatementKind::Assign(box (ref place, ref mut rval)) = statement.kind {
813             let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
814             if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
815                 if let Some(local) = place.as_local() {
816                     let can_const_prop = self.can_const_prop[local];
817                     if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
818                         if can_const_prop == ConstPropMode::FullConstProp
819                             || can_const_prop == ConstPropMode::OnlyPropagateInto
820                         {
821                             if let Some(value) = self.get_const(local) {
822                                 if self.should_const_prop(value) {
823                                     trace!("replacing {:?} with {:?}", rval, value);
824                                     self.replace_with_const(rval, value, statement.source_info);
825
826                                     if can_const_prop == ConstPropMode::FullConstProp {
827                                         trace!("propagated into {:?}", local);
828                                     }
829                                 }
830                             }
831                         }
832                     }
833                     if self.can_const_prop[local] != ConstPropMode::FullConstProp {
834                         trace!("can't propagate into {:?}", local);
835                         if local != RETURN_PLACE {
836                             self.remove_const(local);
837                         }
838                     }
839                 }
840             }
841         } else {
842             match statement.kind {
843                 StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
844                     let frame = self.ecx.frame_mut();
845                     frame.locals[local].value =
846                         if let StatementKind::StorageLive(_) = statement.kind {
847                             LocalValue::Uninitialized
848                         } else {
849                             LocalValue::Dead
850                         };
851                 }
852                 _ => {}
853             }
854         }
855
856         self.super_statement(statement, location);
857     }
858
859     fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
860         let source_info = terminator.source_info;
861         self.source_info = Some(source_info);
862         self.super_terminator(terminator, location);
863         self.lint_root = self.lint_root(source_info);
864         match &mut terminator.kind {
865             TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
866                 if let Some(value) = self.eval_operand(&cond, source_info) {
867                     trace!("assertion on {:?} should be {:?}", value, expected);
868                     let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
869                     let value_const = self.ecx.read_scalar(value).unwrap();
870                     if expected != value_const {
871                         // poison all places this operand references so that further code
872                         // doesn't use the invalid value
873                         match cond {
874                             Operand::Move(ref place) | Operand::Copy(ref place) => {
875                                 self.remove_const(place.local);
876                             }
877                             Operand::Constant(_) => {}
878                         }
879                         let span = terminator.source_info.span;
880                         let hir_id = self
881                             .tcx
882                             .hir()
883                             .as_local_hir_id(self.source.def_id())
884                             .expect("some part of a failing const eval must be local");
885                         let msg = match msg {
886                             PanicInfo::Overflow(_)
887                             | PanicInfo::OverflowNeg
888                             | PanicInfo::DivisionByZero
889                             | PanicInfo::RemainderByZero => msg.description().to_owned(),
890                             PanicInfo::BoundsCheck { ref len, ref index } => {
891                                 let len =
892                                     self.eval_operand(len, source_info).expect("len must be const");
893                                 let len = match self.ecx.read_scalar(len) {
894                                     Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
895                                     other => bug!("const len not primitive: {:?}", other),
896                                 };
897                                 let index = self
898                                     .eval_operand(index, source_info)
899                                     .expect("index must be const");
900                                 let index = match self.ecx.read_scalar(index) {
901                                     Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
902                                     other => bug!("const index not primitive: {:?}", other),
903                                 };
904                                 format!(
905                                     "index out of bounds: \
906                                     the len is {} but the index is {}",
907                                     len, index,
908                                 )
909                             }
910                             // Need proper const propagator for these
911                             _ => return,
912                         };
913                         self.tcx.lint_hir(::rustc::lint::builtin::CONST_ERR, hir_id, span, &msg);
914                     } else {
915                         if self.should_const_prop(value) {
916                             if let ScalarMaybeUndef::Scalar(scalar) = value_const {
917                                 *cond = self.operand_from_scalar(
918                                     scalar,
919                                     self.tcx.types.bool,
920                                     source_info.span,
921                                 );
922                             }
923                         }
924                     }
925                 }
926             }
927             TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => {
928                 if let Some(value) = self.eval_operand(&discr, source_info) {
929                     if self.should_const_prop(value) {
930                         if let ScalarMaybeUndef::Scalar(scalar) =
931                             self.ecx.read_scalar(value).unwrap()
932                         {
933                             *discr = self.operand_from_scalar(scalar, switch_ty, source_info.span);
934                         }
935                     }
936                 }
937             }
938             //none of these have Operands to const-propagate
939             TerminatorKind::Goto { .. }
940             | TerminatorKind::Resume
941             | TerminatorKind::Abort
942             | TerminatorKind::Return
943             | TerminatorKind::Unreachable
944             | TerminatorKind::Drop { .. }
945             | TerminatorKind::DropAndReplace { .. }
946             | TerminatorKind::Yield { .. }
947             | TerminatorKind::GeneratorDrop
948             | TerminatorKind::FalseEdges { .. }
949             | TerminatorKind::FalseUnwind { .. } => {}
950             //FIXME(wesleywiser) Call does have Operands that could be const-propagated
951             TerminatorKind::Call { .. } => {}
952         }
953     }
954 }