]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/qualify_consts.rs
Fix BTreeMap example typo
[rust.git] / src / librustc_mir / transform / qualify_consts.rs
1 // Copyright 2016 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 //! A pass that qualifies constness of temporaries in constants,
12 //! static initializers and functions and also drives promotion.
13 //!
14 //! The Qualif flags below can be used to also provide better
15 //! diagnostics as to why a constant rvalue wasn't promoted.
16
17 use rustc_data_structures::bitvec::BitVector;
18 use rustc::hir;
19 use rustc::hir::def_id::DefId;
20 use rustc::hir::intravisit::FnKind;
21 use rustc::hir::map::blocks::FnLikeNode;
22 use rustc::traits::{self, ProjectionMode};
23 use rustc::ty::{self, TyCtxt, Ty};
24 use rustc::ty::cast::CastTy;
25 use rustc::mir::repr::*;
26 use rustc::mir::mir_map::MirMap;
27 use rustc::mir::transform::{Pass, MirMapPass, MirSource};
28 use rustc::mir::visit::{LvalueContext, Visitor};
29 use rustc::util::nodemap::DefIdMap;
30 use syntax::abi::Abi;
31 use syntax::codemap::Span;
32 use syntax::feature_gate::UnstableFeatures;
33
34 use std::collections::hash_map::Entry;
35 use std::fmt;
36
37 use build::Location;
38 use traversal::{self, ReversePostorder};
39
40 use super::promote_consts::{self, Candidate, TempState};
41
42 bitflags! {
43     flags Qualif: u8 {
44         // Const item's qualification while recursing.
45         // Recursive consts are an error.
46         const RECURSIVE         = 1 << 0,
47
48         // Constant containing interior mutability (UnsafeCell).
49         const MUTABLE_INTERIOR  = 1 << 1,
50
51         // Constant containing an ADT that implements Drop.
52         const NEEDS_DROP        = 1 << 2,
53
54         // Function argument.
55         const FN_ARGUMENT       = 1 << 3,
56
57         // Static lvalue or move from a static.
58         const STATIC            = 1 << 4,
59
60         // Reference to a static.
61         const STATIC_REF        = 1 << 5,
62
63         // Not constant at all - non-`const fn` calls, asm!,
64         // pointer comparisons, ptr-to-int casts, etc.
65         const NOT_CONST         = 1 << 6,
66
67         // Refers to temporaries which cannot be promoted as
68         // promote_consts decided they weren't simple enough.
69         const NOT_PROMOTABLE    = 1 << 7,
70
71         // Borrows of temporaries can be promoted only
72         // if they have none of the above qualifications.
73         const NEVER_PROMOTE     = !0,
74
75         // Const items can only have MUTABLE_INTERIOR
76         // and NOT_PROMOTABLE without producing an error.
77         const CONST_ERROR       = !Qualif::MUTABLE_INTERIOR.bits &
78                                   !Qualif::NOT_PROMOTABLE.bits
79     }
80 }
81
82 impl<'a, 'tcx> Qualif {
83     /// Remove flags which are impossible for the given type.
84     fn restrict(&mut self, ty: Ty<'tcx>,
85                 tcx: TyCtxt<'a, 'tcx, 'tcx>,
86                 param_env: &ty::ParameterEnvironment<'tcx>) {
87         if !ty.type_contents(tcx).interior_unsafe() {
88             *self = *self - Qualif::MUTABLE_INTERIOR;
89         }
90         if !tcx.type_needs_drop_given_env(ty, param_env) {
91             *self = *self - Qualif::NEEDS_DROP;
92         }
93     }
94 }
95
96 /// What kind of item we are in.
97 #[derive(Copy, Clone, PartialEq, Eq)]
98 enum Mode {
99     Const,
100     Static,
101     StaticMut,
102     ConstFn,
103     Fn
104 }
105
106 impl fmt::Display for Mode {
107     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108         match *self {
109             Mode::Const => write!(f, "constant"),
110             Mode::Static | Mode::StaticMut => write!(f, "static"),
111             Mode::ConstFn => write!(f, "constant function"),
112             Mode::Fn => write!(f, "function")
113         }
114     }
115 }
116
117 fn is_const_fn(tcx: TyCtxt, def_id: DefId) -> bool {
118     if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
119         let fn_like = FnLikeNode::from_node(tcx.map.get(node_id));
120         match fn_like.map(|f| f.kind()) {
121             Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => {
122                 c == hir::Constness::Const
123             }
124             Some(FnKind::Method(_, m, _, _)) => {
125                 m.constness == hir::Constness::Const
126             }
127             _ => false
128         }
129     } else {
130         tcx.sess.cstore.is_const_fn(def_id)
131     }
132 }
133
134 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
135     mode: Mode,
136     span: Span,
137     def_id: DefId,
138     mir: &'a Mir<'tcx>,
139     rpo: ReversePostorder<'a, 'tcx>,
140     tcx: TyCtxt<'a, 'gcx, 'tcx>,
141     param_env: ty::ParameterEnvironment<'tcx>,
142     qualif_map: &'a mut DefIdMap<Qualif>,
143     mir_map: Option<&'a MirMap<'tcx>>,
144     temp_qualif: Vec<Option<Qualif>>,
145     return_qualif: Option<Qualif>,
146     qualif: Qualif,
147     const_fn_arg_vars: BitVector,
148     location: Location,
149     temp_promotion_state: Vec<TempState>,
150     promotion_candidates: Vec<Candidate>
151 }
152
153 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
154     fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
155            param_env: ty::ParameterEnvironment<'tcx>,
156            qualif_map: &'a mut DefIdMap<Qualif>,
157            mir_map: Option<&'a MirMap<'tcx>>,
158            def_id: DefId,
159            mir: &'a Mir<'tcx>,
160            mode: Mode)
161            -> Qualifier<'a, 'tcx, 'tcx> {
162         let mut rpo = traversal::reverse_postorder(mir);
163         let temps = promote_consts::collect_temps(mir, &mut rpo);
164         rpo.reset();
165         Qualifier {
166             mode: mode,
167             span: mir.span,
168             def_id: def_id,
169             mir: mir,
170             rpo: rpo,
171             tcx: tcx,
172             param_env: param_env,
173             qualif_map: qualif_map,
174             mir_map: mir_map,
175             temp_qualif: vec![None; mir.temp_decls.len()],
176             return_qualif: None,
177             qualif: Qualif::empty(),
178             const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
179             location: Location {
180                 block: START_BLOCK,
181                 statement_index: 0
182             },
183             temp_promotion_state: temps,
184             promotion_candidates: vec![]
185         }
186     }
187
188     // FIXME(eddyb) we could split the errors into meaningful
189     // categories, but enabling full miri would make that
190     // slightly pointless (even with feature-gating).
191     fn not_const(&mut self) {
192         self.add(Qualif::NOT_CONST);
193         if self.mode != Mode::Fn {
194             span_err!(self.tcx.sess, self.span, E0019,
195                       "{} contains unimplemented expression type", self.mode);
196         }
197     }
198
199     /// Error about extra statements in a constant.
200     fn statement_like(&mut self) {
201         self.add(Qualif::NOT_CONST);
202         if self.mode != Mode::Fn {
203             span_err!(self.tcx.sess, self.span, E0016,
204                       "blocks in {}s are limited to items and tail expressions",
205                       self.mode);
206         }
207     }
208
209     /// Add the given qualification to self.qualif.
210     fn add(&mut self, qualif: Qualif) {
211         self.qualif = self.qualif | qualif;
212     }
213
214     /// Add the given type's qualification to self.qualif.
215     fn add_type(&mut self, ty: Ty<'tcx>) {
216         self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
217         self.qualif.restrict(ty, self.tcx, &self.param_env);
218     }
219
220     /// Within the provided closure, self.qualif will start
221     /// out empty, and its value after the closure returns will
222     /// be combined with the value before the call to nest.
223     fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
224         let original = self.qualif;
225         self.qualif = Qualif::empty();
226         f(self);
227         self.add(original);
228     }
229
230     /// Check for NEEDS_DROP (from an ADT or const fn call) and
231     /// error, unless we're in a function, or the feature-gate
232     /// for globals with destructors is enabled.
233     fn deny_drop(&self) {
234         if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) {
235             return;
236         }
237
238         // Static and const fn's allow destructors, but they're feature-gated.
239         let msg = if self.mode != Mode::Const {
240             // Feature-gate for globals with destructors is enabled.
241             if self.tcx.sess.features.borrow().drop_types_in_const {
242                 return;
243             }
244
245             // This comes from a macro that has #[allow_internal_unstable].
246             if self.tcx.sess.codemap().span_allows_unstable(self.span) {
247                 return;
248             }
249
250             format!("destructors in {}s are an unstable feature",
251                     self.mode)
252         } else {
253             format!("{}s are not allowed to have destructors",
254                     self.mode)
255         };
256
257         let mut err =
258             struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg);
259         if self.mode != Mode::Const {
260             help!(&mut err,
261                   "in Nightly builds, add `#![feature(drop_types_in_const)]` \
262                    to the crate attributes to enable");
263         }
264         err.emit();
265     }
266
267     /// Check if an Lvalue with the current qualifications could
268     /// be consumed, by either an operand or a Deref projection.
269     fn try_consume(&mut self) -> bool {
270         if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
271             let msg = if self.mode == Mode::Static ||
272                          self.mode == Mode::StaticMut {
273                 "cannot refer to other statics by value, use the \
274                  address-of operator or a constant instead"
275             } else {
276                 "cannot refer to statics by value, use a constant instead"
277             };
278             span_err!(self.tcx.sess, self.span, E0394, "{}", msg);
279
280             // Replace STATIC with NOT_CONST to avoid further errors.
281             self.qualif = self.qualif - Qualif::STATIC;
282             self.add(Qualif::NOT_CONST);
283
284             false
285         } else {
286             true
287         }
288     }
289
290     /// Assign the current qualification to the given destination.
291     fn assign(&mut self, dest: &Lvalue<'tcx>) {
292         let qualif = self.qualif;
293         let span = self.span;
294         let store = |slot: &mut Option<Qualif>| {
295             if slot.is_some() {
296                 span_bug!(span, "multiple assignments to {:?}", dest);
297             }
298             *slot = Some(qualif);
299         };
300
301         // Only handle promotable temps in non-const functions.
302         if self.mode == Mode::Fn {
303             if let Lvalue::Temp(index) = *dest {
304                 if self.temp_promotion_state[index as usize].is_promotable() {
305                     store(&mut self.temp_qualif[index as usize]);
306                 }
307             }
308             return;
309         }
310
311         match *dest {
312             Lvalue::Temp(index) => store(&mut self.temp_qualif[index as usize]),
313             Lvalue::ReturnPointer => store(&mut self.return_qualif),
314
315             Lvalue::Projection(box Projection {
316                 base: Lvalue::Temp(index),
317                 elem: ProjectionElem::Deref
318             }) if self.mir.temp_decls[index as usize].ty.is_unique()
319                && self.temp_qualif[index as usize].map_or(false, |qualif| {
320                     qualif.intersects(Qualif::NOT_CONST)
321                }) => {
322                 // Part of `box expr`, we should've errored
323                 // already for the Box allocation Rvalue.
324             }
325
326             // This must be an explicit assignment.
327             _ => {
328                 // Catch more errors in the destination.
329                 self.visit_lvalue(dest, LvalueContext::Store);
330                 self.statement_like();
331             }
332         }
333     }
334
335     /// Qualify a whole const, static initializer or const fn.
336     fn qualify_const(&mut self) -> Qualif {
337         let mir = self.mir;
338
339         let mut seen_blocks = BitVector::new(mir.basic_blocks.len());
340         let mut bb = START_BLOCK;
341         loop {
342             seen_blocks.insert(bb.index());
343
344             self.visit_basic_block_data(bb, &mir[bb]);
345
346             let target = match mir[bb].terminator().kind {
347                 TerminatorKind::Goto { target } |
348                 // Drops are considered noops.
349                 TerminatorKind::Drop { target, .. } |
350                 TerminatorKind::Assert { target, .. } |
351                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
352                     Some(target)
353                 }
354
355                 // Non-terminating calls cannot produce any value.
356                 TerminatorKind::Call { destination: None, .. } => {
357                     return Qualif::empty();
358                 }
359
360                 TerminatorKind::If {..} |
361                 TerminatorKind::Switch {..} |
362                 TerminatorKind::SwitchInt {..} |
363                 TerminatorKind::DropAndReplace { .. } |
364                 TerminatorKind::Resume => None,
365
366                 TerminatorKind::Return => {
367                     // Check for unused values. This usually means
368                     // there are extra statements in the AST.
369                     for i in 0..mir.temp_decls.len() {
370                         if self.temp_qualif[i].is_none() {
371                             continue;
372                         }
373
374                         let state = self.temp_promotion_state[i];
375                         if let TempState::Defined { location, uses: 0 } = state {
376                             let data = &mir[location.block];
377                             let stmt_idx = location.statement_index;
378
379                             // Get the span for the initialization.
380                             let source_info = if stmt_idx < data.statements.len() {
381                                 data.statements[stmt_idx].source_info
382                             } else {
383                                 data.terminator().source_info
384                             };
385                             self.span = source_info.span;
386
387                             // Treat this as a statement in the AST.
388                             self.statement_like();
389                         }
390                     }
391
392                     // Make sure there are no extra unassigned variables.
393                     self.qualif = Qualif::NOT_CONST;
394                     for index in 0..mir.var_decls.len() {
395                         if !self.const_fn_arg_vars.contains(index) {
396                             self.assign(&Lvalue::Var(index as u32));
397                         }
398                     }
399
400                     break;
401                 }
402             };
403
404             match target {
405                 // No loops allowed.
406                 Some(target) if !seen_blocks.contains(target.index()) => {
407                     bb = target;
408                 }
409                 _ => {
410                     self.not_const();
411                     break;
412                 }
413             }
414         }
415
416         let return_ty = mir.return_ty.unwrap();
417         self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
418
419         match self.mode {
420             Mode::StaticMut => {
421                 // Check for destructors in static mut.
422                 self.add_type(return_ty);
423                 self.deny_drop();
424             }
425             _ => {
426                 // Account for errors in consts by using the
427                 // conservative type qualification instead.
428                 if self.qualif.intersects(Qualif::CONST_ERROR) {
429                     self.qualif = Qualif::empty();
430                     self.add_type(return_ty);
431                 }
432             }
433         }
434         self.qualif
435     }
436 }
437
438 /// Accumulates an Rvalue or Call's effects in self.qualif.
439 /// For functions (constant or not), it also records
440 /// candidates for promotion in promotion_candidates.
441 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
442     fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
443         match *lvalue {
444             Lvalue::Arg(_) => {
445                 self.add(Qualif::FN_ARGUMENT);
446             }
447             Lvalue::Var(_) => {
448                 self.add(Qualif::NOT_CONST);
449             }
450             Lvalue::Temp(index) => {
451                 if !self.temp_promotion_state[index as usize].is_promotable() {
452                     self.add(Qualif::NOT_PROMOTABLE);
453                 }
454
455                 if let Some(qualif) = self.temp_qualif[index as usize] {
456                     self.add(qualif);
457                 } else {
458                     self.not_const();
459                 }
460             }
461             Lvalue::Static(_) => {
462                 self.add(Qualif::STATIC);
463                 if self.mode == Mode::Const || self.mode == Mode::ConstFn {
464                     span_err!(self.tcx.sess, self.span, E0013,
465                               "{}s cannot refer to statics, use \
466                                a constant instead", self.mode);
467                 }
468             }
469             Lvalue::ReturnPointer => {
470                 self.not_const();
471             }
472             Lvalue::Projection(ref proj) => {
473                 self.nest(|this| {
474                     this.super_lvalue(lvalue, context);
475                     match proj.elem {
476                         ProjectionElem::Deref => {
477                             if !this.try_consume() {
478                                 return;
479                             }
480
481                             if this.qualif.intersects(Qualif::STATIC_REF) {
482                                 this.qualif = this.qualif - Qualif::STATIC_REF;
483                                 this.add(Qualif::STATIC);
484                             }
485
486                             let base_ty = this.mir.lvalue_ty(this.tcx, &proj.base)
487                                               .to_ty(this.tcx);
488                             if let ty::TyRawPtr(_) = base_ty.sty {
489                                 this.add(Qualif::NOT_CONST);
490                                 if this.mode != Mode::Fn {
491                                     span_err!(this.tcx.sess, this.span, E0396,
492                                               "raw pointers cannot be dereferenced in {}s",
493                                               this.mode);
494                                 }
495                             }
496                         }
497
498                         ProjectionElem::Field(..) |
499                         ProjectionElem::Index(_) => {
500                             if this.mode != Mode::Fn &&
501                                this.qualif.intersects(Qualif::STATIC) {
502                                 span_err!(this.tcx.sess, this.span, E0494,
503                                           "cannot refer to the interior of another \
504                                            static, use a constant instead");
505                             }
506                             let ty = this.mir.lvalue_ty(this.tcx, lvalue)
507                                          .to_ty(this.tcx);
508                             this.qualif.restrict(ty, this.tcx, &this.param_env);
509                         }
510
511                         ProjectionElem::ConstantIndex {..} |
512                         ProjectionElem::Downcast(..) => {
513                             this.not_const()
514                         }
515                     }
516                 });
517             }
518         }
519     }
520
521     fn visit_operand(&mut self, operand: &Operand<'tcx>) {
522         match *operand {
523             Operand::Consume(_) => {
524                 self.nest(|this| {
525                     this.super_operand(operand);
526                     this.try_consume();
527                 });
528             }
529             Operand::Constant(ref constant) => {
530                 // Only functions and methods can have these types.
531                 if let ty::TyFnDef(..) = constant.ty.sty {
532                     return;
533                 }
534
535                 if let Literal::Item { def_id, substs } = constant.literal {
536                     // Don't peek inside generic (associated) constants.
537                     if !substs.types.is_empty() {
538                         self.add_type(constant.ty);
539                     } else {
540                         let qualif = qualify_const_item_cached(self.tcx,
541                                                                self.qualif_map,
542                                                                self.mir_map,
543                                                                def_id);
544                         self.add(qualif);
545                     }
546
547                     // FIXME(eddyb) check recursive constants here,
548                     // instead of rustc_passes::static_recursion.
549                     if self.qualif.intersects(Qualif::RECURSIVE) {
550                         span_bug!(constant.span,
551                                   "recursive constant wasn't caught earlier");
552                     }
553
554                     // Let `const fn` transitively have destructors,
555                     // but they do get stopped in `const` or `static`.
556                     if self.mode != Mode::ConstFn {
557                         self.deny_drop();
558                     }
559                 }
560             }
561         }
562     }
563
564     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
565         // Recurse through operands and lvalues.
566         self.super_rvalue(rvalue);
567
568         match *rvalue {
569             Rvalue::Use(_) |
570             Rvalue::Repeat(..) |
571             Rvalue::UnaryOp(..) |
572             Rvalue::CheckedBinaryOp(..) |
573             Rvalue::Cast(CastKind::ReifyFnPointer, _, _) |
574             Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
575             Rvalue::Cast(CastKind::Unsize, _, _) => {}
576
577             Rvalue::Len(_) => {
578                 // Static lvalues in consts would have errored already,
579                 // don't treat length checks as reads from statics.
580                 self.qualif = self.qualif - Qualif::STATIC;
581             }
582
583             Rvalue::Ref(_, kind, ref lvalue) => {
584                 // Static lvalues in consts would have errored already,
585                 // only keep track of references to them here.
586                 if self.qualif.intersects(Qualif::STATIC) {
587                     self.qualif = self.qualif - Qualif::STATIC;
588                     self.add(Qualif::STATIC_REF);
589                 }
590
591                 let ty = self.mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx);
592                 if kind == BorrowKind::Mut {
593                     // In theory, any zero-sized value could be borrowed
594                     // mutably without consequences. However, only &mut []
595                     // is allowed right now, and only in functions.
596                     let allow = if self.mode == Mode::StaticMut {
597                         // Inside a `static mut`, &mut [...] is also allowed.
598                         match ty.sty {
599                             ty::TyArray(..) | ty::TySlice(_) => {
600                                 // Mutating can expose drops, be conservative.
601                                 self.add_type(ty);
602                                 self.deny_drop();
603                                 true
604                             }
605                             _ => false
606                         }
607                     } else if let ty::TyArray(_, 0) = ty.sty {
608                         self.mode == Mode::Fn
609                     } else {
610                         false
611                     };
612
613                     if !allow {
614                         self.add(Qualif::NOT_CONST);
615                         if self.mode != Mode::Fn {
616                             span_err!(self.tcx.sess, self.span, E0017,
617                                       "references in {}s may only refer \
618                                        to immutable values", self.mode);
619                         }
620                     }
621                 } else {
622                     // Constants cannot be borrowed if they contain interior mutability as
623                     // it means that our "silent insertion of statics" could change
624                     // initializer values (very bad).
625                     if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
626                         // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
627                         // duplicate errors (from reborrowing, for example).
628                         self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
629                         self.add(Qualif::NOT_CONST);
630                         if self.mode != Mode::Fn {
631                             span_err!(self.tcx.sess, self.span, E0492,
632                                       "cannot borrow a constant which contains \
633                                        interior mutability, create a static instead");
634                         }
635                     }
636                 }
637
638                 // We might have a candidate for promotion.
639                 let candidate = Candidate::Ref(self.location);
640                 if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
641                     if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
642                         // We can only promote direct borrows of temps.
643                         if let Lvalue::Temp(_) = *lvalue {
644                             self.promotion_candidates.push(candidate);
645                         }
646                     }
647                 }
648             }
649
650             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
651                 let operand_ty = self.mir.operand_ty(self.tcx, operand);
652                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
653                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
654                 match (cast_in, cast_out) {
655                     (CastTy::Ptr(_), CastTy::Int(_)) |
656                     (CastTy::FnPtr, CastTy::Int(_)) => {
657                         self.add(Qualif::NOT_CONST);
658                         if self.mode != Mode::Fn {
659                             span_err!(self.tcx.sess, self.span, E0018,
660                                       "raw pointers cannot be cast to integers in {}s",
661                                       self.mode);
662                         }
663                     }
664                     _ => {}
665                 }
666             }
667
668             Rvalue::BinaryOp(op, ref lhs, _) => {
669                 if let ty::TyRawPtr(_) = self.mir.operand_ty(self.tcx, lhs).sty {
670                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
671                             op == BinOp::Le || op == BinOp::Lt ||
672                             op == BinOp::Ge || op == BinOp::Gt);
673
674                     self.add(Qualif::NOT_CONST);
675                     if self.mode != Mode::Fn {
676                         span_err!(self.tcx.sess, self.span, E0395,
677                                   "raw pointers cannot be compared in {}s",
678                                   self.mode);
679                     }
680                 }
681             }
682
683             Rvalue::Box(_) => {
684                 self.add(Qualif::NOT_CONST);
685                 if self.mode != Mode::Fn {
686                     span_err!(self.tcx.sess, self.span, E0010,
687                               "allocations are not allowed in {}s", self.mode);
688                 }
689             }
690
691             Rvalue::Aggregate(ref kind, _) => {
692                 if let AggregateKind::Adt(def, _, _) = *kind {
693                     if def.has_dtor() {
694                         self.add(Qualif::NEEDS_DROP);
695                         self.deny_drop();
696                     }
697
698                     if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() {
699                         let ty = self.mir.rvalue_ty(self.tcx, rvalue).unwrap();
700                         self.add_type(ty);
701                         assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
702                         // Even if the value inside may not need dropping,
703                         // mutating it would change that.
704                         if !self.qualif.intersects(Qualif::NOT_CONST) {
705                             self.deny_drop();
706                         }
707                     }
708                 }
709             }
710
711             Rvalue::Slice {..} |
712             Rvalue::InlineAsm {..} => {
713                 self.not_const();
714             }
715         }
716     }
717
718     fn visit_terminator_kind(&mut self, bb: BasicBlock, kind: &TerminatorKind<'tcx>) {
719         if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
720             self.visit_operand(func);
721
722             let fn_ty = self.mir.operand_ty(self.tcx, func);
723             let (is_shuffle, is_const_fn) = match fn_ty.sty {
724                 ty::TyFnDef(def_id, _, f) => {
725                     (f.abi == Abi::PlatformIntrinsic &&
726                      self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
727                      is_const_fn(self.tcx, def_id))
728                 }
729                 _ => (false, false)
730             };
731
732             for (i, arg) in args.iter().enumerate() {
733                 self.nest(|this| {
734                     this.visit_operand(arg);
735                     if is_shuffle && i == 2 && this.mode == Mode::Fn {
736                         let candidate = Candidate::ShuffleIndices(bb);
737                         if !this.qualif.intersects(Qualif::NEVER_PROMOTE) {
738                             this.promotion_candidates.push(candidate);
739                         } else {
740                             span_err!(this.tcx.sess, this.span, E0526,
741                                       "shuffle indices are not constant");
742                         }
743                     }
744                 });
745             }
746
747             // Const fn calls.
748             if is_const_fn {
749                 // We are in a const or static initializer,
750                 if self.mode != Mode::Fn &&
751
752                     // feature-gate is not enabled,
753                     !self.tcx.sess.features.borrow().const_fn &&
754
755                     // this doesn't come from a crate with the feature-gate enabled,
756                     self.def_id.is_local() &&
757
758                     // this doesn't come from a macro that has #[allow_internal_unstable]
759                     !self.tcx.sess.codemap().span_allows_unstable(self.span)
760                 {
761                     let mut err = self.tcx.sess.struct_span_err(self.span,
762                         "const fns are an unstable feature");
763                     help!(&mut err,
764                           "in Nightly builds, add `#![feature(const_fn)]` \
765                            to the crate attributes to enable");
766                     err.emit();
767                 }
768             } else {
769                 self.qualif = Qualif::NOT_CONST;
770                 if self.mode != Mode::Fn {
771                     // FIXME(#24111) Remove this check when const fn stabilizes
772                     let (msg, note) = if let UnstableFeatures::Disallow =
773                             self.tcx.sess.opts.unstable_features {
774                         (format!("calls in {}s are limited to \
775                                   struct and enum constructors",
776                                  self.mode),
777                          Some("a limited form of compile-time function \
778                                evaluation is available on a nightly \
779                                compiler via `const fn`"))
780                     } else {
781                         (format!("calls in {}s are limited \
782                                   to constant functions, \
783                                   struct and enum constructors",
784                                  self.mode),
785                          None)
786                     };
787                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
788                     if let Some(note) = note {
789                         err.span_note(self.span, note);
790                     }
791                     err.emit();
792                 }
793             }
794
795             if let Some((ref dest, _)) = *destination {
796                 // Avoid propagating irrelevant callee/argument qualifications.
797                 if self.qualif.intersects(Qualif::CONST_ERROR) {
798                     self.qualif = Qualif::NOT_CONST;
799                 } else {
800                     // Be conservative about the returned value of a const fn.
801                     let tcx = self.tcx;
802                     let ty = self.mir.lvalue_ty(tcx, dest).to_ty(tcx);
803                     self.qualif = Qualif::empty();
804                     self.add_type(ty);
805
806                     // Let `const fn` transitively have destructors,
807                     // but they do get stopped in `const` or `static`.
808                     if self.mode != Mode::ConstFn {
809                         self.deny_drop();
810                     }
811                 }
812                 self.assign(dest);
813             }
814         } else {
815             // Qualify any operands inside other terminators.
816             self.super_terminator_kind(bb, kind);
817         }
818     }
819
820     fn visit_assign(&mut self, _: BasicBlock, dest: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
821         self.visit_rvalue(rvalue);
822
823         // Check the allowed const fn argument forms.
824         if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
825             if self.const_fn_arg_vars.insert(index as usize) {
826                 // Direct use of an argument is permitted.
827                 if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
828                     return;
829                 }
830
831                 // Avoid a generic error for other uses of arguments.
832                 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
833                     let decl = &self.mir.var_decls[index as usize];
834                     span_err!(self.tcx.sess, decl.source_info.span, E0022,
835                               "arguments of constant functions can only \
836                                be immutable by-value bindings");
837                     return;
838                 }
839             }
840         }
841
842         self.assign(dest);
843     }
844
845     fn visit_source_info(&mut self, source_info: &SourceInfo) {
846         self.span = source_info.span;
847     }
848
849     fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
850         assert_eq!(self.location.block, bb);
851         self.nest(|this| this.super_statement(bb, statement));
852         self.location.statement_index += 1;
853     }
854
855     fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
856         assert_eq!(self.location.block, bb);
857         self.nest(|this| this.super_terminator(bb, terminator));
858     }
859
860     fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
861         self.location.statement_index = 0;
862         self.location.block = bb;
863         self.super_basic_block_data(bb, data);
864     }
865 }
866
867 fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
868                                        qualif_map: &mut DefIdMap<Qualif>,
869                                        mir_map: Option<&MirMap<'tcx>>,
870                                        def_id: DefId)
871                                        -> Qualif {
872     match qualif_map.entry(def_id) {
873         Entry::Occupied(entry) => return *entry.get(),
874         Entry::Vacant(entry) => {
875             // Guard against `const` recursion.
876             entry.insert(Qualif::RECURSIVE);
877         }
878     }
879
880     let extern_mir;
881     let param_env_and_mir = if def_id.is_local() {
882         let node_id = tcx.map.as_local_node_id(def_id).unwrap();
883         mir_map.and_then(|map| map.map.get(&node_id)).map(|mir| {
884             (ty::ParameterEnvironment::for_item(tcx, node_id), mir)
885         })
886     } else if let Some(mir) = tcx.sess.cstore.maybe_get_item_mir(tcx, def_id) {
887         // These should only be monomorphic constants.
888         extern_mir = mir;
889         Some((tcx.empty_parameter_environment(), &extern_mir))
890     } else {
891         None
892     };
893
894     let (param_env, mir) = param_env_and_mir.unwrap_or_else(|| {
895         bug!("missing constant MIR for {}", tcx.item_path_str(def_id))
896     });
897
898     let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, mir_map,
899                                        def_id, mir, Mode::Const);
900     let qualif = qualifier.qualify_const();
901     qualifier.qualif_map.insert(def_id, qualif);
902     qualif
903 }
904
905 pub struct QualifyAndPromoteConstants;
906
907 impl Pass for QualifyAndPromoteConstants {}
908
909 impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
910     fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>) {
911         let mut qualif_map = DefIdMap();
912
913         // First, visit `const` items, potentially recursing, to get
914         // accurate MUTABLE_INTERIOR and NEEDS_DROP qualifications.
915         for &id in map.map.keys() {
916             let def_id = tcx.map.local_def_id(id);
917             let _task = tcx.dep_graph.in_task(self.dep_node(def_id));
918             let src = MirSource::from_node(tcx, id);
919             if let MirSource::Const(_) = src {
920                 qualify_const_item_cached(tcx, &mut qualif_map, Some(map), def_id);
921             }
922         }
923
924         // Then, handle everything else, without recursing,
925         // as the MIR map is not shared, since promotion
926         // in functions (including `const fn`) mutates it.
927         for (&id, mir) in &mut map.map {
928             let def_id = tcx.map.local_def_id(id);
929             let _task = tcx.dep_graph.in_task(self.dep_node(def_id));
930             let src = MirSource::from_node(tcx, id);
931             let mode = match src {
932                 MirSource::Fn(_) => {
933                     if is_const_fn(tcx, def_id) {
934                         Mode::ConstFn
935                     } else {
936                         Mode::Fn
937                     }
938                 }
939                 MirSource::Const(_) => continue,
940                 MirSource::Static(_, hir::MutImmutable) => Mode::Static,
941                 MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
942                 MirSource::Promoted(..) => bug!()
943             };
944             let param_env = ty::ParameterEnvironment::for_item(tcx, id);
945
946             if mode == Mode::Fn || mode == Mode::ConstFn {
947                 // This is ugly because Qualifier holds onto mir,
948                 // which can't be mutated until its scope ends.
949                 let (temps, candidates) = {
950                     let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
951                                                        None, def_id, mir, mode);
952                     if mode == Mode::ConstFn {
953                         // Enforce a constant-like CFG for `const fn`.
954                         qualifier.qualify_const();
955                     } else {
956                         while let Some((bb, data)) = qualifier.rpo.next() {
957                             qualifier.visit_basic_block_data(bb, data);
958                         }
959                     }
960
961                     (qualifier.temp_promotion_state,
962                      qualifier.promotion_candidates)
963                 };
964
965                 // Do the actual promotion, now that we know what's viable.
966                 promote_consts::promote_candidates(mir, tcx, temps, candidates);
967             } else {
968                 let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
969                                                    None, def_id, mir, mode);
970                 qualifier.qualify_const();
971             }
972
973             // Statics must be Sync.
974             if mode == Mode::Static {
975                 let ty = mir.return_ty.unwrap();
976                 tcx.infer_ctxt(None, None, ProjectionMode::AnyFinal).enter(|infcx| {
977                     let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
978                     let mut fulfillment_cx = traits::FulfillmentContext::new();
979                     fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
980                     if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
981                         infcx.report_fulfillment_errors(&err);
982                     }
983
984                     if let Err(errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
985                         infcx.report_fulfillment_errors_as_warnings(&errors, id);
986                     }
987                 });
988             }
989         }
990     }
991 }