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