]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/qualify_consts.rs
Rollup merge of #48084 - cramertj:impl-trait-errors, r=nikomatsakis
[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_set::IdxSetBuf;
19 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
20 use rustc_data_structures::fx::FxHashSet;
21 use rustc::hir;
22 use rustc::hir::def_id::DefId;
23 use rustc::middle::const_val::ConstVal;
24 use rustc::traits::{self, Reveal};
25 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
26 use rustc::ty::cast::CastTy;
27 use rustc::ty::maps::Providers;
28 use rustc::mir::*;
29 use rustc::mir::traversal::ReversePostorder;
30 use rustc::mir::visit::{PlaceContext, Visitor};
31 use rustc::middle::lang_items;
32 use syntax::abi::Abi;
33 use syntax::attr;
34 use syntax::ast::LitKind;
35 use syntax::feature_gate::UnstableFeatures;
36 use syntax_pos::{Span, DUMMY_SP};
37
38 use std::fmt;
39 use std::rc::Rc;
40 use std::usize;
41
42 use transform::{MirPass, MirSource};
43 use super::promote_consts::{self, Candidate, TempState};
44
45 bitflags! {
46     // Borrows of temporaries can be promoted only if
47     // they have none of these qualifications, with
48     // the exception of `STATIC_REF` (in statics only).
49     struct Qualif: u8 {
50         // Constant containing interior mutability (UnsafeCell).
51         const MUTABLE_INTERIOR  = 1 << 0;
52
53         // Constant containing an ADT that implements Drop.
54         const NEEDS_DROP        = 1 << 1;
55
56         // Function argument.
57         const FN_ARGUMENT       = 1 << 2;
58
59         // Static place or move from a static.
60         const STATIC            = 1 << 3;
61
62         // Reference to a static.
63         const STATIC_REF        = 1 << 4;
64
65         // Not constant at all - non-`const fn` calls, asm!,
66         // pointer comparisons, ptr-to-int casts, etc.
67         const NOT_CONST         = 1 << 5;
68
69         // Refers to temporaries which cannot be promoted as
70         // promote_consts decided they weren't simple enough.
71         const NOT_PROMOTABLE    = 1 << 6;
72
73         // Const items can only have MUTABLE_INTERIOR
74         // and NOT_PROMOTABLE without producing an error.
75         const CONST_ERROR       = !Qualif::MUTABLE_INTERIOR.bits &
76                                   !Qualif::NOT_PROMOTABLE.bits;
77     }
78 }
79
80 impl<'a, 'tcx> Qualif {
81     /// Remove flags which are impossible for the given type.
82     fn restrict(&mut self, ty: Ty<'tcx>,
83                 tcx: TyCtxt<'a, 'tcx, 'tcx>,
84                 param_env: ty::ParamEnv<'tcx>) {
85         if ty.is_freeze(tcx, param_env, DUMMY_SP) {
86             *self = *self - Qualif::MUTABLE_INTERIOR;
87         }
88         if !ty.needs_drop(tcx, param_env) {
89             *self = *self - Qualif::NEEDS_DROP;
90         }
91     }
92 }
93
94 /// What kind of item we are in.
95 #[derive(Copy, Clone, PartialEq, Eq)]
96 enum Mode {
97     Const,
98     Static,
99     StaticMut,
100     ConstFn,
101     Fn
102 }
103
104 impl fmt::Display for Mode {
105     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106         match *self {
107             Mode::Const => write!(f, "constant"),
108             Mode::Static | Mode::StaticMut => write!(f, "static"),
109             Mode::ConstFn => write!(f, "constant function"),
110             Mode::Fn => write!(f, "function")
111         }
112     }
113 }
114
115 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
116     mode: Mode,
117     span: Span,
118     def_id: DefId,
119     mir: &'a Mir<'tcx>,
120     rpo: ReversePostorder<'a, 'tcx>,
121     tcx: TyCtxt<'a, 'gcx, 'tcx>,
122     param_env: ty::ParamEnv<'tcx>,
123     temp_qualif: IndexVec<Local, Option<Qualif>>,
124     return_qualif: Option<Qualif>,
125     qualif: Qualif,
126     const_fn_arg_vars: BitVector,
127     temp_promotion_state: IndexVec<Local, TempState>,
128     promotion_candidates: Vec<Candidate>
129 }
130
131 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
132     fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
133            def_id: DefId,
134            mir: &'a Mir<'tcx>,
135            mode: Mode)
136            -> Qualifier<'a, 'tcx, 'tcx> {
137         let mut rpo = traversal::reverse_postorder(mir);
138         let temps = promote_consts::collect_temps(mir, &mut rpo);
139         rpo.reset();
140
141         let param_env = tcx.param_env(def_id);
142
143         let mut temp_qualif = IndexVec::from_elem(None, &mir.local_decls);
144         for arg in mir.args_iter() {
145             let mut qualif = Qualif::NEEDS_DROP;
146             qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
147             temp_qualif[arg] = Some(qualif);
148         }
149
150         Qualifier {
151             mode,
152             span: mir.span,
153             def_id,
154             mir,
155             rpo,
156             tcx,
157             param_env,
158             temp_qualif,
159             return_qualif: None,
160             qualif: Qualif::empty(),
161             const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
162             temp_promotion_state: temps,
163             promotion_candidates: vec![]
164         }
165     }
166
167     // FIXME(eddyb) we could split the errors into meaningful
168     // categories, but enabling full miri would make that
169     // slightly pointless (even with feature-gating).
170     fn not_const(&mut self) {
171         self.add(Qualif::NOT_CONST);
172         if self.mode != Mode::Fn {
173             let mut err = struct_span_err!(
174                 self.tcx.sess,
175                 self.span,
176                 E0019,
177                 "{} contains unimplemented expression type",
178                 self.mode
179             );
180             if self.tcx.sess.teach(&err.get_code().unwrap()) {
181                 err.note("A function call isn't allowed in the const's initialization expression \
182                           because the expression's value must be known at compile-time.");
183                 err.note("Remember: you can't use a function call inside a const's initialization \
184                           expression! However, you can use it anywhere else.");
185             }
186             err.emit();
187         }
188     }
189
190     /// Error about extra statements in a constant.
191     fn statement_like(&mut self) {
192         self.add(Qualif::NOT_CONST);
193         if self.mode != Mode::Fn {
194             let mut err = struct_span_err!(
195                 self.tcx.sess,
196                 self.span,
197                 E0016,
198                 "blocks in {}s are limited to items and tail expressions",
199                 self.mode
200             );
201             if self.tcx.sess.teach(&err.get_code().unwrap()) {
202                 err.note("Blocks in constants may only contain items (such as constant, function \
203                           definition, etc...) and a tail expression.");
204                 err.help("To avoid it, you have to replace the non-item object.");
205             }
206             err.emit();
207         }
208     }
209
210     /// Add the given qualification to self.qualif.
211     fn add(&mut self, qualif: Qualif) {
212         self.qualif = self.qualif | qualif;
213     }
214
215     /// Add the given type's qualification to self.qualif.
216     fn add_type(&mut self, ty: Ty<'tcx>) {
217         self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
218         self.qualif.restrict(ty, self.tcx, self.param_env);
219     }
220
221     /// Within the provided closure, self.qualif will start
222     /// out empty, and its value after the closure returns will
223     /// be combined with the value before the call to nest.
224     fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
225         let original = self.qualif;
226         self.qualif = Qualif::empty();
227         f(self);
228         self.add(original);
229     }
230
231     /// Check if a Local with the current qualifications is promotable.
232     fn can_promote(&mut self) -> bool {
233         // References to statics are allowed, but only in other statics.
234         if self.mode == Mode::Static || self.mode == Mode::StaticMut {
235             (self.qualif - Qualif::STATIC_REF).is_empty()
236         } else {
237             self.qualif.is_empty()
238         }
239     }
240
241     /// Check if a Place with the current qualifications could
242     /// be consumed, by either an operand or a Deref projection.
243     fn try_consume(&mut self) -> bool {
244         if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
245             let msg = if self.mode == Mode::Static ||
246                          self.mode == Mode::StaticMut {
247                 "cannot refer to other statics by value, use the \
248                  address-of operator or a constant instead"
249             } else {
250                 "cannot refer to statics by value, use a constant instead"
251             };
252             struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg)
253                 .span_label(self.span, "referring to another static by value")
254                 .note("use the address-of operator or a constant instead")
255                 .emit();
256
257             // Replace STATIC with NOT_CONST to avoid further errors.
258             self.qualif = self.qualif - Qualif::STATIC;
259             self.add(Qualif::NOT_CONST);
260
261             false
262         } else {
263             true
264         }
265     }
266
267     /// Assign the current qualification to the given destination.
268     fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
269         let qualif = self.qualif;
270         let span = self.span;
271         let store = |slot: &mut Option<Qualif>| {
272             if slot.is_some() {
273                 span_bug!(span, "multiple assignments to {:?}", dest);
274             }
275             *slot = Some(qualif);
276         };
277
278         // Only handle promotable temps in non-const functions.
279         if self.mode == Mode::Fn {
280             if let Place::Local(index) = *dest {
281                 if self.mir.local_kind(index) == LocalKind::Temp
282                 && self.temp_promotion_state[index].is_promotable() {
283                     debug!("store to promotable temp {:?}", index);
284                     store(&mut self.temp_qualif[index]);
285                 }
286             }
287             return;
288         }
289
290         match *dest {
291             Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
292                 debug!("store to temp {:?}", index);
293                 store(&mut self.temp_qualif[index])
294             }
295             Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
296                 debug!("store to return place {:?}", index);
297                 store(&mut self.return_qualif)
298             }
299
300             Place::Projection(box Projection {
301                 base: Place::Local(index),
302                 elem: ProjectionElem::Deref
303             }) if self.mir.local_kind(index) == LocalKind::Temp
304                && self.mir.local_decls[index].ty.is_box()
305                && self.temp_qualif[index].map_or(false, |qualif| {
306                     qualif.intersects(Qualif::NOT_CONST)
307                }) => {
308                 // Part of `box expr`, we should've errored
309                 // already for the Box allocation Rvalue.
310             }
311
312             // This must be an explicit assignment.
313             _ => {
314                 // Catch more errors in the destination.
315                 self.visit_place(dest, PlaceContext::Store, location);
316                 self.statement_like();
317             }
318         }
319     }
320
321     /// Qualify a whole const, static initializer or const fn.
322     fn qualify_const(&mut self) -> (Qualif, Rc<IdxSetBuf<Local>>) {
323         debug!("qualifying {} {:?}", self.mode, self.def_id);
324
325         let mir = self.mir;
326
327         let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
328         let mut bb = START_BLOCK;
329         loop {
330             seen_blocks.insert(bb.index());
331
332             self.visit_basic_block_data(bb, &mir[bb]);
333
334             let target = match mir[bb].terminator().kind {
335                 TerminatorKind::Goto { target } |
336                 TerminatorKind::Drop { target, .. } |
337                 TerminatorKind::Assert { target, .. } |
338                 TerminatorKind::Call { destination: Some((_, target)), .. } => {
339                     Some(target)
340                 }
341
342                 // Non-terminating calls cannot produce any value.
343                 TerminatorKind::Call { destination: None, .. } => {
344                     break;
345                 }
346
347                 TerminatorKind::SwitchInt {..} |
348                 TerminatorKind::DropAndReplace { .. } |
349                 TerminatorKind::Resume |
350                 TerminatorKind::Abort |
351                 TerminatorKind::GeneratorDrop |
352                 TerminatorKind::Yield { .. } |
353                 TerminatorKind::Unreachable |
354                 TerminatorKind::FalseEdges { .. } |
355                 TerminatorKind::FalseUnwind { .. } => None,
356
357                 TerminatorKind::Return => {
358                     // Check for unused values. This usually means
359                     // there are extra statements in the AST.
360                     for temp in mir.temps_iter() {
361                         if self.temp_qualif[temp].is_none() {
362                             continue;
363                         }
364
365                         let state = self.temp_promotion_state[temp];
366                         if let TempState::Defined { location, uses: 0 } = state {
367                             let data = &mir[location.block];
368                             let stmt_idx = location.statement_index;
369
370                             // Get the span for the initialization.
371                             let source_info = if stmt_idx < data.statements.len() {
372                                 data.statements[stmt_idx].source_info
373                             } else {
374                                 data.terminator().source_info
375                             };
376                             self.span = source_info.span;
377
378                             // Treat this as a statement in the AST.
379                             self.statement_like();
380                         }
381                     }
382
383                     // Make sure there are no extra unassigned variables.
384                     self.qualif = Qualif::NOT_CONST;
385                     for index in mir.vars_iter() {
386                         if !self.const_fn_arg_vars.contains(index.index()) {
387                             debug!("unassigned variable {:?}", index);
388                             self.assign(&Place::Local(index), Location {
389                                 block: bb,
390                                 statement_index: usize::MAX,
391                             });
392                         }
393                     }
394
395                     break;
396                 }
397             };
398
399             match target {
400                 // No loops allowed.
401                 Some(target) if !seen_blocks.contains(target.index()) => {
402                     bb = target;
403                 }
404                 _ => {
405                     self.not_const();
406                     break;
407                 }
408             }
409         }
410
411         self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
412
413         // Account for errors in consts by using the
414         // conservative type qualification instead.
415         if self.qualif.intersects(Qualif::CONST_ERROR) {
416             self.qualif = Qualif::empty();
417             let return_ty = mir.return_ty();
418             self.add_type(return_ty);
419         }
420
421
422         // Collect all the temps we need to promote.
423         let mut promoted_temps = IdxSetBuf::new_empty(self.temp_promotion_state.len());
424
425         for candidate in &self.promotion_candidates {
426             match *candidate {
427                 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
428                     match self.mir[bb].statements[stmt_idx].kind {
429                         StatementKind::Assign(_, Rvalue::Ref(_, _, Place::Local(index))) => {
430                             promoted_temps.add(&index);
431                         }
432                         _ => {}
433                     }
434                 }
435                 Candidate::Argument { .. } => {}
436             }
437         }
438
439         (self.qualif, Rc::new(promoted_temps))
440     }
441 }
442
443 /// Accumulates an Rvalue or Call's effects in self.qualif.
444 /// For functions (constant or not), it also records
445 /// candidates for promotion in promotion_candidates.
446 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
447     fn visit_local(&mut self,
448                    &local: &Local,
449                    _: PlaceContext<'tcx>,
450                    _: Location) {
451         let kind = self.mir.local_kind(local);
452         match kind {
453             LocalKind::ReturnPointer => {
454                 self.not_const();
455             }
456             LocalKind::Var => {
457                 self.add(Qualif::NOT_CONST);
458             }
459             LocalKind::Arg |
460             LocalKind::Temp => {
461                 if let LocalKind::Arg = kind {
462                     self.add(Qualif::FN_ARGUMENT);
463                 }
464
465                 if !self.temp_promotion_state[local].is_promotable() {
466                     self.add(Qualif::NOT_PROMOTABLE);
467                 }
468
469                 if let Some(qualif) = self.temp_qualif[local] {
470                     self.add(qualif);
471                 } else {
472                     self.not_const();
473                 }
474             }
475         }
476     }
477
478     fn visit_place(&mut self,
479                     place: &Place<'tcx>,
480                     context: PlaceContext<'tcx>,
481                     location: Location) {
482         match *place {
483             Place::Local(ref local) => self.visit_local(local, context, location),
484             Place::Static(ref global) => {
485                 self.add(Qualif::STATIC);
486
487                 if self.mode != Mode::Fn {
488                     for attr in &self.tcx.get_attrs(global.def_id)[..] {
489                         if attr.check_name("thread_local") {
490                             span_err!(self.tcx.sess, self.span, E0625,
491                                       "thread-local statics cannot be \
492                                        accessed at compile-time");
493                             self.add(Qualif::NOT_CONST);
494                             return;
495                         }
496                     }
497                 }
498
499                 if self.mode == Mode::Const || self.mode == Mode::ConstFn {
500                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
501                                                    "{}s cannot refer to statics, use \
502                                                     a constant instead", self.mode);
503                     if self.tcx.sess.teach(&err.get_code().unwrap()) {
504                         err.note(
505                             "Static and const variables can refer to other const variables. But a \
506                              const variable cannot refer to a static variable."
507                         );
508                         err.help(
509                             "To fix this, the value can be extracted as a const and then used."
510                         );
511                     }
512                     err.emit()
513                 }
514             }
515             Place::Projection(ref proj) => {
516                 self.nest(|this| {
517                     this.super_place(place, context, location);
518                     match proj.elem {
519                         ProjectionElem::Deref => {
520                             if !this.try_consume() {
521                                 return;
522                             }
523
524                             if this.qualif.intersects(Qualif::STATIC_REF) {
525                                 this.qualif = this.qualif - Qualif::STATIC_REF;
526                                 this.add(Qualif::STATIC);
527                             }
528
529                             this.add(Qualif::NOT_CONST);
530
531                             let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
532                             if let ty::TyRawPtr(_) = base_ty.sty {
533                                 if this.mode != Mode::Fn {
534                                     let mut err = struct_span_err!(
535                                         this.tcx.sess,
536                                         this.span,
537                                         E0396,
538                                         "raw pointers cannot be dereferenced in {}s",
539                                         this.mode
540                                     );
541                                     err.span_label(this.span,
542                                                    "dereference of raw pointer in constant");
543                                     if this.tcx.sess.teach(&err.get_code().unwrap()) {
544                                         err.note(
545                                             "The value behind a raw pointer can't be determined \
546                                              at compile-time (or even link-time), which means it \
547                                              can't be used in a constant expression."
548                                         );
549                                         err.help("A possible fix is to dereference your pointer \
550                                                   at some point in run-time.");
551                                     }
552                                     err.emit();
553                                 }
554                             }
555                         }
556
557                         ProjectionElem::Field(..) |
558                         ProjectionElem::Index(_) => {
559                             if this.mode != Mode::Fn &&
560                                this.qualif.intersects(Qualif::STATIC) {
561                                 span_err!(this.tcx.sess, this.span, E0494,
562                                           "cannot refer to the interior of another \
563                                            static, use a constant instead");
564                             }
565                             let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
566                             this.qualif.restrict(ty, this.tcx, this.param_env);
567                         }
568
569                         ProjectionElem::ConstantIndex {..} |
570                         ProjectionElem::Subslice {..} |
571                         ProjectionElem::Downcast(..) => {
572                             this.not_const()
573                         }
574                     }
575                 });
576             }
577         }
578     }
579
580     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
581         match *operand {
582             Operand::Copy(_) |
583             Operand::Move(_) => {
584                 self.nest(|this| {
585                     this.super_operand(operand, location);
586                     this.try_consume();
587                 });
588
589                 // Mark the consumed locals to indicate later drops are noops.
590                 if let Operand::Move(Place::Local(local)) = *operand {
591                     self.temp_qualif[local] = self.temp_qualif[local].map(|q|
592                         q - Qualif::NEEDS_DROP
593                     );
594                 }
595             }
596             Operand::Constant(ref constant) => {
597                 if let Literal::Value {
598                     value: &ty::Const { val: ConstVal::Unevaluated(def_id, _), ty }
599                 } = constant.literal {
600                     // Don't peek inside trait associated constants.
601                     if self.tcx.trait_of_item(def_id).is_some() {
602                         self.add_type(ty);
603                     } else {
604                         let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
605
606                         let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
607                         self.add(qualif);
608
609                         // Just in case the type is more specific than
610                         // the definition, e.g. impl associated const
611                         // with type parameters, take it into account.
612                         self.qualif.restrict(ty, self.tcx, self.param_env);
613                     }
614                 }
615             }
616         }
617     }
618
619     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
620         // Recurse through operands and places.
621         if let Rvalue::Ref(region, kind, ref place) = *rvalue {
622             let mut is_reborrow = false;
623             if let Place::Projection(ref proj) = *place {
624                 if let ProjectionElem::Deref = proj.elem {
625                     let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
626                     if let ty::TyRef(..) = base_ty.sty {
627                         is_reborrow = true;
628                     }
629                 }
630             }
631
632             if is_reborrow {
633                 self.nest(|this| {
634                     this.super_place(place, PlaceContext::Borrow {
635                         region,
636                         kind
637                     }, location);
638                     if !this.try_consume() {
639                         return;
640                     }
641
642                     if this.qualif.intersects(Qualif::STATIC_REF) {
643                         this.qualif = this.qualif - Qualif::STATIC_REF;
644                         this.add(Qualif::STATIC);
645                     }
646                 });
647             } else {
648                 self.super_rvalue(rvalue, location);
649             }
650         } else {
651             self.super_rvalue(rvalue, location);
652         }
653
654         match *rvalue {
655             Rvalue::Use(_) |
656             Rvalue::Repeat(..) |
657             Rvalue::UnaryOp(UnOp::Neg, _) |
658             Rvalue::UnaryOp(UnOp::Not, _) |
659             Rvalue::NullaryOp(NullOp::SizeOf, _) |
660             Rvalue::CheckedBinaryOp(..) |
661             Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
662             Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
663             Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
664             Rvalue::Cast(CastKind::Unsize, ..) |
665             Rvalue::Discriminant(..) => {}
666
667             Rvalue::Len(_) => {
668                 // Static places in consts would have errored already,
669                 // don't treat length checks as reads from statics.
670                 self.qualif = self.qualif - Qualif::STATIC;
671             }
672
673             Rvalue::Ref(_, kind, ref place) => {
674                 // Static places in consts would have errored already,
675                 // only keep track of references to them here.
676                 if self.qualif.intersects(Qualif::STATIC) {
677                     self.qualif = self.qualif - Qualif::STATIC;
678                     self.add(Qualif::STATIC_REF);
679                 }
680
681                 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
682                 if let BorrowKind::Mut { .. } = kind {
683                     // In theory, any zero-sized value could be borrowed
684                     // mutably without consequences. However, only &mut []
685                     // is allowed right now, and only in functions.
686                     let allow = if self.mode == Mode::StaticMut {
687                         // Inside a `static mut`, &mut [...] is also allowed.
688                         match ty.sty {
689                             ty::TyArray(..) | ty::TySlice(_) => true,
690                             _ => false
691                         }
692                     } else if let ty::TyArray(_, len) = ty.sty {
693                         len.val.to_const_int().unwrap().to_u64().unwrap() == 0 &&
694                             self.mode == Mode::Fn
695                     } else {
696                         false
697                     };
698
699                     if !allow {
700                         self.add(Qualif::NOT_CONST);
701                         if self.mode != Mode::Fn {
702                             let mut err = struct_span_err!(self.tcx.sess,  self.span, E0017,
703                                                            "references in {}s may only refer \
704                                                             to immutable values", self.mode);
705                             err.span_label(self.span, format!("{}s require immutable values",
706                                                                 self.mode));
707                             if self.tcx.sess.teach(&err.get_code().unwrap()) {
708                                 err.note("References in statics and constants may only refer to \
709                                           immutable values.\n\n\
710                                           Statics are shared everywhere, and if they refer to \
711                                           mutable data one might violate memory safety since \
712                                           holding multiple mutable references to shared data is \
713                                           not allowed.\n\n\
714                                           If you really want global mutable state, try using \
715                                           static mut or a global UnsafeCell.");
716                             }
717                             err.emit();
718                         }
719                     }
720                 } else {
721                     // Constants cannot be borrowed if they contain interior mutability as
722                     // it means that our "silent insertion of statics" could change
723                     // initializer values (very bad).
724                     if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
725                         // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
726                         // duplicate errors (from reborrowing, for example).
727                         self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
728                         self.add(Qualif::NOT_CONST);
729                         if self.mode != Mode::Fn {
730                             span_err!(self.tcx.sess, self.span, E0492,
731                                       "cannot borrow a constant which may contain \
732                                        interior mutability, create a static instead");
733                         }
734                     }
735                 }
736
737                 // We might have a candidate for promotion.
738                 let candidate = Candidate::Ref(location);
739                 if self.can_promote() {
740                     // We can only promote direct borrows of temps.
741                     if let Place::Local(local) = *place {
742                         if self.mir.local_kind(local) == LocalKind::Temp {
743                             self.promotion_candidates.push(candidate);
744                         }
745                     }
746                 }
747             }
748
749             Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
750                 let operand_ty = operand.ty(self.mir, self.tcx);
751                 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
752                 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
753                 match (cast_in, cast_out) {
754                     (CastTy::Ptr(_), CastTy::Int(_)) |
755                     (CastTy::FnPtr, CastTy::Int(_)) => {
756                         self.add(Qualif::NOT_CONST);
757                         if self.mode != Mode::Fn {
758                             let mut err = struct_span_err!(
759                                 self.tcx.sess,
760                                 self.span,
761                                 E0018,
762                                 "raw pointers cannot be cast to integers in {}s",
763                                 self.mode
764                             );
765                             if self.tcx.sess.teach(&err.get_code().unwrap()) {
766                                 err.note("\
767 The value of static and constant integers must be known at compile time. You can't cast a pointer \
768 to an integer because the address of a pointer can vary.
769
770 For example, if you write:
771
772 ```
773 static MY_STATIC: u32 = 42;
774 static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
775 static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
776 ```
777
778 Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However, the address can change \
779 when the program is linked, as well as change between different executions due to ASLR, and many \
780 linkers would not be able to calculate the value of `WHAT`.
781
782 On the other hand, static and constant pointers can point either to a known numeric address or to \
783 the address of a symbol.
784
785 ```
786 static MY_STATIC: u32 = 42;
787 static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
788 const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
789 ```
790
791 This does not pose a problem by itself because they can't be accessed directly.");
792                             }
793                             err.emit();
794                         }
795                     }
796                     _ => {}
797                 }
798             }
799
800             Rvalue::BinaryOp(op, ref lhs, _) => {
801                 if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
802                     assert!(op == BinOp::Eq || op == BinOp::Ne ||
803                             op == BinOp::Le || op == BinOp::Lt ||
804                             op == BinOp::Ge || op == BinOp::Gt ||
805                             op == BinOp::Offset);
806
807                     self.add(Qualif::NOT_CONST);
808                     if self.mode != Mode::Fn {
809                         struct_span_err!(
810                             self.tcx.sess, self.span, E0395,
811                             "raw pointers cannot be compared in {}s",
812                             self.mode)
813                         .span_label(
814                             self.span,
815                             "comparing raw pointers in static")
816                         .emit();
817                     }
818                 }
819             }
820
821             Rvalue::NullaryOp(NullOp::Box, _) => {
822                 self.add(Qualif::NOT_CONST);
823                 if self.mode != Mode::Fn {
824                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
825                                                    "allocations are not allowed in {}s", self.mode);
826                     err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
827                     if self.tcx.sess.teach(&err.get_code().unwrap()) {
828                         err.note(
829                             "The value of statics and constants must be known at compile time, \
830                              and they live for the entire lifetime of a program. Creating a boxed \
831                              value allocates memory on the heap at runtime, and therefore cannot \
832                              be done at compile time."
833                         );
834                     }
835                     err.emit();
836                 }
837             }
838
839             Rvalue::Aggregate(ref kind, _) => {
840                 if let AggregateKind::Adt(def, ..) = **kind {
841                     if def.has_dtor(self.tcx) {
842                         self.add(Qualif::NEEDS_DROP);
843                     }
844
845                     if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
846                         let ty = rvalue.ty(self.mir, self.tcx);
847                         self.add_type(ty);
848                         assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
849                     }
850                 }
851             }
852         }
853     }
854
855     fn visit_terminator_kind(&mut self,
856                              bb: BasicBlock,
857                              kind: &TerminatorKind<'tcx>,
858                              location: Location) {
859         if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
860             self.visit_operand(func, location);
861
862             let fn_ty = func.ty(self.mir, self.tcx);
863             let mut callee_def_id = None;
864             let (mut is_shuffle, mut is_const_fn) = (false, None);
865             if let ty::TyFnDef(def_id, _) = fn_ty.sty {
866                 callee_def_id = Some(def_id);
867                 match self.tcx.fn_sig(def_id).abi() {
868                     Abi::RustIntrinsic |
869                     Abi::PlatformIntrinsic => {
870                         assert!(!self.tcx.is_const_fn(def_id));
871                         match &self.tcx.item_name(def_id)[..] {
872                             "size_of" | "min_align_of" | "type_id" => is_const_fn = Some(def_id),
873
874                             name if name.starts_with("simd_shuffle") => {
875                                 is_shuffle = true;
876                             }
877
878                             _ => {}
879                         }
880                     }
881                     _ => {
882                         if self.tcx.is_const_fn(def_id) {
883                             is_const_fn = Some(def_id);
884                         }
885                     }
886                 }
887             }
888
889             let constant_arguments = callee_def_id.and_then(|id| {
890                 args_required_const(self.tcx, id)
891             });
892             for (i, arg) in args.iter().enumerate() {
893                 self.nest(|this| {
894                     this.visit_operand(arg, location);
895                     if this.mode != Mode::Fn {
896                         return
897                     }
898                     let candidate = Candidate::Argument { bb, index: i };
899                     if is_shuffle && i == 2 {
900                         if this.can_promote() {
901                             this.promotion_candidates.push(candidate);
902                         } else {
903                             span_err!(this.tcx.sess, this.span, E0526,
904                                       "shuffle indices are not constant");
905                         }
906                         return
907                     }
908
909                     let constant_arguments = match constant_arguments.as_ref() {
910                         Some(s) => s,
911                         None => return,
912                     };
913                     if !constant_arguments.contains(&i) {
914                         return
915                     }
916                     if this.can_promote() {
917                         this.promotion_candidates.push(candidate);
918                     } else {
919                         this.tcx.sess.span_err(this.span,
920                             &format!("argument {} is required to be a constant",
921                                      i + 1));
922                     }
923                 });
924             }
925
926             // Const fn calls.
927             if let Some(def_id) = is_const_fn {
928                 // find corresponding rustc_const_unstable feature
929                 if let Some(&attr::Stability {
930                     rustc_const_unstable: Some(attr::RustcConstUnstable {
931                         feature: ref feature_name
932                     }),
933                 .. }) = self.tcx.lookup_stability(def_id) {
934
935                     // We are in a const or static initializer,
936                     if self.mode != Mode::Fn &&
937
938                         // feature-gate is not enabled,
939                         !self.tcx.sess.features.borrow()
940                             .declared_lib_features
941                             .iter()
942                             .any(|&(ref sym, _)| sym == feature_name) &&
943
944                         // this doesn't come from a crate with the feature-gate enabled,
945                         self.def_id.is_local() &&
946
947                         // this doesn't come from a macro that has #[allow_internal_unstable]
948                         !self.span.allows_unstable()
949                     {
950                         let mut err = self.tcx.sess.struct_span_err(self.span,
951                             &format!("`{}` is not yet stable as a const fn",
952                                      self.tcx.item_path_str(def_id)));
953                         help!(&mut err,
954                               "in Nightly builds, add `#![feature({})]` \
955                                to the crate attributes to enable",
956                               feature_name);
957                         err.emit();
958                     }
959                 }
960             } else {
961                 self.qualif = Qualif::NOT_CONST;
962                 if self.mode != Mode::Fn {
963                     // FIXME(#24111) Remove this check when const fn stabilizes
964                     let (msg, note) = if let UnstableFeatures::Disallow =
965                             self.tcx.sess.opts.unstable_features {
966                         (format!("calls in {}s are limited to \
967                                   struct and enum constructors",
968                                  self.mode),
969                          Some("a limited form of compile-time function \
970                                evaluation is available on a nightly \
971                                compiler via `const fn`"))
972                     } else {
973                         (format!("calls in {}s are limited \
974                                   to constant functions, \
975                                   struct and enum constructors",
976                                  self.mode),
977                          None)
978                     };
979                     let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
980                     if let Some(note) = note {
981                         err.span_note(self.span, note);
982                     }
983                     err.emit();
984                 }
985             }
986
987             if let Some((ref dest, _)) = *destination {
988                 // Avoid propagating irrelevant callee/argument qualifications.
989                 if self.qualif.intersects(Qualif::CONST_ERROR) {
990                     self.qualif = Qualif::NOT_CONST;
991                 } else {
992                     // Be conservative about the returned value of a const fn.
993                     let tcx = self.tcx;
994                     let ty = dest.ty(self.mir, tcx).to_ty(tcx);
995                     self.qualif = Qualif::empty();
996                     self.add_type(ty);
997                 }
998                 self.assign(dest, location);
999             }
1000         } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1001             self.super_terminator_kind(bb, kind, location);
1002
1003             // Deny *any* live drops anywhere other than functions.
1004             if self.mode != Mode::Fn {
1005                 // HACK(eddyb) Emulate a bit of dataflow analysis,
1006                 // conservatively, that drop elaboration will do.
1007                 let needs_drop = if let Place::Local(local) = *place {
1008                     if self.temp_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
1009                         Some(self.mir.local_decls[local].source_info.span)
1010                     } else {
1011                         None
1012                     }
1013                 } else {
1014                     Some(self.span)
1015                 };
1016
1017                 if let Some(span) = needs_drop {
1018                     // Double-check the type being dropped, to minimize false positives.
1019                     let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1020                     if ty.needs_drop(self.tcx, self.param_env) {
1021                         struct_span_err!(self.tcx.sess, span, E0493,
1022                                          "destructors cannot be evaluated at compile-time")
1023                             .span_label(span, format!("{}s cannot evaluate destructors",
1024                                                       self.mode))
1025                             .emit();
1026                     }
1027                 }
1028             }
1029         } else {
1030             // Qualify any operands inside other terminators.
1031             self.super_terminator_kind(bb, kind, location);
1032         }
1033     }
1034
1035     fn visit_assign(&mut self,
1036                     _: BasicBlock,
1037                     dest: &Place<'tcx>,
1038                     rvalue: &Rvalue<'tcx>,
1039                     location: Location) {
1040         self.visit_rvalue(rvalue, location);
1041
1042         // Check the allowed const fn argument forms.
1043         if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
1044             if self.mir.local_kind(index) == LocalKind::Var &&
1045                self.const_fn_arg_vars.insert(index.index()) {
1046
1047                 // Direct use of an argument is permitted.
1048                 match *rvalue {
1049                     Rvalue::Use(Operand::Copy(Place::Local(local))) |
1050                     Rvalue::Use(Operand::Move(Place::Local(local))) => {
1051                         if self.mir.local_kind(local) == LocalKind::Arg {
1052                             return;
1053                         }
1054                     }
1055                     _ => {}
1056                 }
1057
1058                 // Avoid a generic error for other uses of arguments.
1059                 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
1060                     let decl = &self.mir.local_decls[index];
1061                     let mut err = struct_span_err!(
1062                         self.tcx.sess,
1063                         decl.source_info.span,
1064                         E0022,
1065                         "arguments of constant functions can only be immutable by-value bindings"
1066                     );
1067                     if self.tcx.sess.teach(&err.get_code().unwrap()) {
1068                         err.note("Constant functions are not allowed to mutate anything. Thus, \
1069                                   binding to an argument with a mutable pattern is not allowed.");
1070                         err.note("Remove any mutable bindings from the argument list to fix this \
1071                                   error. In case you need to mutate the argument, try lazily \
1072                                   initializing a global variable instead of using a const fn, or \
1073                                   refactoring the code to a functional style to avoid mutation if \
1074                                   possible.");
1075                     }
1076                     err.emit();
1077                     return;
1078                 }
1079             }
1080         }
1081
1082         self.assign(dest, location);
1083     }
1084
1085     fn visit_source_info(&mut self, source_info: &SourceInfo) {
1086         self.span = source_info.span;
1087     }
1088
1089     fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1090         self.nest(|this| {
1091             this.visit_source_info(&statement.source_info);
1092             match statement.kind {
1093                 StatementKind::Assign(ref place, ref rvalue) => {
1094                     this.visit_assign(bb, place, rvalue, location);
1095                 }
1096                 StatementKind::SetDiscriminant { .. } |
1097                 StatementKind::StorageLive(_) |
1098                 StatementKind::StorageDead(_) |
1099                 StatementKind::InlineAsm {..} |
1100                 StatementKind::EndRegion(_) |
1101                 StatementKind::Validate(..) |
1102                 StatementKind::Nop => {}
1103             }
1104         });
1105     }
1106
1107     fn visit_terminator(&mut self,
1108                         bb: BasicBlock,
1109                         terminator: &Terminator<'tcx>,
1110                         location: Location) {
1111         self.nest(|this| this.super_terminator(bb, terminator, location));
1112     }
1113 }
1114
1115 pub fn provide(providers: &mut Providers) {
1116     *providers = Providers {
1117         mir_const_qualif,
1118         ..*providers
1119     };
1120 }
1121
1122 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1123                               def_id: DefId)
1124                               -> (u8, Rc<IdxSetBuf<Local>>) {
1125     // NB: This `borrow()` is guaranteed to be valid (i.e., the value
1126     // cannot yet be stolen), because `mir_validated()`, which steals
1127     // from `mir_const(), forces this query to execute before
1128     // performing the steal.
1129     let mir = &tcx.mir_const(def_id).borrow();
1130
1131     if mir.return_ty().references_error() {
1132         tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1133         return (Qualif::NOT_CONST.bits(), Rc::new(IdxSetBuf::new_empty(0)));
1134     }
1135
1136     let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const);
1137     let (qualif, promoted_temps) = qualifier.qualify_const();
1138     (qualif.bits(), promoted_temps)
1139 }
1140
1141 pub struct QualifyAndPromoteConstants;
1142
1143 impl MirPass for QualifyAndPromoteConstants {
1144     fn run_pass<'a, 'tcx>(&self,
1145                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
1146                           src: MirSource,
1147                           mir: &mut Mir<'tcx>) {
1148         // There's not really any point in promoting errorful MIR.
1149         if mir.return_ty().references_error() {
1150             tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1151             return;
1152         }
1153
1154         if src.promoted.is_some() {
1155             return;
1156         }
1157
1158         let def_id = src.def_id;
1159         let id = tcx.hir.as_local_node_id(def_id).unwrap();
1160         let mut const_promoted_temps = None;
1161         let mode = match tcx.hir.body_owner_kind(id) {
1162             hir::BodyOwnerKind::Fn => {
1163                 if tcx.is_const_fn(def_id) {
1164                     Mode::ConstFn
1165                 } else {
1166                     Mode::Fn
1167                 }
1168             }
1169             hir::BodyOwnerKind::Const => {
1170                 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1171                 Mode::Const
1172             }
1173             hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1174             hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1175         };
1176
1177         if mode == Mode::Fn || mode == Mode::ConstFn {
1178             // This is ugly because Qualifier holds onto mir,
1179             // which can't be mutated until its scope ends.
1180             let (temps, candidates) = {
1181                 let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
1182                 if mode == Mode::ConstFn {
1183                     // Enforce a constant-like CFG for `const fn`.
1184                     qualifier.qualify_const();
1185                 } else {
1186                     while let Some((bb, data)) = qualifier.rpo.next() {
1187                         qualifier.visit_basic_block_data(bb, data);
1188                     }
1189                 }
1190
1191                 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1192             };
1193
1194             // Do the actual promotion, now that we know what's viable.
1195             promote_consts::promote_candidates(mir, tcx, temps, candidates);
1196         } else {
1197             let promoted_temps = if mode == Mode::Const {
1198                 // Already computed by `mir_const_qualif`.
1199                 const_promoted_temps.unwrap()
1200             } else {
1201                 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1202             };
1203
1204             // In `const` and `static` everything without `StorageDead`
1205             // is `'static`, we don't have to create promoted MIR fragments,
1206             // just remove `Drop` and `StorageDead` on "promoted" locals.
1207             for block in mir.basic_blocks_mut() {
1208                 block.statements.retain(|statement| {
1209                     match statement.kind {
1210                         StatementKind::StorageDead(index) => {
1211                             !promoted_temps.contains(&index)
1212                         }
1213                         _ => true
1214                     }
1215                 });
1216                 let terminator = block.terminator_mut();
1217                 match terminator.kind {
1218                     TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1219                         if promoted_temps.contains(&index) {
1220                             terminator.kind = TerminatorKind::Goto {
1221                                 target,
1222                             };
1223                         }
1224                     }
1225                     _ => {}
1226                 }
1227             }
1228         }
1229
1230         // Statics must be Sync.
1231         if mode == Mode::Static {
1232             // `#[thread_local]` statics don't have to be `Sync`.
1233             for attr in &tcx.get_attrs(def_id)[..] {
1234                 if attr.check_name("thread_local") {
1235                     return;
1236                 }
1237             }
1238             let ty = mir.return_ty();
1239             tcx.infer_ctxt().enter(|infcx| {
1240                 let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
1241                 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1242                 let mut fulfillment_cx = traits::FulfillmentContext::new();
1243                 fulfillment_cx.register_bound(&infcx,
1244                                               param_env,
1245                                               ty,
1246                                               tcx.require_lang_item(lang_items::SyncTraitLangItem),
1247                                               cause);
1248                 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1249                     infcx.report_fulfillment_errors(&err, None);
1250                 }
1251             });
1252         }
1253     }
1254 }
1255
1256 fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
1257     let attrs = tcx.get_attrs(def_id);
1258     let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1259     let mut ret = FxHashSet();
1260     for meta in attr.meta_item_list()? {
1261         match meta.literal()?.node {
1262             LitKind::Int(a, _) => { ret.insert(a as usize); }
1263             _ => return None,
1264         }
1265     }
1266     Some(ret)
1267 }