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