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