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