]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/qualify_consts.rs
updated E0396 to new error format
[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             span_err!(self.tcx.sess, self.span, E0394, "{}", msg);
281
282             // Replace STATIC with NOT_CONST to avoid further errors.
283             self.qualif = self.qualif - Qualif::STATIC;
284             self.add(Qualif::NOT_CONST);
285
286             false
287         } else {
288             true
289         }
290     }
291
292     /// Assign the current qualification to the given destination.
293     fn assign(&mut self, dest: &Lvalue<'tcx>) {
294         let qualif = self.qualif;
295         let span = self.span;
296         let store = |slot: &mut Option<Qualif>| {
297             if slot.is_some() {
298                 span_bug!(span, "multiple assignments to {:?}", dest);
299             }
300             *slot = Some(qualif);
301         };
302
303         // Only handle promotable temps in non-const functions.
304         if self.mode == Mode::Fn {
305             if let Lvalue::Temp(index) = *dest {
306                 if self.temp_promotion_state[index].is_promotable() {
307                     store(&mut self.temp_qualif[index]);
308                 }
309             }
310             return;
311         }
312
313         match *dest {
314             Lvalue::Temp(index) => store(&mut self.temp_qualif[index]),
315             Lvalue::ReturnPointer => store(&mut self.return_qualif),
316
317             Lvalue::Projection(box Projection {
318                 base: Lvalue::Temp(index),
319                 elem: ProjectionElem::Deref
320             }) if self.mir.temp_decls[index].ty.is_unique()
321                && self.temp_qualif[index].map_or(false, |qualif| {
322                     qualif.intersects(Qualif::NOT_CONST)
323                }) => {
324                 // Part of `box expr`, we should've errored
325                 // already for the Box allocation Rvalue.
326             }
327
328             // This must be an explicit assignment.
329             _ => {
330                 // Catch more errors in the destination.
331                 self.visit_lvalue(dest, LvalueContext::Store);
332                 self.statement_like();
333             }
334         }
335     }
336
337     /// Qualify a whole const, static initializer or const fn.
338     fn qualify_const(&mut self) -> Qualif {
339         let mir = self.mir;
340
341         let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
342         let mut bb = START_BLOCK;
343         loop {
344             seen_blocks.insert(bb.index());
345
346             self.visit_basic_block_data(bb, &mir[bb]);
347
348             let target = match mir[bb].terminator().kind {
349                 TerminatorKind::Goto { target } |
350                 // Drops are considered noops.
351                 TerminatorKind::Drop { target, .. } |
352                 TerminatorKind::Assert { target, .. } |
353                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
354                     Some(target)
355                 }
356
357                 // Non-terminating calls cannot produce any value.
358                 TerminatorKind::Call { destination: None, .. } => {
359                     return Qualif::empty();
360                 }
361
362                 TerminatorKind::If {..} |
363                 TerminatorKind::Switch {..} |
364                 TerminatorKind::SwitchInt {..} |
365                 TerminatorKind::DropAndReplace { .. } |
366                 TerminatorKind::Resume |
367                 TerminatorKind::Unreachable => None,
368
369                 TerminatorKind::Return => {
370                     // Check for unused values. This usually means
371                     // there are extra statements in the AST.
372                     for temp in mir.temp_decls.indices() {
373                         if self.temp_qualif[temp].is_none() {
374                             continue;
375                         }
376
377                         let state = self.temp_promotion_state[temp];
378                         if let TempState::Defined { location, uses: 0 } = state {
379                             let data = &mir[location.block];
380                             let stmt_idx = location.statement_index;
381
382                             // Get the span for the initialization.
383                             let source_info = if stmt_idx < data.statements.len() {
384                                 data.statements[stmt_idx].source_info
385                             } else {
386                                 data.terminator().source_info
387                             };
388                             self.span = source_info.span;
389
390                             // Treat this as a statement in the AST.
391                             self.statement_like();
392                         }
393                     }
394
395                     // Make sure there are no extra unassigned variables.
396                     self.qualif = Qualif::NOT_CONST;
397                     for index in 0..mir.var_decls.len() {
398                         if !self.const_fn_arg_vars.contains(index) {
399                             self.assign(&Lvalue::Var(Var::new(index)));
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) {
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);
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                                     struct_span_err!(this.tcx.sess,
494                                         this.span, E0396,
495                                         "raw pointers cannot be dereferenced in {}s",
496                                         this.mode)
497                                     .span_label(this.span,
498                                         &format!("dereference of raw pointer in constant"))
499                                     .emit();
500                                 }
501                             }
502                         }
503
504                         ProjectionElem::Field(..) |
505                         ProjectionElem::Index(_) => {
506                             if this.mode != Mode::Fn &&
507                                this.qualif.intersects(Qualif::STATIC) {
508                                 span_err!(this.tcx.sess, this.span, E0494,
509                                           "cannot refer to the interior of another \
510                                            static, use a constant instead");
511                             }
512                             let ty = lvalue.ty(this.mir, this.tcx).to_ty(this.tcx);
513                             this.qualif.restrict(ty, this.tcx, &this.param_env);
514                         }
515
516                         ProjectionElem::ConstantIndex {..} |
517                         ProjectionElem::Subslice {..} |
518                         ProjectionElem::Downcast(..) => {
519                             this.not_const()
520                         }
521                     }
522                 });
523             }
524         }
525     }
526
527     fn visit_operand(&mut self, operand: &Operand<'tcx>) {
528         match *operand {
529             Operand::Consume(_) => {
530                 self.nest(|this| {
531                     this.super_operand(operand);
532                     this.try_consume();
533                 });
534             }
535             Operand::Constant(ref constant) => {
536                 // Only functions and methods can have these types.
537                 if let ty::TyFnDef(..) = constant.ty.sty {
538                     return;
539                 }
540
541                 if let Literal::Item { def_id, substs } = constant.literal {
542                     // Don't peek inside generic (associated) constants.
543                     if !substs.types.is_empty() {
544                         self.add_type(constant.ty);
545                     } else {
546                         let qualif = qualify_const_item_cached(self.tcx,
547                                                                self.qualif_map,
548                                                                self.mir_map,
549                                                                def_id);
550                         self.add(qualif);
551                     }
552
553                     // FIXME(eddyb) check recursive constants here,
554                     // instead of rustc_passes::static_recursion.
555                     if self.qualif.intersects(Qualif::RECURSIVE) {
556                         span_bug!(constant.span,
557                                   "recursive constant wasn't caught earlier");
558                     }
559
560                     // Let `const fn` transitively have destructors,
561                     // but they do get stopped in `const` or `static`.
562                     if self.mode != Mode::ConstFn {
563                         self.deny_drop();
564                     }
565                 }
566             }
567         }
568     }
569
570     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
571         // Recurse through operands and lvalues.
572         self.super_rvalue(rvalue);
573
574         match *rvalue {
575             Rvalue::Use(_) |
576             Rvalue::Repeat(..) |
577             Rvalue::UnaryOp(..) |
578             Rvalue::CheckedBinaryOp(..) |
579             Rvalue::Cast(CastKind::ReifyFnPointer, _, _) |
580             Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
581             Rvalue::Cast(CastKind::Unsize, _, _) => {}
582
583             Rvalue::Len(_) => {
584                 // Static lvalues in consts would have errored already,
585                 // don't treat length checks as reads from statics.
586                 self.qualif = self.qualif - Qualif::STATIC;
587             }
588
589             Rvalue::Ref(_, kind, ref lvalue) => {
590                 // Static lvalues in consts would have errored already,
591                 // only keep track of references to them here.
592                 if self.qualif.intersects(Qualif::STATIC) {
593                     self.qualif = self.qualif - Qualif::STATIC;
594                     self.add(Qualif::STATIC_REF);
595                 }
596
597                 let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
598                 if kind == BorrowKind::Mut {
599                     // In theory, any zero-sized value could be borrowed
600                     // mutably without consequences. However, only &mut []
601                     // is allowed right now, and only in functions.
602                     let allow = if self.mode == Mode::StaticMut {
603                         // Inside a `static mut`, &mut [...] is also allowed.
604                         match ty.sty {
605                             ty::TyArray(..) | ty::TySlice(_) => {
606                                 // Mutating can expose drops, be conservative.
607                                 self.add_type(ty);
608                                 self.deny_drop();
609                                 true
610                             }
611                             _ => false
612                         }
613                     } else if let ty::TyArray(_, 0) = ty.sty {
614                         self.mode == Mode::Fn
615                     } else {
616                         false
617                     };
618
619                     if !allow {
620                         self.add(Qualif::NOT_CONST);
621                         if self.mode != Mode::Fn {
622                             struct_span_err!(self.tcx.sess,  self.span, E0017,
623                                              "references in {}s may only refer \
624                                               to immutable values", self.mode)
625                                 .span_label(self.span, &format!("{}s require immutable values",
626                                                                 self.mode))
627                                 .emit();
628                         }
629                     }
630                 } else {
631                     // Constants cannot be borrowed if they contain interior mutability as
632                     // it means that our "silent insertion of statics" could change
633                     // initializer values (very bad).
634                     if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
635                         // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
636                         // duplicate errors (from reborrowing, for example).
637                         self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
638                         self.add(Qualif::NOT_CONST);
639                         if self.mode != Mode::Fn {
640                             span_err!(self.tcx.sess, self.span, E0492,
641                                       "cannot borrow a constant which contains \
642                                        interior mutability, create a static instead");
643                         }
644                     }
645                 }
646
647                 // We might have a candidate for promotion.
648                 let candidate = Candidate::Ref(self.location);
649                 if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
650                     if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
651                         // We can only promote direct borrows of temps.
652                         if let Lvalue::Temp(_) = *lvalue {
653                             self.promotion_candidates.push(candidate);
654                         }
655                     }
656                 }
657             }
658
659             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
660                 let operand_ty = operand.ty(self.mir, self.tcx);
661                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
662                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
663                 match (cast_in, cast_out) {
664                     (CastTy::Ptr(_), CastTy::Int(_)) |
665                     (CastTy::FnPtr, CastTy::Int(_)) => {
666                         self.add(Qualif::NOT_CONST);
667                         if self.mode != Mode::Fn {
668                             span_err!(self.tcx.sess, self.span, E0018,
669                                       "raw pointers cannot be cast to integers in {}s",
670                                       self.mode);
671                         }
672                     }
673                     _ => {}
674                 }
675             }
676
677             Rvalue::BinaryOp(op, ref lhs, _) => {
678                 if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
679                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
680                             op == BinOp::Le || op == BinOp::Lt ||
681                             op == BinOp::Ge || op == BinOp::Gt);
682
683                     self.add(Qualif::NOT_CONST);
684                     if self.mode != Mode::Fn {
685                         span_err!(self.tcx.sess, self.span, E0395,
686                                   "raw pointers cannot be compared in {}s",
687                                   self.mode);
688                     }
689                 }
690             }
691
692             Rvalue::Box(_) => {
693                 self.add(Qualif::NOT_CONST);
694                 if self.mode != Mode::Fn {
695                     struct_span_err!(self.tcx.sess, self.span, E0010,
696                                      "allocations are not allowed in {}s", self.mode)
697                         .span_label(self.span, &format!("allocation not allowed in {}s", self.mode))
698                         .emit();
699                 }
700             }
701
702             Rvalue::Aggregate(ref kind, _) => {
703                 if let AggregateKind::Adt(def, _, _) = *kind {
704                     if def.has_dtor() {
705                         self.add(Qualif::NEEDS_DROP);
706                         self.deny_drop();
707                     }
708
709                     if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() {
710                         let ty = rvalue.ty(self.mir, self.tcx).unwrap();
711                         self.add_type(ty);
712                         assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
713                         // Even if the value inside may not need dropping,
714                         // mutating it would change that.
715                         if !self.qualif.intersects(Qualif::NOT_CONST) {
716                             self.deny_drop();
717                         }
718                     }
719                 }
720             }
721
722             Rvalue::InlineAsm {..} => {
723                 self.not_const();
724             }
725         }
726     }
727
728     fn visit_terminator_kind(&mut self, bb: BasicBlock, kind: &TerminatorKind<'tcx>) {
729         if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
730             self.visit_operand(func);
731
732             let fn_ty = func.ty(self.mir, self.tcx);
733             let (is_shuffle, is_const_fn) = match fn_ty.sty {
734                 ty::TyFnDef(def_id, _, f) => {
735                     (f.abi == Abi::PlatformIntrinsic &&
736                      self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
737                      is_const_fn(self.tcx, def_id))
738                 }
739                 _ => (false, false)
740             };
741
742             for (i, arg) in args.iter().enumerate() {
743                 self.nest(|this| {
744                     this.visit_operand(arg);
745                     if is_shuffle && i == 2 && this.mode == Mode::Fn {
746                         let candidate = Candidate::ShuffleIndices(bb);
747                         if !this.qualif.intersects(Qualif::NEVER_PROMOTE) {
748                             this.promotion_candidates.push(candidate);
749                         } else {
750                             span_err!(this.tcx.sess, this.span, E0526,
751                                       "shuffle indices are not constant");
752                         }
753                     }
754                 });
755             }
756
757             // Const fn calls.
758             if is_const_fn {
759                 // We are in a const or static initializer,
760                 if self.mode != Mode::Fn &&
761
762                     // feature-gate is not enabled,
763                     !self.tcx.sess.features.borrow().const_fn &&
764
765                     // this doesn't come from a crate with the feature-gate enabled,
766                     self.def_id.is_local() &&
767
768                     // this doesn't come from a macro that has #[allow_internal_unstable]
769                     !self.tcx.sess.codemap().span_allows_unstable(self.span)
770                 {
771                     let mut err = self.tcx.sess.struct_span_err(self.span,
772                         "const fns are an unstable feature");
773                     help!(&mut err,
774                           "in Nightly builds, add `#![feature(const_fn)]` \
775                            to the crate attributes to enable");
776                     err.emit();
777                 }
778             } else {
779                 self.qualif = Qualif::NOT_CONST;
780                 if self.mode != Mode::Fn {
781                     // FIXME(#24111) Remove this check when const fn stabilizes
782                     let (msg, note) = if let UnstableFeatures::Disallow =
783                             self.tcx.sess.opts.unstable_features {
784                         (format!("calls in {}s are limited to \
785                                   struct and enum constructors",
786                                  self.mode),
787                          Some("a limited form of compile-time function \
788                                evaluation is available on a nightly \
789                                compiler via `const fn`"))
790                     } else {
791                         (format!("calls in {}s are limited \
792                                   to constant functions, \
793                                   struct and enum constructors",
794                                  self.mode),
795                          None)
796                     };
797                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
798                     if let Some(note) = note {
799                         err.span_note(self.span, note);
800                     }
801                     err.emit();
802                 }
803             }
804
805             if let Some((ref dest, _)) = *destination {
806                 // Avoid propagating irrelevant callee/argument qualifications.
807                 if self.qualif.intersects(Qualif::CONST_ERROR) {
808                     self.qualif = Qualif::NOT_CONST;
809                 } else {
810                     // Be conservative about the returned value of a const fn.
811                     let tcx = self.tcx;
812                     let ty = dest.ty(self.mir, tcx).to_ty(tcx);
813                     self.qualif = Qualif::empty();
814                     self.add_type(ty);
815
816                     // Let `const fn` transitively have destructors,
817                     // but they do get stopped in `const` or `static`.
818                     if self.mode != Mode::ConstFn {
819                         self.deny_drop();
820                     }
821                 }
822                 self.assign(dest);
823             }
824         } else {
825             // Qualify any operands inside other terminators.
826             self.super_terminator_kind(bb, kind);
827         }
828     }
829
830     fn visit_assign(&mut self, _: BasicBlock, dest: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
831         self.visit_rvalue(rvalue);
832
833         // Check the allowed const fn argument forms.
834         if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
835             if self.const_fn_arg_vars.insert(index.index()) {
836                 // Direct use of an argument is permitted.
837                 if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
838                     return;
839                 }
840
841                 // Avoid a generic error for other uses of arguments.
842                 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
843                     let decl = &self.mir.var_decls[index];
844                     span_err!(self.tcx.sess, decl.source_info.span, E0022,
845                               "arguments of constant functions can only \
846                                be immutable by-value bindings");
847                     return;
848                 }
849             }
850         }
851
852         self.assign(dest);
853     }
854
855     fn visit_source_info(&mut self, source_info: &SourceInfo) {
856         self.span = source_info.span;
857     }
858
859     fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
860         assert_eq!(self.location.block, bb);
861         self.nest(|this| {
862             this.visit_source_info(&statement.source_info);
863             match statement.kind {
864                 StatementKind::Assign(ref lvalue, ref rvalue) => {
865                     this.visit_assign(bb, lvalue, rvalue);
866                 }
867                 StatementKind::SetDiscriminant { .. } |
868                 StatementKind::StorageLive(_) |
869                 StatementKind::StorageDead(_) => {}
870             }
871         });
872         self.location.statement_index += 1;
873     }
874
875     fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
876         assert_eq!(self.location.block, bb);
877         self.nest(|this| this.super_terminator(bb, terminator));
878     }
879
880     fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
881         self.location.statement_index = 0;
882         self.location.block = bb;
883         self.super_basic_block_data(bb, data);
884     }
885 }
886
887 fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
888                                        qualif_map: &mut DefIdMap<Qualif>,
889                                        mir_map: Option<&MirMap<'tcx>>,
890                                        def_id: DefId)
891                                        -> Qualif {
892     match qualif_map.entry(def_id) {
893         Entry::Occupied(entry) => return *entry.get(),
894         Entry::Vacant(entry) => {
895             // Guard against `const` recursion.
896             entry.insert(Qualif::RECURSIVE);
897         }
898     }
899
900     let extern_mir;
901     let param_env_and_mir = if def_id.is_local() {
902         mir_map.and_then(|map| map.map.get(&def_id)).map(|mir| {
903             let node_id = tcx.map.as_local_node_id(def_id).unwrap();
904             (ty::ParameterEnvironment::for_item(tcx, node_id), mir)
905         })
906     } else if let Some(mir) = tcx.sess.cstore.maybe_get_item_mir(tcx, def_id) {
907         // These should only be monomorphic constants.
908         extern_mir = mir;
909         Some((tcx.empty_parameter_environment(), &extern_mir))
910     } else {
911         None
912     };
913
914     let (param_env, mir) = param_env_and_mir.unwrap_or_else(|| {
915         bug!("missing constant MIR for {}", tcx.item_path_str(def_id))
916     });
917
918     let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, mir_map,
919                                        def_id, mir, Mode::Const);
920     let qualif = qualifier.qualify_const();
921     qualifier.qualif_map.insert(def_id, qualif);
922     qualif
923 }
924
925 pub struct QualifyAndPromoteConstants;
926
927 impl Pass for QualifyAndPromoteConstants {}
928
929 impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
930     fn run_pass<'a>(&mut self,
931                     tcx: TyCtxt<'a, 'tcx, 'tcx>,
932                     map: &mut MirMap<'tcx>,
933                     hooks: &mut [Box<for<'s> MirPassHook<'s>>]) {
934         let mut qualif_map = DefIdMap();
935
936         // First, visit `const` items, potentially recursing, to get
937         // accurate MUTABLE_INTERIOR and NEEDS_DROP qualifications.
938         let keys = map.map.keys();
939         for &def_id in &keys {
940             let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
941             let id = tcx.map.as_local_node_id(def_id).unwrap();
942             let src = MirSource::from_node(tcx, id);
943             if let MirSource::Const(_) = src {
944                 qualify_const_item_cached(tcx, &mut qualif_map, Some(map), def_id);
945             }
946         }
947
948         // Then, handle everything else, without recursing,
949         // as the MIR map is not shared, since promotion
950         // in functions (including `const fn`) mutates it.
951         for &def_id in &keys {
952             let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
953             let id = tcx.map.as_local_node_id(def_id).unwrap();
954             let src = MirSource::from_node(tcx, id);
955             let mode = match src {
956                 MirSource::Fn(_) => {
957                     if is_const_fn(tcx, def_id) {
958                         Mode::ConstFn
959                     } else {
960                         Mode::Fn
961                     }
962                 }
963                 MirSource::Const(_) => continue,
964                 MirSource::Static(_, hir::MutImmutable) => Mode::Static,
965                 MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
966                 MirSource::Promoted(..) => bug!()
967             };
968             let param_env = ty::ParameterEnvironment::for_item(tcx, id);
969
970             let mir = map.map.get_mut(&def_id).unwrap();
971             for hook in &mut *hooks {
972                 hook.on_mir_pass(tcx, src, mir, self, false);
973             }
974
975             if mode == Mode::Fn || mode == Mode::ConstFn {
976                 // This is ugly because Qualifier holds onto mir,
977                 // which can't be mutated until its scope ends.
978                 let (temps, candidates) = {
979                     let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
980                                                        None, def_id, mir, mode);
981                     if mode == Mode::ConstFn {
982                         // Enforce a constant-like CFG for `const fn`.
983                         qualifier.qualify_const();
984                     } else {
985                         while let Some((bb, data)) = qualifier.rpo.next() {
986                             qualifier.visit_basic_block_data(bb, data);
987                         }
988                     }
989
990                     (qualifier.temp_promotion_state,
991                      qualifier.promotion_candidates)
992                 };
993
994                 // Do the actual promotion, now that we know what's viable.
995                 promote_consts::promote_candidates(mir, tcx, temps, candidates);
996             } else {
997                 let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
998                                                    None, def_id, mir, mode);
999                 qualifier.qualify_const();
1000             }
1001
1002             for hook in &mut *hooks {
1003                 hook.on_mir_pass(tcx, src, mir, self, true);
1004             }
1005
1006             // Statics must be Sync.
1007             if mode == Mode::Static {
1008                 let ty = mir.return_ty;
1009                 tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
1010                     let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1011                     let mut fulfillment_cx = traits::FulfillmentContext::new();
1012                     fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
1013                     if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1014                         infcx.report_fulfillment_errors(&err);
1015                     }
1016
1017                     if let Err(errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
1018                         infcx.report_fulfillment_errors_as_warnings(&errors, id);
1019                     }
1020                 });
1021             }
1022         }
1023     }
1024 }