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