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