1 //! Propagates constants for early reporting of statically known
7 use rustc::hir::def::DefKind;
8 use rustc::hir::def_id::DefId;
10 AggregateKind, Constant, Location, Place, PlaceBase, Body, BodyCache, Operand, Local, UnOp,
11 Rvalue. StatementKind, Statement, LocalKind, TerminatorKind, Terminator, ClearCrossCrate,
12 SourceInfo, BinOp, SourceScope, SourceScopeData, LocalDecl, BasicBlock, ReadOnlyBodyCache,
15 use rustc::mir::visit::{
16 Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext,
18 use rustc::mir::interpret::{Scalar, InterpResult, PanicInfo};
19 use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
20 use syntax::ast::Mutability;
21 use syntax_pos::{Span, DUMMY_SP};
22 use rustc::ty::subst::InternalSubsts;
23 use rustc_data_structures::fx::FxHashMap;
24 use rustc_index::vec::IndexVec;
25 use rustc::ty::layout::{
26 LayoutOf, TyLayout, LayoutError, HasTyCtxt, TargetDataLayout, HasDataLayout, Size,
29 use crate::rustc::ty::subst::Subst;
30 use crate::interpret::{
31 self, InterpCx, ScalarMaybeUndef, Immediate, OpTy,
32 StackPopCleanup, LocalValue, LocalState, AllocId, Frame,
33 Allocation, MemoryKind, ImmTy, Pointer, Memory, PlaceTy,
34 Operand as InterpOperand, intern_const_alloc_recursive,
36 use crate::const_eval::error_to_const_error;
37 use crate::transform::{MirPass, MirSource};
39 /// The maximum number of bytes that we'll allocate space for a return value.
40 const MAX_ALLOC_LIMIT: u64 = 1024;
44 impl<'tcx> MirPass<'tcx> for ConstProp {
45 fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
46 // will be evaluated by miri and produce its errors there
47 if source.promoted.is_some() {
51 use rustc::hir::map::blocks::FnLikeNode;
52 let hir_id = tcx.hir().as_local_hir_id(source.def_id())
53 .expect("Non-local call to local provider is_const_fn");
55 let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some();
56 let is_assoc_const = match tcx.def_kind(source.def_id()) {
57 Some(DefKind::AssocConst) => true,
61 // Only run const prop on functions, methods, closures and associated constants
62 if !is_fn_like && !is_assoc_const {
63 // skip anon_const/statics/consts because they'll be evaluated by miri anyway
64 trace!("ConstProp skipped for {:?}", source.def_id());
68 let is_generator = tcx.type_of(source.def_id()).is_generator();
69 // FIXME(welseywiser) const prop doesn't work on generators because of query cycles
70 // computing their layout.
72 trace!("ConstProp skipped for generator {:?}", source.def_id());
76 trace!("ConstProp starting for {:?}", source.def_id());
80 body_cache.basic_blocks().clone(),
81 body_cache.source_scopes.clone(),
82 body_cache.local_decls.clone(),
86 tcx.def_span(source.def_id()),
88 body_cache.generator_kind,
91 // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
92 // constants, instead of just checking for const-folding succeeding.
93 // That would require an uniform one-def no-mutation analysis
94 // and RPO (or recursing when needing the value of a local).
95 let mut optimization_finder = ConstPropagator::new(
96 body_cache.read_only(),
101 optimization_finder.visit_body(body_cache);
103 trace!("ConstProp done for {:?}", source.def_id());
107 struct ConstPropMachine;
109 impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
110 type MemoryKinds = !;
111 type PointerTag = ();
114 type FrameExtra = ();
115 type MemoryExtra = ();
116 type AllocExtra = ();
118 type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
120 const STATIC_KIND: Option<!> = None;
122 const CHECK_ALIGN: bool = false;
125 fn enforce_validity(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
130 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
131 _instance: ty::Instance<'tcx>,
132 _args: &[OpTy<'tcx>],
133 _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
134 _unwind: Option<BasicBlock>,
135 ) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
140 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
142 _args: &[OpTy<'tcx>],
143 _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
144 _unwind: Option<BasicBlock>
145 ) -> InterpResult<'tcx> {
150 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
152 _instance: ty::Instance<'tcx>,
153 _args: &[OpTy<'tcx>],
154 _ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
155 _unwind: Option<BasicBlock>
156 ) -> InterpResult<'tcx> {
157 throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"));
161 _mem: &Memory<'mir, 'tcx, Self>,
163 ) -> InterpResult<'tcx, u64> {
164 throw_unsup!(ConstPropUnsupported("ptr-to-int casts aren't supported in ConstProp"));
168 _ecx: &InterpCx<'mir, 'tcx, Self>,
172 ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
173 // We can't do this because aliasing of memory can differ between const eval and llvm
174 throw_unsup!(ConstPropUnsupported("pointer arithmetic or comparisons aren't supported \
178 fn find_foreign_static(
181 ) -> InterpResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>> {
182 throw_unsup!(ReadForeignStatic)
186 fn tag_allocation<'b>(
189 alloc: Cow<'b, Allocation>,
190 _kind: Option<MemoryKind<!>>,
191 ) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
192 // We do not use a tag so we can just cheaply forward the allocation
197 fn tag_static_base_pointer(
200 ) -> Self::PointerTag {
205 _ecx: &mut InterpCx<'mir, 'tcx, Self>,
206 _dest: PlaceTy<'tcx>,
207 ) -> InterpResult<'tcx> {
208 throw_unsup!(ConstPropUnsupported("can't const prop `box` keyword"));
212 _ecx: &InterpCx<'mir, 'tcx, Self>,
213 frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
215 ) -> InterpResult<'tcx, InterpOperand<Self::PointerTag>> {
216 let l = &frame.locals[local];
218 if l.value == LocalValue::Uninitialized {
219 throw_unsup!(ConstPropUnsupported("tried to access an uninitialized local"));
225 fn before_access_static(
226 allocation: &Allocation<Self::PointerTag, Self::AllocExtra>,
227 ) -> InterpResult<'tcx> {
228 // if the static allocation is mutable or if it has relocations (it may be legal to mutate
229 // the memory behind that in the future), then we can't const prop it
230 if allocation.mutability == Mutability::Mutable || allocation.relocations().len() > 0 {
231 throw_unsup!(ConstPropUnsupported("can't eval mutable statics in ConstProp"));
237 fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
242 fn stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
247 type Const<'tcx> = OpTy<'tcx>;
249 /// Finds optimization opportunities on the MIR.
250 struct ConstPropagator<'mir, 'tcx> {
251 ecx: InterpCx<'mir, 'tcx, ConstPropMachine>,
253 source: MirSource<'tcx>,
254 can_const_prop: IndexVec<Local, bool>,
255 param_env: ParamEnv<'tcx>,
256 // FIXME(eddyb) avoid cloning these two fields more than once,
257 // by accessing them through `ecx` instead.
258 source_scopes: IndexVec<SourceScope, SourceScopeData>,
259 local_decls: IndexVec<Local, LocalDecl<'tcx>>,
260 ret: Option<OpTy<'tcx, ()>>,
263 impl<'mir, 'tcx> LayoutOf for ConstPropagator<'mir, 'tcx> {
265 type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
267 fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
268 self.tcx.layout_of(self.param_env.and(ty))
272 impl<'mir, 'tcx> HasDataLayout for ConstPropagator<'mir, 'tcx> {
274 fn data_layout(&self) -> &TargetDataLayout {
275 &self.tcx.data_layout
279 impl<'mir, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
281 fn tcx(&self) -> TyCtxt<'tcx> {
286 impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
288 body_cache: ReadOnlyBodyCache<'mir, 'tcx>,
289 dummy_body: &'mir Body<'tcx>,
291 source: MirSource<'tcx>,
292 ) -> ConstPropagator<'mir, 'tcx> {
293 let def_id = source.def_id();
294 let param_env = tcx.param_env(def_id);
295 let span = tcx.def_span(def_id);
296 let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ());
297 let can_const_prop = CanConstProp::check(body_cache);
299 let substs = &InternalSubsts::identity_for_item(tcx, def_id);
303 .layout_of(body.return_ty().subst(tcx, substs))
305 // Don't bother allocating memory for ZST types which have no values
306 // or for large values.
307 .filter(|ret_layout| !ret_layout.is_zst() &&
308 ret_layout.size < Size::from_bytes(MAX_ALLOC_LIMIT))
309 .map(|ret_layout| ecx.allocate(ret_layout, MemoryKind::Stack));
311 ecx.push_stack_frame(
312 Instance::new(def_id, substs),
316 StackPopCleanup::None {
319 ).expect("failed to push initial stack frame");
327 // FIXME(eddyb) avoid cloning these two fields more than once,
328 // by accessing them through `ecx` instead.
329 source_scopes: body_cache.source_scopes.clone(),
330 //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
331 local_decls: body_cache.local_decls.clone(),
332 ret: ret.map(Into::into),
336 fn get_const(&self, local: Local) -> Option<Const<'tcx>> {
337 if local == RETURN_PLACE {
338 // Try to read the return place as an immediate so that if it is representable as a
339 // scalar, we can handle it as such, but otherwise, just return the value as is.
340 return match self.ret.map(|ret| self.ecx.try_read_immediate(ret)) {
341 Some(Ok(Ok(imm))) => Some(imm.into()),
346 self.ecx.access_local(self.ecx.frame(), local, None).ok()
349 fn remove_const(&mut self, local: Local) {
350 self.ecx.frame_mut().locals[local] = LocalState {
351 value: LocalValue::Uninitialized,
352 layout: Cell::new(None),
358 source_info: SourceInfo,
362 F: FnOnce(&mut Self) -> InterpResult<'tcx, T>,
364 self.ecx.tcx.span = source_info.span;
365 // FIXME(eddyb) move this to the `Panic(_)` error case, so that
366 // `f(self)` is always called, and that the only difference when the
367 // scope's `local_data` is missing, is that the lint isn't emitted.
368 let lint_root = match &self.source_scopes[source_info.scope].local_data {
369 ClearCrossCrate::Set(data) => data.lint_root,
370 ClearCrossCrate::Clear => return None,
372 let r = match f(self) {
373 Ok(val) => Some(val),
375 use rustc::mir::interpret::{
377 UndefinedBehaviorInfo,
381 MachineStop(_) => bug!("ConstProp does not stop"),
383 // Some error shouldn't come up because creating them causes
384 // an allocation, which we should avoid. When that happens,
385 // dedicated error variants should be introduced instead.
386 // Only test this in debug builds though to avoid disruptions.
387 Unsupported(UnsupportedOpInfo::Unsupported(_))
388 | Unsupported(UnsupportedOpInfo::ValidationFailure(_))
389 | UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
390 | UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_))
391 if cfg!(debug_assertions) => {
392 bug!("const-prop encountered allocating error: {:?}", error.kind);
396 | UndefinedBehavior(_)
398 | ResourceExhaustion(_) => {
399 // Ignore these errors.
402 let diagnostic = error_to_const_error(&self.ecx, error);
403 diagnostic.report_as_lint(
405 "this expression will panic at runtime",
414 self.ecx.tcx.span = DUMMY_SP;
421 ) -> Option<Const<'tcx>> {
422 self.ecx.tcx.span = c.span;
423 match self.ecx.eval_const_to_op(c.literal, None) {
428 let err = error_to_const_error(&self.ecx, error);
429 err.report_as_error(self.ecx.tcx, "erroneous constant used");
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)
442 fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Option<Const<'tcx>> {
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),
452 rvalue: &Rvalue<'tcx>,
453 place_layout: TyLayout<'tcx>,
454 source_info: SourceInfo,
457 let span = source_info.span;
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) {
464 let overflow_check = self.tcx.sess.overflow_checks();
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.
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);
480 self.use_ecx(source_info, |this| {
481 let ty = arg.ty(&this.local_decls, this.tcx);
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)
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);
502 let r = self.use_ecx(source_info, |this| {
503 this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)
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 lint_root = match &self.source_scopes[source_info.scope].local_data {
511 ClearCrossCrate::Set(data) => data.lint_root,
512 ClearCrossCrate::Clear => return None,
514 let dir = if *op == BinOp::Shr {
520 ::rustc::lint::builtin::EXCEEDING_BITSHIFTS,
523 &format!("attempt to shift {} with overflow", dir));
528 // If overflow checking is enabled (like in debug mode by default),
529 // then we'll already catch overflow when we evaluate the `Assert` statement
530 // in MIR. However, if overflow checking is disabled, then there won't be any
531 // `Assert` statement and so we have to do additional checking here.
533 self.use_ecx(source_info, |this| {
534 let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
535 let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
538 let err = err_panic!(Overflow(*op)).into();
547 // Work around: avoid ICE in miri. FIXME(wesleywiser)
548 // The Miri engine ICEs when taking a reference to an uninitialized unsized
549 // local. There's nothing it can do here: taking a reference needs an allocation
550 // which needs to know the size. Normally that's okay as during execution
551 // (e.g. for CTFE) it can never happen. But here in const_prop
552 // unknown data is uninitialized, so if e.g. a function argument is unsized
553 // and has a reference taken, we get an ICE.
554 Rvalue::Ref(_, _, place_ref) => {
555 trace!("checking Ref({:?})", place_ref);
557 if let Some(local) = place_ref.as_local() {
559 if let LocalValue::Live(_) = self.ecx.frame().locals[local].value {
566 trace!("skipping Ref({:?}) to uninitialized local", place);
575 self.use_ecx(source_info, |this| {
576 trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place);
577 this.ecx.eval_rvalue_into_place(rvalue, place)?;
582 fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
583 Operand::Constant(Box::new(
587 literal: self.tcx.mk_const(*ty::Const::from_scalar(
596 fn replace_with_const(
598 rval: &mut Rvalue<'tcx>,
600 source_info: SourceInfo,
602 trace!("attepting to replace {:?} with {:?}", rval, value);
603 if let Err(e) = self.ecx.validate_operand(
606 // FIXME: is ref tracking too expensive?
607 Some(&mut interpret::RefTracking::empty()),
609 trace!("validation error, attempt failed: {:?}", e);
613 // FIXME> figure out what tho do when try_read_immediate fails
614 let imm = self.use_ecx(source_info, |this| {
615 this.ecx.try_read_immediate(value)
618 if let Some(Ok(imm)) = imm {
620 interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => {
622 self.operand_from_scalar(scalar, value.layout.ty, source_info.span));
624 Immediate::ScalarPair(
625 ScalarMaybeUndef::Scalar(one),
626 ScalarMaybeUndef::Scalar(two)
628 let ty = &value.layout.ty.kind;
629 if let ty::Tuple(substs) = ty {
630 *rval = Rvalue::Aggregate(
631 Box::new(AggregateKind::Tuple),
633 self.operand_from_scalar(
634 one, substs[0].expect_ty(), source_info.span
636 self.operand_from_scalar(
637 two, substs[1].expect_ty(), source_info.span
648 fn should_const_prop(&mut self, op: OpTy<'tcx>) -> bool {
649 let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level;
651 if mir_opt_level == 0 {
656 interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUndef::Scalar(s))) =>
658 interpret::Operand::Immediate(Immediate::ScalarPair(ScalarMaybeUndef::Scalar(l),
659 ScalarMaybeUndef::Scalar(r))) =>
660 l.is_bits() && r.is_bits(),
661 interpret::Operand::Indirect(_) if mir_opt_level >= 2 => {
662 intern_const_alloc_recursive(
665 op.assert_mem_place()
666 ).expect("failed to intern alloc");
674 struct CanConstProp {
675 can_const_prop: IndexVec<Local, bool>,
676 // false at the beginning, once set, there are not allowed to be any more assignments
677 found_assignment: IndexVec<Local, bool>,
681 /// returns true if `local` can be propagated
682 fn check(body_cache: ReadOnlyBodyCache<'_, '_>) -> IndexVec<Local, bool> {
683 let mut cpv = CanConstProp {
684 can_const_prop: IndexVec::from_elem(true, &body_cache.local_decls),
685 found_assignment: IndexVec::from_elem(false, &body_cache.local_decls),
687 for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
688 // cannot use args at all
689 // cannot use locals because if x < y { y - x } else { x - y } would
691 // FIXME(oli-obk): lint variables until they are used in a condition
692 // FIXME(oli-obk): lint if return value is constant
693 let local_kind = body_cache.local_kind(local);
694 *val = local_kind == LocalKind::Temp || local_kind == LocalKind::ReturnPointer;
697 trace!("local {:?} can't be propagated because it's not a temporary", local);
700 cpv.visit_body(body_cache);
705 impl<'tcx> Visitor<'tcx> for CanConstProp {
709 context: PlaceContext,
712 use rustc::mir::visit::PlaceContext::*;
714 // Constants must have at most one write
715 // FIXME(oli-obk): we could be more powerful here, if the multiple writes
716 // only occur in independent execution paths
717 MutatingUse(MutatingUseContext::Store) => if self.found_assignment[local] {
718 trace!("local {:?} can't be propagated because of multiple assignments", local);
719 self.can_const_prop[local] = false;
721 self.found_assignment[local] = true
723 // Reading constants is allowed an arbitrary number of times
724 NonMutatingUse(NonMutatingUseContext::Copy) |
725 NonMutatingUse(NonMutatingUseContext::Move) |
726 NonMutatingUse(NonMutatingUseContext::Inspect) |
727 NonMutatingUse(NonMutatingUseContext::Projection) |
728 MutatingUse(MutatingUseContext::Projection) |
731 trace!("local {:?} can't be propagaged because it's used: {:?}", local, context);
732 self.can_const_prop[local] = false;
738 impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
739 fn tcx(&self) -> TyCtxt<'tcx> {
745 constant: &mut Constant<'tcx>,
748 trace!("visit_constant: {:?}", constant);
749 self.super_constant(constant, location);
750 self.eval_constant(constant);
755 statement: &mut Statement<'tcx>,
758 trace!("visit_statement: {:?}", statement);
759 if let StatementKind::Assign(box(ref place, ref mut rval)) = statement.kind {
760 let place_ty: Ty<'tcx> = place
761 .ty(&self.local_decls, self.tcx)
763 if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
764 if let Some(local) = place.as_local() {
765 let source = statement.source_info;
766 if let Some(()) = self.const_prop(rval, place_layout, source, place) {
767 if self.can_const_prop[local] {
768 trace!("propagated into {:?}", local);
770 if let Some(value) = self.get_const(local) {
771 if self.should_const_prop(value) {
772 trace!("replacing {:?} with {:?}", rval, value);
773 self.replace_with_const(
776 statement.source_info,
781 trace!("can't propagate into {:?}", local);
782 if local != RETURN_PLACE {
783 self.remove_const(local);
790 match statement.kind {
791 StatementKind::StorageLive(local) |
792 StatementKind::StorageDead(local) if self.can_const_prop[local] => {
793 let frame = self.ecx.frame_mut();
794 frame.locals[local].value =
795 if let StatementKind::StorageLive(_) = statement.kind {
796 LocalValue::Uninitialized
805 self.super_statement(statement, location);
810 terminator: &mut Terminator<'tcx>,
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
825 Operand::Move(ref place) | Operand::Copy(ref place) => {
826 if let PlaceBase::Local(local) = place.base {
827 self.remove_const(local);
830 Operand::Constant(_) => {}
832 let span = terminator.source_info.span;
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 =>
843 msg.description().to_owned(),
844 PanicInfo::BoundsCheck { ref len, ref index } => {
846 .eval_operand(len, source_info)
847 .expect("len must be const");
848 let len = match self.ecx.read_scalar(len) {
849 Ok(ScalarMaybeUndef::Scalar(Scalar::Raw {
852 other => bug!("const len not primitive: {:?}", other),
855 .eval_operand(index, source_info)
856 .expect("index must be const");
857 let index = match self.ecx.read_scalar(index) {
858 Ok(ScalarMaybeUndef::Scalar(Scalar::Raw {
861 other => bug!("const index not primitive: {:?}", other),
864 "index out of bounds: \
865 the len is {} but the index is {}",
870 // Need proper const propagator for these
874 ::rustc::lint::builtin::CONST_ERR,
880 if self.should_const_prop(value) {
881 if let ScalarMaybeUndef::Scalar(scalar) = value_const {
882 *cond = self.operand_from_scalar(
892 TerminatorKind::SwitchInt { ref mut discr, switch_ty, .. } => {
893 if let Some(value) = self.eval_operand(&discr, source_info) {
894 if self.should_const_prop(value) {
895 if let ScalarMaybeUndef::Scalar(scalar) =
896 self.ecx.read_scalar(value).unwrap() {
897 *discr = self.operand_from_scalar(scalar, switch_ty, source_info.span);
902 //none of these have Operands to const-propagate
903 TerminatorKind::Goto { .. } |
904 TerminatorKind::Resume |
905 TerminatorKind::Abort |
906 TerminatorKind::Return |
907 TerminatorKind::Unreachable |
908 TerminatorKind::Drop { .. } |
909 TerminatorKind::DropAndReplace { .. } |
910 TerminatorKind::Yield { .. } |
911 TerminatorKind::GeneratorDrop |
912 TerminatorKind::FalseEdges { .. } |
913 TerminatorKind::FalseUnwind { .. } => { }
914 //FIXME(wesleywiser) Call does have Operands that could be const-propagated
915 TerminatorKind::Call { .. } => { }