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