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