]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/const_prop.rs
Fix `Instance::resolve()` incorrectly returning specialized instances
[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::mir::interpret::{InterpResult, PanicInfo, Scalar};
10 use rustc::mir::visit::{
11     MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
12 };
13 use rustc::mir::{
14     read_only, AggregateKind, BasicBlock, BinOp, Body, BodyAndCache, ClearCrossCrate, Constant,
15     Local, LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, ReadOnlyBodyAndCache, Rvalue,
16     SourceInfo, SourceScope, SourceScopeData, Statement, StatementKind, Terminator, TerminatorKind,
17     UnOp, RETURN_PLACE,
18 };
19 use rustc::ty::layout::{
20     HasDataLayout, HasTyCtxt, LayoutError, LayoutOf, Size, TargetDataLayout, TyLayout,
21 };
22 use rustc::ty::subst::InternalSubsts;
23 use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
24 use rustc_data_structures::fx::FxHashMap;
25 use rustc_index::vec::IndexVec;
26 use syntax::ast::Mutability;
27 use syntax_pos::{Span, DUMMY_SP};
28
29 use crate::const_eval::error_to_const_error;
30 use crate::interpret::{
31     self, intern_const_alloc_recursive, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx,
32     LocalState, LocalValue, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer,
33     ScalarMaybeUndef, StackPopCleanup,
34 };
35 use crate::rustc::ty::subst::Subst;
36 use crate::rustc::ty::TypeFoldable;
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 }
265
266 impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
267     type Ty = Ty<'tcx>;
268     type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
269
270     fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
271         self.tcx.layout_of(self.param_env.and(ty))
272     }
273 }
274
275 impl<'mir, 'tcx> HasDataLayout for ConstPropagator<'mir, 'tcx> {
276     #[inline]
277     fn data_layout(&self) -> &TargetDataLayout {
278         &self.tcx.data_layout
279     }
280 }
281
282 impl<'mir, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
283     #[inline]
284     fn tcx(&self) -> TyCtxt<'tcx> {
285         self.tcx
286     }
287 }
288
289 impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
290     fn new(
291         body: ReadOnlyBodyAndCache<'_, 'tcx>,
292         dummy_body: &'mir Body<'tcx>,
293         tcx: TyCtxt<'tcx>,
294         source: MirSource<'tcx>,
295     ) -> ConstPropagator<'mir, 'tcx> {
296         let def_id = source.def_id();
297         let substs = &InternalSubsts::identity_for_item(tcx, def_id);
298         let mut param_env = tcx.param_env(def_id);
299
300         // If we're evaluating inside a monomorphic function, then use `Reveal::All` because
301         // we want to see the same instances that codegen will see. This allows us to `resolve()`
302         // specializations.
303         if !substs.needs_subst() {
304             param_env = param_env.with_reveal_all();
305         }
306
307         let span = tcx.def_span(def_id);
308         let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ());
309         let can_const_prop = CanConstProp::check(body);
310
311         let ret = ecx
312             .layout_of(body.return_ty().subst(tcx, substs))
313             .ok()
314             // Don't bother allocating memory for ZST types which have no values
315             // or for large values.
316             .filter(|ret_layout| {
317                 !ret_layout.is_zst() && ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT)
318             })
319             .map(|ret_layout| ecx.allocate(ret_layout, MemoryKind::Stack));
320
321         ecx.push_stack_frame(
322             Instance::new(def_id, substs),
323             span,
324             dummy_body,
325             ret.map(Into::into),
326             StackPopCleanup::None { cleanup: false },
327         )
328         .expect("failed to push initial stack frame");
329
330         ConstPropagator {
331             ecx,
332             tcx,
333             source,
334             param_env,
335             can_const_prop,
336             // FIXME(eddyb) avoid cloning these two fields more than once,
337             // by accessing them through `ecx` instead.
338             source_scopes: body.source_scopes.clone(),
339             //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
340             local_decls: body.local_decls.clone(),
341             ret: ret.map(Into::into),
342         }
343     }
344
345     fn get_const(&self, local: Local) -> Option<Const<'tcx>> {
346         if local == RETURN_PLACE {
347             // Try to read the return place as an immediate so that if it is representable as a
348             // scalar, we can handle it as such, but otherwise, just return the value as is.
349             return match self.ret.map(|ret| self.ecx.try_read_immediate(ret)) {
350                 Some(Ok(Ok(imm))) => Some(imm.into()),
351                 _ => self.ret,
352             };
353         }
354
355         self.ecx.access_local(self.ecx.frame(), local, None).ok()
356     }
357
358     fn remove_const(&mut self, local: Local) {
359         self.ecx.frame_mut().locals[local] =
360             LocalState { value: LocalValue::Uninitialized, layout: Cell::new(None) };
361     }
362
363     fn use_ecx<F, T>(&mut self, source_info: SourceInfo, f: F) -> Option<T>
364     where
365         F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
366     {
367         self.ecx.tcx.span = source_info.span;
368         // FIXME(eddyb) move this to the `Panic(_)` error case, so that
369         // `f(self)` is always called, and that the only difference when the
370         // scope's `local_data` is missing, is that the lint isn't emitted.
371         let lint_root = match &self.source_scopes[source_info.scope].local_data {
372             ClearCrossCrate::Set(data) => data.lint_root,
373             ClearCrossCrate::Clear => return None,
374         };
375         let r = match f(self) {
376             Ok(val) => Some(val),
377             Err(error) => {
378                 use rustc::mir::interpret::{
379                     InterpError::*, UndefinedBehaviorInfo, UnsupportedOpInfo,
380                 };
381                 match error.kind {
382                     MachineStop(_) => bug!("ConstProp does not stop"),
383
384                     // Some error shouldn't come up because creating them causes
385                     // an allocation, which we should avoid. When that happens,
386                     // dedicated error variants should be introduced instead.
387                     // Only test this in debug builds though to avoid disruptions.
388                     Unsupported(UnsupportedOpInfo::Unsupported(_))
389                     | Unsupported(UnsupportedOpInfo::ValidationFailure(_))
390                     | UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
391                     | UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_))
392                         if cfg!(debug_assertions) =>
393                     {
394                         bug!("const-prop encountered allocating error: {:?}", error.kind);
395                     }
396
397                     Unsupported(_)
398                     | UndefinedBehavior(_)
399                     | InvalidProgram(_)
400                     | ResourceExhaustion(_) => {
401                         // Ignore these errors.
402                     }
403                     Panic(_) => {
404                         let diagnostic = error_to_const_error(&self.ecx, error);
405                         diagnostic.report_as_lint(
406                             self.ecx.tcx,
407                             "this expression will panic at runtime",
408                             lint_root,
409                             None,
410                         );
411                     }
412                 }
413                 None
414             }
415         };
416         self.ecx.tcx.span = DUMMY_SP;
417         r
418     }
419
420     fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<Const<'tcx>> {
421         self.ecx.tcx.span = c.span;
422         match self.ecx.eval_const_to_op(c.literal, None) {
423             Ok(op) => Some(op),
424             Err(error) => {
425                 let err = error_to_const_error(&self.ecx, error);
426                 err.report_as_error(self.ecx.tcx, "erroneous constant used");
427                 None
428             }
429         }
430     }
431
432     fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
433         trace!("eval_place(place={:?})", place);
434         self.use_ecx(source_info, |this| this.ecx.eval_place_to_op(place, None))
435     }
436
437     fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
438         match *op {
439             Operand::Constant(ref c) => self.eval_constant(c),
440             Operand::Move(ref place) | Operand::Copy(ref place) => {
441                 self.eval_place(place, source_info)
442             }
443         }
444     }
445
446     fn const_prop(
447         &mut self,
448         rvalue: &Rvalue<'tcx>,
449         place_layout: TyLayout<'tcx>,
450         source_info: SourceInfo,
451         place: &Place<'tcx>,
452     ) -> Option<()> {
453         let span = source_info.span;
454
455         // #66397: Don't try to eval into large places as that can cause an OOM
456         if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
457             return None;
458         }
459
460         let overflow_check = self.tcx.sess.overflow_checks();
461
462         // Perform any special handling for specific Rvalue types.
463         // Generally, checks here fall into one of two categories:
464         //   1. Additional checking to provide useful lints to the user
465         //        - In this case, we will do some validation and then fall through to the
466         //          end of the function which evals the assignment.
467         //   2. Working around bugs in other parts of the compiler
468         //        - In this case, we'll return `None` from this function to stop evaluation.
469         match rvalue {
470             // Additional checking: if overflow checks are disabled (which is usually the case in
471             // release mode), then we need to do additional checking here to give lints to the user
472             // if an overflow would occur.
473             Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => {
474                 trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
475
476                 self.use_ecx(source_info, |this| {
477                     let ty = arg.ty(&this.local_decls, this.tcx);
478
479                     if ty.is_integral() {
480                         let arg = this.ecx.eval_operand(arg, None)?;
481                         let prim = this.ecx.read_immediate(arg)?;
482                         // Need to do overflow check here: For actual CTFE, MIR
483                         // generation emits code that does this before calling the op.
484                         if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
485                             throw_panic!(OverflowNeg)
486                         }
487                     }
488
489                     Ok(())
490                 })?;
491             }
492
493             // Additional checking: check for overflows on integer binary operations and report
494             // them to the user as lints.
495             Rvalue::BinaryOp(op, left, right) => {
496                 trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
497
498                 let r = self.use_ecx(source_info, |this| {
499                     this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
500                 })?;
501                 if *op == BinOp::Shr || *op == BinOp::Shl {
502                     let left_bits = place_layout.size.bits();
503                     let right_size = r.layout.size;
504                     let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
505                     if r_bits.map_or(false, |b| b >= left_bits as u128) {
506                         let lint_root = match &self.source_scopes[source_info.scope].local_data {
507                             ClearCrossCrate::Set(data) => data.lint_root,
508                             ClearCrossCrate::Clear => return None,
509                         };
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                             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
540             // Work around: avoid ICE in miri. FIXME(wesleywiser)
541             // The Miri engine ICEs when taking a reference to an uninitialized unsized
542             // local. There's nothing it can do here: taking a reference needs an allocation
543             // which needs to know the size. Normally that's okay as during execution
544             // (e.g. for CTFE) it can never happen. But here in const_prop
545             // unknown data is uninitialized, so if e.g. a function argument is unsized
546             // and has a reference taken, we get an ICE.
547             Rvalue::Ref(_, _, place_ref) => {
548                 trace!("checking Ref({:?})", place_ref);
549
550                 if let Some(local) = place_ref.as_local() {
551                     let alive = if let LocalValue::Live(_) = self.ecx.frame().locals[local].value {
552                         true
553                     } else {
554                         false
555                     };
556
557                     if !alive {
558                         trace!("skipping Ref({:?}) to uninitialized local", place);
559                         return None;
560                     }
561                 }
562             }
563
564             _ => {}
565         }
566
567         self.use_ecx(source_info, |this| {
568             trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
569             this.ecx.eval_rvalue_into_place(rvalue, place)?;
570             Ok(())
571         })
572     }
573
574     fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
575         Operand::Constant(Box::new(Constant {
576             span,
577             user_ty: None,
578             literal: self.tcx.mk_const(*ty::Const::from_scalar(self.tcx, scalar, ty)),
579         }))
580     }
581
582     fn replace_with_const(
583         &mut self,
584         rval: &mut Rvalue<'tcx>,
585         value: Const<'tcx>,
586         source_info: SourceInfo,
587     ) {
588         trace!("attepting to replace {:?} with {:?}", rval, value);
589         if let Err(e) = self.ecx.validate_operand(
590             value,
591             vec![],
592             // FIXME: is ref tracking too expensive?
593             Some(&mut interpret::RefTracking::empty()),
594         ) {
595             trace!("validation error, attempt failed: {:?}", e);
596             return;
597         }
598
599         // FIXME> figure out what tho do when try_read_immediate fails
600         let imm = self.use_ecx(source_info, |this| this.ecx.try_read_immediate(value));
601
602         if let Some(Ok(imm)) = imm {
603             match *imm {
604                 interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => {
605                     *rval = Rvalue::Use(self.operand_from_scalar(
606                         scalar,
607                         value.layout.ty,
608                         source_info.span,
609                     ));
610                 }
611                 Immediate::ScalarPair(
612                     ScalarMaybeUndef::Scalar(one),
613                     ScalarMaybeUndef::Scalar(two),
614                 ) => {
615                     // Found a value represented as a pair. For now only do cont-prop if type of
616                     // Rvalue is also a pair with two scalars. The more general case is more
617                     // complicated to implement so we'll do it later.
618                     let ty = &value.layout.ty.kind;
619                     // Only do it for tuples
620                     if let ty::Tuple(substs) = ty {
621                         // Only do it if tuple is also a pair with two scalars
622                         if substs.len() == 2 {
623                             let opt_ty1_ty2 = self.use_ecx(source_info, |this| {
624                                 let ty1 = substs[0].expect_ty();
625                                 let ty2 = substs[1].expect_ty();
626                                 let ty_is_scalar = |ty| {
627                                     this.ecx.layout_of(ty).ok().map(|ty| ty.details.abi.is_scalar())
628                                         == Some(true)
629                                 };
630                                 if ty_is_scalar(ty1) && ty_is_scalar(ty2) {
631                                     Ok(Some((ty1, ty2)))
632                                 } else {
633                                     Ok(None)
634                                 }
635                             });
636
637                             if let Some(Some((ty1, ty2))) = opt_ty1_ty2 {
638                                 *rval = Rvalue::Aggregate(
639                                     Box::new(AggregateKind::Tuple),
640                                     vec![
641                                         self.operand_from_scalar(one, ty1, source_info.span),
642                                         self.operand_from_scalar(two, ty2, source_info.span),
643                                     ],
644                                 );
645                             }
646                         }
647                     }
648                 }
649                 _ => {}
650             }
651         }
652     }
653
654     fn should_const_prop(&mut self, op: OpTy<'tcx>) -> bool {
655         let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level;
656
657         if mir_opt_level == 0 {
658             return false;
659         }
660
661         match *op {
662             interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Scalar(s))) => {
663                 s.is_bits()
664             }
665             interpret::Operand::Immediate(Immediate::ScalarPair(
666                 ScalarMaybeUndef::Scalar(l),
667                 ScalarMaybeUndef::Scalar(r),
668             )) => l.is_bits() && r.is_bits(),
669             interpret::Operand::Indirect(_) if mir_opt_level >= 2 => {
670                 intern_const_alloc_recursive(&mut self.ecx, None, op.assert_mem_place())
671                     .expect("failed to intern alloc");
672                 true
673             }
674             _ => false,
675         }
676     }
677 }
678
679 /// The mode that `ConstProp` is allowed to run in for a given `Local`.
680 #[derive(Clone, Copy, Debug, PartialEq)]
681 enum ConstPropMode {
682     /// The `Local` can be propagated into and reads of this `Local` can also be propagated.
683     FullConstProp,
684     /// The `Local` can be propagated into but reads cannot be propagated.
685     OnlyPropagateInto,
686     /// No propagation is allowed at all.
687     NoPropagation,
688 }
689
690 struct CanConstProp {
691     can_const_prop: IndexVec<Local, ConstPropMode>,
692     // false at the beginning, once set, there are not allowed to be any more assignments
693     found_assignment: IndexVec<Local, bool>,
694 }
695
696 impl CanConstProp {
697     /// returns true if `local` can be propagated
698     fn check(body: ReadOnlyBodyAndCache<'_, '_>) -> IndexVec<Local, ConstPropMode> {
699         let mut cpv = CanConstProp {
700             can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
701             found_assignment: IndexVec::from_elem(false, &body.local_decls),
702         };
703         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
704             // cannot use args at all
705             // cannot use locals because if x < y { y - x } else { x - y } would
706             //        lint for x != y
707             // FIXME(oli-obk): lint variables until they are used in a condition
708             // FIXME(oli-obk): lint if return value is constant
709             let local_kind = body.local_kind(local);
710
711             if local_kind == LocalKind::Arg || local_kind == LocalKind::Var {
712                 *val = ConstPropMode::OnlyPropagateInto;
713                 trace!("local {:?} can't be const propagated because it's not a temporary", local);
714             }
715         }
716         cpv.visit_body(body);
717         cpv.can_const_prop
718     }
719 }
720
721 impl<'tcx> Visitor<'tcx> for CanConstProp {
722     fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
723         use rustc::mir::visit::PlaceContext::*;
724         match context {
725             // Constants must have at most one write
726             // FIXME(oli-obk): we could be more powerful here, if the multiple writes
727             // only occur in independent execution paths
728             MutatingUse(MutatingUseContext::Store) => {
729                 if self.found_assignment[local] {
730                     trace!("local {:?} can't be propagated because of multiple assignments", local);
731                     self.can_const_prop[local] = ConstPropMode::NoPropagation;
732                 } else {
733                     self.found_assignment[local] = true
734                 }
735             }
736             // Reading constants is allowed an arbitrary number of times
737             NonMutatingUse(NonMutatingUseContext::Copy)
738             | NonMutatingUse(NonMutatingUseContext::Move)
739             | NonMutatingUse(NonMutatingUseContext::Inspect)
740             | NonMutatingUse(NonMutatingUseContext::Projection)
741             | MutatingUse(MutatingUseContext::Projection)
742             | NonUse(_) => {}
743             _ => {
744                 trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
745                 self.can_const_prop[local] = ConstPropMode::NoPropagation;
746             }
747         }
748     }
749 }
750
751 impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
752     fn tcx(&self) -> TyCtxt<'tcx> {
753         self.tcx
754     }
755
756     fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
757         trace!("visit_constant: {:?}", constant);
758         self.super_constant(constant, location);
759         self.eval_constant(constant);
760     }
761
762     fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
763         trace!("visit_statement: {:?}", statement);
764         if let StatementKind::Assign(box (ref place, ref mut rval)) = statement.kind {
765             let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
766             if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
767                 if let Some(local) = place.as_local() {
768                     let source = statement.source_info;
769                     let can_const_prop = self.can_const_prop[local];
770                     if let Some(()) = self.const_prop(rval, place_layout, source, place) {
771                         if can_const_prop == ConstPropMode::FullConstProp
772                             || can_const_prop == ConstPropMode::OnlyPropagateInto
773                         {
774                             if let Some(value) = self.get_const(local) {
775                                 if self.should_const_prop(value) {
776                                     trace!("replacing {:?} with {:?}", rval, value);
777                                     self.replace_with_const(rval, value, statement.source_info);
778
779                                     if can_const_prop == ConstPropMode::FullConstProp {
780                                         trace!("propagated into {:?}", local);
781                                     }
782                                 }
783                             }
784                         }
785                     }
786                     if self.can_const_prop[local] != ConstPropMode::FullConstProp {
787                         trace!("can't propagate into {:?}", local);
788                         if local != RETURN_PLACE {
789                             self.remove_const(local);
790                         }
791                     }
792                 }
793             }
794         } else {
795             match statement.kind {
796                 StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
797                     let frame = self.ecx.frame_mut();
798                     frame.locals[local].value =
799                         if let StatementKind::StorageLive(_) = statement.kind {
800                             LocalValue::Uninitialized
801                         } else {
802                             LocalValue::Dead
803                         };
804                 }
805                 _ => {}
806             }
807         }
808
809         self.super_statement(statement, location);
810     }
811
812     fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
813         self.super_terminator(terminator, location);
814         let source_info = terminator.source_info;
815         match &mut terminator.kind {
816             TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
817                 if let Some(value) = self.eval_operand(&cond, source_info) {
818                     trace!("assertion on {:?} should be {:?}", value, expected);
819                     let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
820                     let value_const = self.ecx.read_scalar(value).unwrap();
821                     if expected != value_const {
822                         // poison all places this operand references so that further code
823                         // doesn't use the invalid value
824                         match cond {
825                             Operand::Move(ref place) | Operand::Copy(ref place) => {
826                                 if let PlaceBase::Local(local) = place.base {
827                                     self.remove_const(local);
828                                 }
829                             }
830                             Operand::Constant(_) => {}
831                         }
832                         let span = terminator.source_info.span;
833                         let hir_id = self
834                             .tcx
835                             .hir()
836                             .as_local_hir_id(self.source.def_id())
837                             .expect("some part of a failing const eval must be local");
838                         let msg = match msg {
839                             PanicInfo::Overflow(_)
840                             | PanicInfo::OverflowNeg
841                             | PanicInfo::DivisionByZero
842                             | PanicInfo::RemainderByZero => msg.description().to_owned(),
843                             PanicInfo::BoundsCheck { ref len, ref index } => {
844                                 let len =
845                                     self.eval_operand(len, source_info).expect("len must be const");
846                                 let len = match self.ecx.read_scalar(len) {
847                                     Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
848                                     other => bug!("const len not primitive: {:?}", other),
849                                 };
850                                 let index = self
851                                     .eval_operand(index, source_info)
852                                     .expect("index must be const");
853                                 let index = match self.ecx.read_scalar(index) {
854                                     Ok(ScalarMaybeUndef::Scalar(Scalar::Raw { data, .. })) => data,
855                                     other => bug!("const index not primitive: {:?}", other),
856                                 };
857                                 format!(
858                                     "index out of bounds: \
859                                     the len is {} but the index is {}",
860                                     len, index,
861                                 )
862                             }
863                             // Need proper const propagator for these
864                             _ => return,
865                         };
866                         self.tcx.lint_hir(::rustc::lint::builtin::CONST_ERR, hir_id, span, &msg);
867                     } else {
868                         if self.should_const_prop(value) {
869                             if let ScalarMaybeUndef::Scalar(scalar) = value_const {
870                                 *cond = self.operand_from_scalar(
871                                     scalar,
872                                     self.tcx.types.bool,
873                                     source_info.span,
874                                 );
875                             }
876                         }
877                     }
878                 }
879             }
880             TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => {
881                 if let Some(value) = self.eval_operand(&discr, source_info) {
882                     if self.should_const_prop(value) {
883                         if let ScalarMaybeUndef::Scalar(scalar) =
884                             self.ecx.read_scalar(value).unwrap()
885                         {
886                             *discr = self.operand_from_scalar(scalar, switch_ty, source_info.span);
887                         }
888                     }
889                 }
890             }
891             //none of these have Operands to const-propagate
892             TerminatorKind::Goto { .. }
893             | TerminatorKind::Resume
894             | TerminatorKind::Abort
895             | TerminatorKind::Return
896             | TerminatorKind::Unreachable
897             | TerminatorKind::Drop { .. }
898             | TerminatorKind::DropAndReplace { .. }
899             | TerminatorKind::Yield { .. }
900             | TerminatorKind::GeneratorDrop
901             | TerminatorKind::FalseEdges { .. }
902             | TerminatorKind::FalseUnwind { .. } => {}
903             //FIXME(wesleywiser) Call does have Operands that could be const-propagated
904             TerminatorKind::Call { .. } => {}
905         }
906     }
907 }