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