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