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