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