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