]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/const_prop.rs
Rollup merge of #60959 - petrochenkov:sassert, r=estebank
[rust.git] / src / librustc_mir / transform / const_prop.rs
1 //! Propagates constants for early reporting of statically known
2 //! assertion failures
3
4 use rustc::hir::def::DefKind;
5 use rustc::mir::{
6     AggregateKind, Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local,
7     NullOp, UnOp, StatementKind, Statement, LocalKind, Static, StaticKind,
8     TerminatorKind, Terminator,  ClearCrossCrate, SourceInfo, BinOp, ProjectionElem,
9     SourceScope, SourceScopeLocalData, LocalDecl, Promoted,
10 };
11 use rustc::mir::visit::{
12     Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext,
13 };
14 use rustc::mir::interpret::{InterpError, Scalar, GlobalId, EvalResult};
15 use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
16 use syntax_pos::{Span, DUMMY_SP};
17 use rustc::ty::subst::InternalSubsts;
18 use rustc_data_structures::indexed_vec::IndexVec;
19 use rustc::ty::layout::{
20     LayoutOf, TyLayout, LayoutError,
21     HasTyCtxt, TargetDataLayout, HasDataLayout,
22 };
23
24 use crate::interpret::{self, InterpretCx, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind};
25 use crate::const_eval::{
26     CompileTimeInterpreter, error_to_const_error, eval_promoted, mk_eval_cx,
27 };
28 use crate::transform::{MirPass, MirSource};
29
30 pub struct ConstProp;
31
32 impl MirPass for ConstProp {
33     fn run_pass<'a, 'tcx>(&self,
34                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
35                           source: MirSource<'tcx>,
36                           mir: &mut Mir<'tcx>) {
37         // will be evaluated by miri and produce its errors there
38         if source.promoted.is_some() {
39             return;
40         }
41
42         use rustc::hir::map::blocks::FnLikeNode;
43         let hir_id = tcx.hir().as_local_hir_id(source.def_id())
44                               .expect("Non-local call to local provider is_const_fn");
45
46         let is_fn_like = FnLikeNode::from_node(tcx.hir().get_by_hir_id(hir_id)).is_some();
47         let is_assoc_const = match tcx.def_kind(source.def_id()) {
48             Some(DefKind::AssociatedConst) => true,
49             _ => false,
50         };
51
52         // Only run const prop on functions, methods, closures and associated constants
53         if !is_fn_like && !is_assoc_const  {
54             // skip anon_const/statics/consts because they'll be evaluated by miri anyway
55             trace!("ConstProp skipped for {:?}", source.def_id());
56             return
57         }
58
59         trace!("ConstProp starting for {:?}", source.def_id());
60
61         // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
62         // constants, instead of just checking for const-folding succeeding.
63         // That would require an uniform one-def no-mutation analysis
64         // and RPO (or recursing when needing the value of a local).
65         let mut optimization_finder = ConstPropagator::new(mir, tcx, source);
66         optimization_finder.visit_mir(mir);
67
68         // put back the data we stole from `mir`
69         std::mem::replace(
70             &mut mir.source_scope_local_data,
71             optimization_finder.source_scope_local_data
72         );
73         std::mem::replace(
74             &mut mir.promoted,
75             optimization_finder.promoted
76         );
77
78         trace!("ConstProp done for {:?}", source.def_id());
79     }
80 }
81
82 type Const<'tcx> = OpTy<'tcx>;
83
84 /// Finds optimization opportunities on the MIR.
85 struct ConstPropagator<'a, 'mir, 'tcx:'a+'mir> {
86     ecx: InterpretCx<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>,
87     tcx: TyCtxt<'a, 'tcx, 'tcx>,
88     source: MirSource<'tcx>,
89     places: IndexVec<Local, Option<Const<'tcx>>>,
90     can_const_prop: IndexVec<Local, bool>,
91     param_env: ParamEnv<'tcx>,
92     source_scope_local_data: ClearCrossCrate<IndexVec<SourceScope, SourceScopeLocalData>>,
93     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
94     promoted: IndexVec<Promoted, Mir<'tcx>>,
95 }
96
97 impl<'a, 'b, 'tcx> LayoutOf for ConstPropagator<'a, 'b, 'tcx> {
98     type Ty = Ty<'tcx>;
99     type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
100
101     fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
102         self.tcx.layout_of(self.param_env.and(ty))
103     }
104 }
105
106 impl<'a, 'b, 'tcx> HasDataLayout for ConstPropagator<'a, 'b, 'tcx> {
107     #[inline]
108     fn data_layout(&self) -> &TargetDataLayout {
109         &self.tcx.data_layout
110     }
111 }
112
113 impl<'a, 'b, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'a, 'b, 'tcx> {
114     #[inline]
115     fn tcx<'c>(&'c self) -> TyCtxt<'c, 'tcx, 'tcx> {
116         self.tcx
117     }
118 }
119
120 impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> {
121     fn new(
122         mir: &mut Mir<'tcx>,
123         tcx: TyCtxt<'a, 'tcx, 'tcx>,
124         source: MirSource<'tcx>,
125     ) -> ConstPropagator<'a, 'mir, 'tcx> {
126         let param_env = tcx.param_env(source.def_id());
127         let ecx = mk_eval_cx(tcx, tcx.def_span(source.def_id()), param_env);
128         let can_const_prop = CanConstProp::check(mir);
129         let source_scope_local_data = std::mem::replace(
130             &mut mir.source_scope_local_data,
131             ClearCrossCrate::Clear
132         );
133         let promoted = std::mem::replace(
134             &mut mir.promoted,
135             IndexVec::new()
136         );
137
138         ConstPropagator {
139             ecx,
140             tcx,
141             source,
142             param_env,
143             can_const_prop,
144             places: IndexVec::from_elem(None, &mir.local_decls),
145             source_scope_local_data,
146             //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_mir()` needs it
147             local_decls: mir.local_decls.clone(),
148             promoted,
149         }
150     }
151
152     fn use_ecx<F, T>(
153         &mut self,
154         source_info: SourceInfo,
155         f: F
156     ) -> Option<T>
157     where
158         F: FnOnce(&mut Self) -> EvalResult<'tcx, T>,
159     {
160         self.ecx.tcx.span = source_info.span;
161         let lint_root = match self.source_scope_local_data {
162             ClearCrossCrate::Set(ref ivs) => {
163                 //FIXME(#51314): remove this check
164                 if source_info.scope.index() >= ivs.len() {
165                     return None;
166                 }
167                 ivs[source_info.scope].lint_root
168             },
169             ClearCrossCrate::Clear => return None,
170         };
171         let r = match f(self) {
172             Ok(val) => Some(val),
173             Err(error) => {
174                 let diagnostic = error_to_const_error(&self.ecx, error);
175                 use rustc::mir::interpret::InterpError::*;
176                 match diagnostic.error {
177                     // don't report these, they make no sense in a const prop context
178                     | MachineError(_)
179                     | Exit(_)
180                     // at runtime these transformations might make sense
181                     // FIXME: figure out the rules and start linting
182                     | FunctionAbiMismatch(..)
183                     | FunctionArgMismatch(..)
184                     | FunctionRetMismatch(..)
185                     | FunctionArgCountMismatch
186                     // fine at runtime, might be a register address or sth
187                     | ReadBytesAsPointer
188                     // fine at runtime
189                     | ReadForeignStatic
190                     | Unimplemented(_)
191                     // don't report const evaluator limits
192                     | StackFrameLimitReached
193                     | NoMirFor(..)
194                     | InlineAsm
195                     => {},
196
197                     | InvalidMemoryAccess
198                     | DanglingPointerDeref
199                     | DoubleFree
200                     | InvalidFunctionPointer
201                     | InvalidBool
202                     | InvalidDiscriminant(..)
203                     | PointerOutOfBounds { .. }
204                     | InvalidNullPointerUsage
205                     | ValidationFailure(..)
206                     | InvalidPointerMath
207                     | ReadUndefBytes(_)
208                     | DeadLocal
209                     | InvalidBoolOp(_)
210                     | DerefFunctionPointer
211                     | ExecuteMemory
212                     | Intrinsic(..)
213                     | InvalidChar(..)
214                     | AbiViolation(_)
215                     | AlignmentCheckFailed{..}
216                     | CalledClosureAsFunction
217                     | VtableForArgumentlessMethod
218                     | ModifiedConstantMemory
219                     | ModifiedStatic
220                     | AssumptionNotHeld
221                     // FIXME: should probably be removed and turned into a bug! call
222                     | TypeNotPrimitive(_)
223                     | ReallocatedWrongMemoryKind(_, _)
224                     | DeallocatedWrongMemoryKind(_, _)
225                     | ReallocateNonBasePtr
226                     | DeallocateNonBasePtr
227                     | IncorrectAllocationInformation(..)
228                     | UnterminatedCString(_)
229                     | HeapAllocZeroBytes
230                     | HeapAllocNonPowerOfTwoAlignment(_)
231                     | Unreachable
232                     | ReadFromReturnPointer
233                     | GeneratorResumedAfterReturn
234                     | GeneratorResumedAfterPanic
235                     | ReferencedConstant
236                     | InfiniteLoop
237                     => {
238                         // FIXME: report UB here
239                     },
240
241                     | OutOfTls
242                     | TlsOutOfBounds
243                     | PathNotFound(_)
244                     => bug!("these should not be in rustc, but in miri's machine errors"),
245
246                     | Layout(_)
247                     | UnimplementedTraitSelection
248                     | TypeckError
249                     | TooGeneric
250                     // these are just noise
251                     => {},
252
253                     // non deterministic
254                     | ReadPointerAsBytes
255                     // FIXME: implement
256                     => {},
257
258                     | Panic { .. }
259                     | BoundsCheck{..}
260                     | Overflow(_)
261                     | OverflowNeg
262                     | DivisionByZero
263                     | RemainderByZero
264                     => {
265                         diagnostic.report_as_lint(
266                             self.ecx.tcx,
267                             "this expression will panic at runtime",
268                             lint_root,
269                             None,
270                         );
271                     }
272                 }
273                 None
274             },
275         };
276         self.ecx.tcx.span = DUMMY_SP;
277         r
278     }
279
280     fn eval_constant(
281         &mut self,
282         c: &Constant<'tcx>,
283     ) -> Option<Const<'tcx>> {
284         self.ecx.tcx.span = c.span;
285         match self.ecx.eval_const_to_op(*c.literal, None) {
286             Ok(op) => {
287                 Some(op)
288             },
289             Err(error) => {
290                 let err = error_to_const_error(&self.ecx, error);
291                 err.report_as_error(self.ecx.tcx, "erroneous constant used");
292                 None
293             },
294         }
295     }
296
297     fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
298         match *place {
299             Place::Base(PlaceBase::Local(loc)) => self.places[loc].clone(),
300             Place::Projection(ref proj) => match proj.elem {
301                 ProjectionElem::Field(field, _) => {
302                     trace!("field proj on {:?}", proj.base);
303                     let base = self.eval_place(&proj.base, source_info)?;
304                     let res = self.use_ecx(source_info, |this| {
305                         this.ecx.operand_field(base, field.index() as u64)
306                     })?;
307                     Some(res)
308                 },
309                 // We could get more projections by using e.g., `operand_projection`,
310                 // but we do not even have the stack frame set up properly so
311                 // an `Index` projection would throw us off-track.
312                 _ => None,
313             },
314             Place::Base(
315                 PlaceBase::Static(box Static {kind: StaticKind::Promoted(promoted), ..})
316             ) => {
317                 let generics = self.tcx.generics_of(self.source.def_id());
318                 if generics.requires_monomorphization(self.tcx) {
319                     // FIXME: can't handle code with generics
320                     return None;
321                 }
322                 let substs = InternalSubsts::identity_for_item(self.tcx, self.source.def_id());
323                 let instance = Instance::new(self.source.def_id(), substs);
324                 let cid = GlobalId {
325                     instance,
326                     promoted: Some(promoted),
327                 };
328                 // cannot use `const_eval` here, because that would require having the MIR
329                 // for the current function available, but we're producing said MIR right now
330                 let res = self.use_ecx(source_info, |this| {
331                     let mir = &this.promoted[promoted];
332                     eval_promoted(this.tcx, cid, mir, this.param_env)
333                 })?;
334                 trace!("evaluated promoted {:?} to {:?}", promoted, res);
335                 Some(res.into())
336             },
337             _ => None,
338         }
339     }
340
341     fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
342         match *op {
343             Operand::Constant(ref c) => self.eval_constant(c),
344             | Operand::Move(ref place)
345             | Operand::Copy(ref place) => self.eval_place(place, source_info),
346         }
347     }
348
349     fn const_prop(
350         &mut self,
351         rvalue: &Rvalue<'tcx>,
352         place_layout: TyLayout<'tcx>,
353         source_info: SourceInfo,
354     ) -> Option<Const<'tcx>> {
355         let span = source_info.span;
356         match *rvalue {
357             Rvalue::Use(ref op) => {
358                 self.eval_operand(op, source_info)
359             },
360             Rvalue::Repeat(..) |
361             Rvalue::Ref(..) |
362             Rvalue::Aggregate(..) |
363             Rvalue::NullaryOp(NullOp::Box, _) |
364             Rvalue::Discriminant(..) => None,
365
366             Rvalue::Cast(kind, ref operand, _) => {
367                 let op = self.eval_operand(operand, source_info)?;
368                 self.use_ecx(source_info, |this| {
369                     let dest = this.ecx.allocate(place_layout, MemoryKind::Stack);
370                     this.ecx.cast(op, kind, dest.into())?;
371                     Ok(dest.into())
372                 })
373             }
374
375             // FIXME(oli-obk): evaluate static/constant slice lengths
376             Rvalue::Len(_) => None,
377             Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
378                 type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(
379                     ImmTy {
380                         imm: Immediate::Scalar(
381                             Scalar::Bits {
382                                 bits: n as u128,
383                                 size: self.tcx.data_layout.pointer_size.bytes() as u8,
384                             }.into()
385                         ),
386                         layout: self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?,
387                     }.into()
388                 ))
389             }
390             Rvalue::UnaryOp(op, ref arg) => {
391                 let def_id = if self.tcx.is_closure(self.source.def_id()) {
392                     self.tcx.closure_base_def_id(self.source.def_id())
393                 } else {
394                     self.source.def_id()
395                 };
396                 let generics = self.tcx.generics_of(def_id);
397                 if generics.requires_monomorphization(self.tcx) {
398                     // FIXME: can't handle code with generics
399                     return None;
400                 }
401
402                 let arg = self.eval_operand(arg, source_info)?;
403                 let val = self.use_ecx(source_info, |this| {
404                     let prim = this.ecx.read_immediate(arg)?;
405                     match op {
406                         UnOp::Neg => {
407                             // Need to do overflow check here: For actual CTFE, MIR
408                             // generation emits code that does this before calling the op.
409                             if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
410                                 return err!(OverflowNeg);
411                             }
412                         }
413                         UnOp::Not => {
414                             // Cannot overflow
415                         }
416                     }
417                     // Now run the actual operation.
418                     this.ecx.unary_op(op, prim)
419                 })?;
420                 let res = ImmTy {
421                     imm: Immediate::Scalar(val.into()),
422                     layout: place_layout,
423                 };
424                 Some(res.into())
425             }
426             Rvalue::CheckedBinaryOp(op, ref left, ref right) |
427             Rvalue::BinaryOp(op, ref left, ref right) => {
428                 trace!("rvalue binop {:?} for {:?} and {:?}", op, left, right);
429                 let right = self.eval_operand(right, source_info)?;
430                 let def_id = if self.tcx.is_closure(self.source.def_id()) {
431                     self.tcx.closure_base_def_id(self.source.def_id())
432                 } else {
433                     self.source.def_id()
434                 };
435                 let generics = self.tcx.generics_of(def_id);
436                 if generics.requires_monomorphization(self.tcx) {
437                     // FIXME: can't handle code with generics
438                     return None;
439                 }
440
441                 let r = self.use_ecx(source_info, |this| {
442                     this.ecx.read_immediate(right)
443                 })?;
444                 if op == BinOp::Shr || op == BinOp::Shl {
445                     let left_ty = left.ty(&self.local_decls, self.tcx);
446                     let left_bits = self
447                         .tcx
448                         .layout_of(self.param_env.and(left_ty))
449                         .unwrap()
450                         .size
451                         .bits();
452                     let right_size = right.layout.size;
453                     let r_bits = r.to_scalar().and_then(|r| r.to_bits(right_size));
454                     if r_bits.ok().map_or(false, |b| b >= left_bits as u128) {
455                         let source_scope_local_data = match self.source_scope_local_data {
456                             ClearCrossCrate::Set(ref data) => data,
457                             ClearCrossCrate::Clear => return None,
458                         };
459                         let dir = if op == BinOp::Shr {
460                             "right"
461                         } else {
462                             "left"
463                         };
464                         let hir_id = source_scope_local_data[source_info.scope].lint_root;
465                         self.tcx.lint_hir(
466                             ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
467                             hir_id,
468                             span,
469                             &format!("attempt to shift {} with overflow", dir));
470                         return None;
471                     }
472                 }
473                 let left = self.eval_operand(left, source_info)?;
474                 let l = self.use_ecx(source_info, |this| {
475                     this.ecx.read_immediate(left)
476                 })?;
477                 trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
478                 let (val, overflow) = self.use_ecx(source_info, |this| {
479                     this.ecx.binary_op(op, l, r)
480                 })?;
481                 let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
482                     Immediate::ScalarPair(
483                         val.into(),
484                         Scalar::from_bool(overflow).into(),
485                     )
486                 } else {
487                     if overflow {
488                         let err = InterpError::Overflow(op).into();
489                         let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
490                         return None;
491                     }
492                     Immediate::Scalar(val.into())
493                 };
494                 let res = ImmTy {
495                     imm: val,
496                     layout: place_layout,
497                 };
498                 Some(res.into())
499             },
500         }
501     }
502
503     fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
504         Operand::Constant(Box::new(
505             Constant {
506                 span,
507                 ty,
508                 user_ty: None,
509                 literal: self.tcx.mk_const(ty::Const::from_scalar(
510                     scalar,
511                     ty,
512                 ))
513             }
514         ))
515     }
516
517     fn replace_with_const(&self, rval: &mut Rvalue<'tcx>, value: Const<'tcx>, span: Span) {
518         self.ecx.validate_operand(
519             value,
520             vec![],
521             None,
522             true,
523         ).expect("value should already be a valid const");
524
525         if let interpret::Operand::Immediate(im) = *value {
526             match im {
527                 interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => {
528                     *rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty, span));
529                 },
530                 Immediate::ScalarPair(
531                     ScalarMaybeUndef::Scalar(one),
532                     ScalarMaybeUndef::Scalar(two)
533                 ) => {
534                     let ty = &value.layout.ty.sty;
535                     if let ty::Tuple(substs) = ty {
536                         *rval = Rvalue::Aggregate(
537                             Box::new(AggregateKind::Tuple),
538                             vec![
539                                 self.operand_from_scalar(one, substs[0].expect_ty(), span),
540                                 self.operand_from_scalar(two, substs[1].expect_ty(), span),
541                             ],
542                         );
543                     }
544                 },
545                 _ => { }
546             }
547         }
548     }
549
550     fn should_const_prop(&self) -> bool {
551         self.tcx.sess.opts.debugging_opts.mir_opt_level >= 2
552     }
553 }
554
555 fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
556                           param_env: ty::ParamEnv<'tcx>,
557                           ty: Ty<'tcx>) -> Option<u64> {
558     tcx.layout_of(param_env.and(ty)).ok().map(|layout| layout.size.bytes())
559 }
560
561 struct CanConstProp {
562     can_const_prop: IndexVec<Local, bool>,
563     // false at the beginning, once set, there are not allowed to be any more assignments
564     found_assignment: IndexVec<Local, bool>,
565 }
566
567 impl CanConstProp {
568     /// returns true if `local` can be propagated
569     fn check(mir: &Mir<'_>) -> IndexVec<Local, bool> {
570         let mut cpv = CanConstProp {
571             can_const_prop: IndexVec::from_elem(true, &mir.local_decls),
572             found_assignment: IndexVec::from_elem(false, &mir.local_decls),
573         };
574         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
575             // cannot use args at all
576             // cannot use locals because if x < y { y - x } else { x - y } would
577             //        lint for x != y
578             // FIXME(oli-obk): lint variables until they are used in a condition
579             // FIXME(oli-obk): lint if return value is constant
580             *val = mir.local_kind(local) == LocalKind::Temp;
581         }
582         cpv.visit_mir(mir);
583         cpv.can_const_prop
584     }
585 }
586
587 impl<'tcx> Visitor<'tcx> for CanConstProp {
588     fn visit_local(
589         &mut self,
590         &local: &Local,
591         context: PlaceContext,
592         _: Location,
593     ) {
594         use rustc::mir::visit::PlaceContext::*;
595         match context {
596             // Constants must have at most one write
597             // FIXME(oli-obk): we could be more powerful here, if the multiple writes
598             // only occur in independent execution paths
599             MutatingUse(MutatingUseContext::Store) => if self.found_assignment[local] {
600                 self.can_const_prop[local] = false;
601             } else {
602                 self.found_assignment[local] = true
603             },
604             // Reading constants is allowed an arbitrary number of times
605             NonMutatingUse(NonMutatingUseContext::Copy) |
606             NonMutatingUse(NonMutatingUseContext::Move) |
607             NonMutatingUse(NonMutatingUseContext::Inspect) |
608             NonMutatingUse(NonMutatingUseContext::Projection) |
609             MutatingUse(MutatingUseContext::Projection) |
610             NonUse(_) => {},
611             _ => self.can_const_prop[local] = false,
612         }
613     }
614 }
615
616 impl<'b, 'a, 'tcx> MutVisitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> {
617     fn visit_constant(
618         &mut self,
619         constant: &mut Constant<'tcx>,
620         location: Location,
621     ) {
622         trace!("visit_constant: {:?}", constant);
623         self.super_constant(constant, location);
624         self.eval_constant(constant);
625     }
626
627     fn visit_statement(
628         &mut self,
629         statement: &mut Statement<'tcx>,
630         location: Location,
631     ) {
632         trace!("visit_statement: {:?}", statement);
633         if let StatementKind::Assign(ref place, ref mut rval) = statement.kind {
634             let place_ty: Ty<'tcx> = place
635                 .ty(&self.local_decls, self.tcx)
636                 .ty;
637             if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
638                 if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) {
639                     if let Place::Base(PlaceBase::Local(local)) = *place {
640                         trace!("checking whether {:?} can be stored to {:?}", value, local);
641                         if self.can_const_prop[local] {
642                             trace!("storing {:?} to {:?}", value, local);
643                             assert!(self.places[local].is_none());
644                             self.places[local] = Some(value);
645
646                             if self.should_const_prop() {
647                                 self.replace_with_const(rval, value, statement.source_info.span);
648                             }
649                         }
650                     }
651                 }
652             }
653         }
654         self.super_statement(statement, location);
655     }
656
657     fn visit_terminator(
658         &mut self,
659         terminator: &mut Terminator<'tcx>,
660         location: Location,
661     ) {
662         self.super_terminator(terminator, location);
663         let source_info = terminator.source_info;
664         match &mut terminator.kind {
665             TerminatorKind::Assert { expected, msg, ref mut cond, .. } => {
666                 if let Some(value) = self.eval_operand(&cond, source_info) {
667                     trace!("assertion on {:?} should be {:?}", value, expected);
668                     let expected = ScalarMaybeUndef::from(Scalar::from_bool(*expected));
669                     let value_const = self.ecx.read_scalar(value).unwrap();
670                     if expected != value_const {
671                         // poison all places this operand references so that further code
672                         // doesn't use the invalid value
673                         match cond {
674                             Operand::Move(ref place) | Operand::Copy(ref place) => {
675                                 let mut place = place;
676                                 while let Place::Projection(ref proj) = *place {
677                                     place = &proj.base;
678                                 }
679                                 if let Place::Base(PlaceBase::Local(local)) = *place {
680                                     self.places[local] = None;
681                                 }
682                             },
683                             Operand::Constant(_) => {}
684                         }
685                         let span = terminator.source_info.span;
686                         let hir_id = self
687                             .tcx
688                             .hir()
689                             .as_local_hir_id(self.source.def_id())
690                             .expect("some part of a failing const eval must be local");
691                         use rustc::mir::interpret::InterpError::*;
692                         let msg = match msg {
693                             Overflow(_) |
694                             OverflowNeg |
695                             DivisionByZero |
696                             RemainderByZero => msg.description().to_owned(),
697                             BoundsCheck { ref len, ref index } => {
698                                 let len = self
699                                     .eval_operand(len, source_info)
700                                     .expect("len must be const");
701                                 let len = match self.ecx.read_scalar(len) {
702                                     Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
703                                         bits, ..
704                                     })) => bits,
705                                     other => bug!("const len not primitive: {:?}", other),
706                                 };
707                                 let index = self
708                                     .eval_operand(index, source_info)
709                                     .expect("index must be const");
710                                 let index = match self.ecx.read_scalar(index) {
711                                     Ok(ScalarMaybeUndef::Scalar(Scalar::Bits {
712                                         bits, ..
713                                     })) => bits,
714                                     other => bug!("const index not primitive: {:?}", other),
715                                 };
716                                 format!(
717                                     "index out of bounds: \
718                                     the len is {} but the index is {}",
719                                     len,
720                                     index,
721                                 )
722                             },
723                             // Need proper const propagator for these
724                             _ => return,
725                         };
726                         self.tcx.lint_hir(
727                             ::rustc::lint::builtin::CONST_ERR,
728                             hir_id,
729                             span,
730                             &msg,
731                         );
732                     } else {
733                         if self.should_const_prop() {
734                             if let ScalarMaybeUndef::Scalar(scalar) = value_const {
735                                 *cond = self.operand_from_scalar(
736                                     scalar,
737                                     self.tcx.types.bool,
738                                     source_info.span,
739                                 );
740                             }
741                         }
742                     }
743                 }
744             },
745             TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => {
746                 if self.should_const_prop() {
747                     if let Some(value) = self.eval_operand(&discr, source_info) {
748                         if let ScalarMaybeUndef::Scalar(scalar) =
749                                 self.ecx.read_scalar(value).unwrap() {
750                             *discr = self.operand_from_scalar(scalar, switch_ty, source_info.span);
751                         }
752                     }
753                 }
754             },
755             //none of these have Operands to const-propagate
756             TerminatorKind::Goto { .. } |
757             TerminatorKind::Resume |
758             TerminatorKind::Abort |
759             TerminatorKind::Return |
760             TerminatorKind::Unreachable |
761             TerminatorKind::Drop { .. } |
762             TerminatorKind::DropAndReplace { .. } |
763             TerminatorKind::Yield { .. } |
764             TerminatorKind::GeneratorDrop |
765             TerminatorKind::FalseEdges { .. } |
766             TerminatorKind::FalseUnwind { .. } => { }
767             //FIXME(wesleywiser) Call does have Operands that could be const-propagated
768             TerminatorKind::Call { .. } => { }
769         }
770     }
771 }