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