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.
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.
11 //! A pass that qualifies constness of temporaries in constants,
12 //! static initializers and functions and also drives promotion.
14 //! The Qualif flags below can be used to also provide better
15 //! diagnostics as to why a constant rvalue wasn't promoted.
17 use rustc_data_structures::bitvec::BitVector;
18 use rustc_data_structures::indexed_set::IdxSetBuf;
19 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
20 use rustc_data_structures::fx::FxHashSet;
22 use rustc::hir::def_id::DefId;
23 use rustc::middle::const_val::ConstVal;
24 use rustc::traits::{self, Reveal};
25 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
26 use rustc::ty::cast::CastTy;
27 use rustc::ty::maps::Providers;
29 use rustc::mir::traversal::ReversePostorder;
30 use rustc::mir::visit::{PlaceContext, Visitor};
31 use rustc::middle::lang_items;
34 use syntax::ast::LitKind;
35 use syntax::feature_gate::UnstableFeatures;
36 use syntax_pos::{Span, DUMMY_SP};
39 use rustc_data_structures::sync::Lrc;
42 use transform::{MirPass, MirSource};
43 use super::promote_consts::{self, Candidate, TempState};
46 // Borrows of temporaries can be promoted only if
47 // they have none of these qualifications, with
48 // the exception of `STATIC_REF` (in statics only).
50 // Constant containing interior mutability (UnsafeCell).
51 const MUTABLE_INTERIOR = 1 << 0;
53 // Constant containing an ADT that implements Drop.
54 const NEEDS_DROP = 1 << 1;
57 const FN_ARGUMENT = 1 << 2;
59 // Static place or move from a static.
60 const STATIC = 1 << 3;
62 // Reference to a static.
63 const STATIC_REF = 1 << 4;
65 // Not constant at all - non-`const fn` calls, asm!,
66 // pointer comparisons, ptr-to-int casts, etc.
67 const NOT_CONST = 1 << 5;
69 // Refers to temporaries which cannot be promoted as
70 // promote_consts decided they weren't simple enough.
71 const NOT_PROMOTABLE = 1 << 6;
73 // Const items can only have MUTABLE_INTERIOR
74 // and NOT_PROMOTABLE without producing an error.
75 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
76 !Qualif::NOT_PROMOTABLE.bits;
80 impl<'a, 'tcx> Qualif {
81 /// Remove flags which are impossible for the given type.
82 fn restrict(&mut self, ty: Ty<'tcx>,
83 tcx: TyCtxt<'a, 'tcx, 'tcx>,
84 param_env: ty::ParamEnv<'tcx>) {
85 if ty.is_freeze(tcx, param_env, DUMMY_SP) {
86 *self = *self - Qualif::MUTABLE_INTERIOR;
88 if !ty.needs_drop(tcx, param_env) {
89 *self = *self - Qualif::NEEDS_DROP;
94 /// What kind of item we are in.
95 #[derive(Copy, Clone, PartialEq, Eq)]
104 impl fmt::Display for Mode {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107 Mode::Const => write!(f, "constant"),
108 Mode::Static | Mode::StaticMut => write!(f, "static"),
109 Mode::ConstFn => write!(f, "constant function"),
110 Mode::Fn => write!(f, "function")
115 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
120 rpo: ReversePostorder<'a, 'tcx>,
121 tcx: TyCtxt<'a, 'gcx, 'tcx>,
122 param_env: ty::ParamEnv<'tcx>,
123 temp_qualif: IndexVec<Local, Option<Qualif>>,
124 return_qualif: Option<Qualif>,
126 const_fn_arg_vars: BitVector,
127 temp_promotion_state: IndexVec<Local, TempState>,
128 promotion_candidates: Vec<Candidate>
131 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
132 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
136 -> Qualifier<'a, 'tcx, 'tcx> {
137 let mut rpo = traversal::reverse_postorder(mir);
138 let temps = promote_consts::collect_temps(mir, &mut rpo);
141 let param_env = tcx.param_env(def_id);
143 let mut temp_qualif = IndexVec::from_elem(None, &mir.local_decls);
144 for arg in mir.args_iter() {
145 let mut qualif = Qualif::NEEDS_DROP;
146 qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
147 temp_qualif[arg] = Some(qualif);
160 qualif: Qualif::empty(),
161 const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
162 temp_promotion_state: temps,
163 promotion_candidates: vec![]
167 // FIXME(eddyb) we could split the errors into meaningful
168 // categories, but enabling full miri would make that
169 // slightly pointless (even with feature-gating).
170 fn not_const(&mut self) {
171 self.add(Qualif::NOT_CONST);
172 if self.mode != Mode::Fn {
173 let mut err = struct_span_err!(
177 "{} contains unimplemented expression type",
180 if self.tcx.sess.teach(&err.get_code().unwrap()) {
181 err.note("A function call isn't allowed in the const's initialization expression \
182 because the expression's value must be known at compile-time.");
183 err.note("Remember: you can't use a function call inside a const's initialization \
184 expression! However, you can use it anywhere else.");
190 /// Error about extra statements in a constant.
191 fn statement_like(&mut self) {
192 self.add(Qualif::NOT_CONST);
193 if self.mode != Mode::Fn {
194 let mut err = struct_span_err!(
198 "blocks in {}s are limited to items and tail expressions",
201 if self.tcx.sess.teach(&err.get_code().unwrap()) {
202 err.note("Blocks in constants may only contain items (such as constant, function \
203 definition, etc...) and a tail expression.");
204 err.help("To avoid it, you have to replace the non-item object.");
210 /// Add the given qualification to self.qualif.
211 fn add(&mut self, qualif: Qualif) {
212 self.qualif = self.qualif | qualif;
215 /// Add the given type's qualification to self.qualif.
216 fn add_type(&mut self, ty: Ty<'tcx>) {
217 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
218 self.qualif.restrict(ty, self.tcx, self.param_env);
221 /// Within the provided closure, self.qualif will start
222 /// out empty, and its value after the closure returns will
223 /// be combined with the value before the call to nest.
224 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
225 let original = self.qualif;
226 self.qualif = Qualif::empty();
231 /// Check if a Local with the current qualifications is promotable.
232 fn can_promote(&mut self) -> bool {
233 // References to statics are allowed, but only in other statics.
234 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
235 (self.qualif - Qualif::STATIC_REF).is_empty()
237 self.qualif.is_empty()
241 /// Check if a Place with the current qualifications could
242 /// be consumed, by either an operand or a Deref projection.
243 fn try_consume(&mut self) -> bool {
244 if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
245 let msg = if self.mode == Mode::Static ||
246 self.mode == Mode::StaticMut {
247 "cannot refer to other statics by value, use the \
248 address-of operator or a constant instead"
250 "cannot refer to statics by value, use a constant instead"
252 struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg)
253 .span_label(self.span, "referring to another static by value")
254 .note("use the address-of operator or a constant instead")
257 // Replace STATIC with NOT_CONST to avoid further errors.
258 self.qualif = self.qualif - Qualif::STATIC;
259 self.add(Qualif::NOT_CONST);
267 /// Assign the current qualification to the given destination.
268 fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
269 let qualif = self.qualif;
270 let span = self.span;
271 let store = |slot: &mut Option<Qualif>| {
273 span_bug!(span, "multiple assignments to {:?}", dest);
275 *slot = Some(qualif);
278 // Only handle promotable temps in non-const functions.
279 if self.mode == Mode::Fn {
280 if let Place::Local(index) = *dest {
281 if self.mir.local_kind(index) == LocalKind::Temp
282 && self.temp_promotion_state[index].is_promotable() {
283 debug!("store to promotable temp {:?}", index);
284 store(&mut self.temp_qualif[index]);
291 Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
292 debug!("store to temp {:?}", index);
293 store(&mut self.temp_qualif[index])
295 Place::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
296 debug!("store to return place {:?}", index);
297 store(&mut self.return_qualif)
300 Place::Projection(box Projection {
301 base: Place::Local(index),
302 elem: ProjectionElem::Deref
303 }) if self.mir.local_kind(index) == LocalKind::Temp
304 && self.mir.local_decls[index].ty.is_box()
305 && self.temp_qualif[index].map_or(false, |qualif| {
306 qualif.intersects(Qualif::NOT_CONST)
308 // Part of `box expr`, we should've errored
309 // already for the Box allocation Rvalue.
312 // This must be an explicit assignment.
314 // Catch more errors in the destination.
315 self.visit_place(dest, PlaceContext::Store, location);
316 self.statement_like();
321 /// Qualify a whole const, static initializer or const fn.
322 fn qualify_const(&mut self) -> (Qualif, Lrc<IdxSetBuf<Local>>) {
323 debug!("qualifying {} {:?}", self.mode, self.def_id);
327 let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
328 let mut bb = START_BLOCK;
330 seen_blocks.insert(bb.index());
332 self.visit_basic_block_data(bb, &mir[bb]);
334 let target = match mir[bb].terminator().kind {
335 TerminatorKind::Goto { target } |
336 TerminatorKind::Drop { target, .. } |
337 TerminatorKind::Assert { target, .. } |
338 TerminatorKind::Call { destination: Some((_, target)), .. } => {
342 // Non-terminating calls cannot produce any value.
343 TerminatorKind::Call { destination: None, .. } => {
347 TerminatorKind::SwitchInt {..} |
348 TerminatorKind::DropAndReplace { .. } |
349 TerminatorKind::Resume |
350 TerminatorKind::Abort |
351 TerminatorKind::GeneratorDrop |
352 TerminatorKind::Yield { .. } |
353 TerminatorKind::Unreachable |
354 TerminatorKind::FalseEdges { .. } |
355 TerminatorKind::FalseUnwind { .. } => None,
357 TerminatorKind::Return => {
358 // Check for unused values. This usually means
359 // there are extra statements in the AST.
360 for temp in mir.temps_iter() {
361 if self.temp_qualif[temp].is_none() {
365 let state = self.temp_promotion_state[temp];
366 if let TempState::Defined { location, uses: 0 } = state {
367 let data = &mir[location.block];
368 let stmt_idx = location.statement_index;
370 // Get the span for the initialization.
371 let source_info = if stmt_idx < data.statements.len() {
372 data.statements[stmt_idx].source_info
374 data.terminator().source_info
376 self.span = source_info.span;
378 // Treat this as a statement in the AST.
379 self.statement_like();
383 // Make sure there are no extra unassigned variables.
384 self.qualif = Qualif::NOT_CONST;
385 for index in mir.vars_iter() {
386 if !self.const_fn_arg_vars.contains(index.index()) {
387 debug!("unassigned variable {:?}", index);
388 self.assign(&Place::Local(index), Location {
390 statement_index: usize::MAX,
401 Some(target) if !seen_blocks.contains(target.index()) => {
411 self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
413 // Account for errors in consts by using the
414 // conservative type qualification instead.
415 if self.qualif.intersects(Qualif::CONST_ERROR) {
416 self.qualif = Qualif::empty();
417 let return_ty = mir.return_ty();
418 self.add_type(return_ty);
422 // Collect all the temps we need to promote.
423 let mut promoted_temps = IdxSetBuf::new_empty(self.temp_promotion_state.len());
425 for candidate in &self.promotion_candidates {
427 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
428 match self.mir[bb].statements[stmt_idx].kind {
429 StatementKind::Assign(_, Rvalue::Ref(_, _, Place::Local(index))) => {
430 promoted_temps.add(&index);
435 Candidate::Argument { .. } => {}
439 (self.qualif, Lrc::new(promoted_temps))
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,
449 _: PlaceContext<'tcx>,
451 let kind = self.mir.local_kind(local);
453 LocalKind::ReturnPointer => {
457 self.add(Qualif::NOT_CONST);
461 if let LocalKind::Arg = kind {
462 self.add(Qualif::FN_ARGUMENT);
465 if !self.temp_promotion_state[local].is_promotable() {
466 self.add(Qualif::NOT_PROMOTABLE);
469 if let Some(qualif) = self.temp_qualif[local] {
478 fn visit_place(&mut self,
480 context: PlaceContext<'tcx>,
481 location: Location) {
483 Place::Local(ref local) => self.visit_local(local, context, location),
484 Place::Static(ref global) => {
485 self.add(Qualif::STATIC);
487 if self.mode != Mode::Fn {
488 for attr in &self.tcx.get_attrs(global.def_id)[..] {
489 if attr.check_name("thread_local") {
490 span_err!(self.tcx.sess, self.span, E0625,
491 "thread-local statics cannot be \
492 accessed at compile-time");
493 self.add(Qualif::NOT_CONST);
499 if self.mode == Mode::Const || self.mode == Mode::ConstFn {
500 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
501 "{}s cannot refer to statics, use \
502 a constant instead", self.mode);
503 if self.tcx.sess.teach(&err.get_code().unwrap()) {
505 "Static and const variables can refer to other const variables. But a \
506 const variable cannot refer to a static variable."
509 "To fix this, the value can be extracted as a const and then used."
515 Place::Projection(ref proj) => {
517 this.super_place(place, context, location);
519 ProjectionElem::Deref => {
520 if !this.try_consume() {
524 if this.qualif.intersects(Qualif::STATIC_REF) {
525 this.qualif = this.qualif - Qualif::STATIC_REF;
526 this.add(Qualif::STATIC);
529 this.add(Qualif::NOT_CONST);
531 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
532 if let ty::TyRawPtr(_) = base_ty.sty {
533 if this.mode != Mode::Fn {
534 let mut err = struct_span_err!(
538 "raw pointers cannot be dereferenced in {}s",
541 err.span_label(this.span,
542 "dereference of raw pointer in constant");
543 if this.tcx.sess.teach(&err.get_code().unwrap()) {
545 "The value behind a raw pointer can't be determined \
546 at compile-time (or even link-time), which means it \
547 can't be used in a constant expression."
549 err.help("A possible fix is to dereference your pointer \
550 at some point in run-time.");
557 ProjectionElem::Field(..) |
558 ProjectionElem::Index(_) => {
559 if this.mode != Mode::Fn &&
560 this.qualif.intersects(Qualif::STATIC) {
561 span_err!(this.tcx.sess, this.span, E0494,
562 "cannot refer to the interior of another \
563 static, use a constant instead");
565 let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
566 this.qualif.restrict(ty, this.tcx, this.param_env);
569 ProjectionElem::ConstantIndex {..} |
570 ProjectionElem::Subslice {..} |
571 ProjectionElem::Downcast(..) => {
580 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
583 Operand::Move(_) => {
585 this.super_operand(operand, location);
589 // Mark the consumed locals to indicate later drops are noops.
590 if let Operand::Move(Place::Local(local)) = *operand {
591 self.temp_qualif[local] = self.temp_qualif[local].map(|q|
592 q - Qualif::NEEDS_DROP
596 Operand::Constant(ref constant) => {
597 if let Literal::Value {
598 value: &ty::Const { val: ConstVal::Unevaluated(def_id, _), ty }
599 } = constant.literal {
600 // Don't peek inside trait associated constants.
601 if self.tcx.trait_of_item(def_id).is_some() {
604 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
606 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
609 // Just in case the type is more specific than
610 // the definition, e.g. impl associated const
611 // with type parameters, take it into account.
612 self.qualif.restrict(ty, self.tcx, self.param_env);
619 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
620 // Recurse through operands and places.
621 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
622 let mut is_reborrow = false;
623 if let Place::Projection(ref proj) = *place {
624 if let ProjectionElem::Deref = proj.elem {
625 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
626 if let ty::TyRef(..) = base_ty.sty {
634 this.super_place(place, PlaceContext::Borrow {
638 if !this.try_consume() {
642 if this.qualif.intersects(Qualif::STATIC_REF) {
643 this.qualif = this.qualif - Qualif::STATIC_REF;
644 this.add(Qualif::STATIC);
648 self.super_rvalue(rvalue, location);
651 self.super_rvalue(rvalue, location);
657 Rvalue::UnaryOp(UnOp::Neg, _) |
658 Rvalue::UnaryOp(UnOp::Not, _) |
659 Rvalue::NullaryOp(NullOp::SizeOf, _) |
660 Rvalue::CheckedBinaryOp(..) |
661 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
662 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
663 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
664 Rvalue::Cast(CastKind::Unsize, ..) |
665 Rvalue::Discriminant(..) => {}
668 // Static places in consts would have errored already,
669 // don't treat length checks as reads from statics.
670 self.qualif = self.qualif - Qualif::STATIC;
673 Rvalue::Ref(_, kind, ref place) => {
674 // Static places in consts would have errored already,
675 // only keep track of references to them here.
676 if self.qualif.intersects(Qualif::STATIC) {
677 self.qualif = self.qualif - Qualif::STATIC;
678 self.add(Qualif::STATIC_REF);
681 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
682 if let BorrowKind::Mut { .. } = kind {
683 // In theory, any zero-sized value could be borrowed
684 // mutably without consequences. However, only &mut []
685 // is allowed right now, and only in functions.
686 let allow = if self.mode == Mode::StaticMut {
687 // Inside a `static mut`, &mut [...] is also allowed.
689 ty::TyArray(..) | ty::TySlice(_) => true,
692 } else if let ty::TyArray(_, len) = ty.sty {
693 len.val.to_const_int().unwrap().to_u64().unwrap() == 0 &&
694 self.mode == Mode::Fn
700 self.add(Qualif::NOT_CONST);
701 if self.mode != Mode::Fn {
702 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
703 "references in {}s may only refer \
704 to immutable values", self.mode);
705 err.span_label(self.span, format!("{}s require immutable values",
707 if self.tcx.sess.teach(&err.get_code().unwrap()) {
708 err.note("References in statics and constants may only refer to \
709 immutable values.\n\n\
710 Statics are shared everywhere, and if they refer to \
711 mutable data one might violate memory safety since \
712 holding multiple mutable references to shared data is \
714 If you really want global mutable state, try using \
715 static mut or a global UnsafeCell.");
721 // Constants cannot be borrowed if they contain interior mutability as
722 // it means that our "silent insertion of statics" could change
723 // initializer values (very bad).
724 if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
725 // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
726 // duplicate errors (from reborrowing, for example).
727 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
728 self.add(Qualif::NOT_CONST);
729 if self.mode != Mode::Fn {
730 span_err!(self.tcx.sess, self.span, E0492,
731 "cannot borrow a constant which may contain \
732 interior mutability, create a static instead");
737 // We might have a candidate for promotion.
738 let candidate = Candidate::Ref(location);
739 if self.can_promote() {
740 // We can only promote direct borrows of temps.
741 if let Place::Local(local) = *place {
742 if self.mir.local_kind(local) == LocalKind::Temp {
743 self.promotion_candidates.push(candidate);
749 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
750 let operand_ty = operand.ty(self.mir, self.tcx);
751 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
752 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
753 match (cast_in, cast_out) {
754 (CastTy::Ptr(_), CastTy::Int(_)) |
755 (CastTy::FnPtr, CastTy::Int(_)) => {
756 self.add(Qualif::NOT_CONST);
757 if self.mode != Mode::Fn {
758 let mut err = struct_span_err!(
762 "raw pointers cannot be cast to integers in {}s",
765 if self.tcx.sess.teach(&err.get_code().unwrap()) {
767 The value of static and constant integers must be known at compile time. You can't cast a pointer \
768 to an integer because the address of a pointer can vary.
770 For example, if you write:
773 static MY_STATIC: u32 = 42;
774 static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
775 static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
778 Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However, the address can change \
779 when the program is linked, as well as change between different executions due to ASLR, and many \
780 linkers would not be able to calculate the value of `WHAT`.
782 On the other hand, static and constant pointers can point either to a known numeric address or to \
783 the address of a symbol.
786 static MY_STATIC: u32 = 42;
787 static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
788 const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
791 This does not pose a problem by itself because they can't be accessed directly.");
800 Rvalue::BinaryOp(op, ref lhs, _) => {
801 if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
802 assert!(op == BinOp::Eq || op == BinOp::Ne ||
803 op == BinOp::Le || op == BinOp::Lt ||
804 op == BinOp::Ge || op == BinOp::Gt ||
805 op == BinOp::Offset);
807 self.add(Qualif::NOT_CONST);
808 if self.mode != Mode::Fn {
810 self.tcx.sess, self.span, E0395,
811 "raw pointers cannot be compared in {}s",
815 "comparing raw pointers in static")
821 Rvalue::NullaryOp(NullOp::Box, _) => {
822 self.add(Qualif::NOT_CONST);
823 if self.mode != Mode::Fn {
824 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
825 "allocations are not allowed in {}s", self.mode);
826 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
827 if self.tcx.sess.teach(&err.get_code().unwrap()) {
829 "The value of statics and constants must be known at compile time, \
830 and they live for the entire lifetime of a program. Creating a boxed \
831 value allocates memory on the heap at runtime, and therefore cannot \
832 be done at compile time."
839 Rvalue::Aggregate(ref kind, _) => {
840 if let AggregateKind::Adt(def, ..) = **kind {
841 if def.has_dtor(self.tcx) {
842 self.add(Qualif::NEEDS_DROP);
845 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
846 let ty = rvalue.ty(self.mir, self.tcx);
848 assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
855 fn visit_terminator_kind(&mut self,
857 kind: &TerminatorKind<'tcx>,
858 location: Location) {
859 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
860 self.visit_operand(func, location);
862 let fn_ty = func.ty(self.mir, self.tcx);
863 let mut callee_def_id = None;
864 let (mut is_shuffle, mut is_const_fn) = (false, None);
865 if let ty::TyFnDef(def_id, _) = fn_ty.sty {
866 callee_def_id = Some(def_id);
867 match self.tcx.fn_sig(def_id).abi() {
869 Abi::PlatformIntrinsic => {
870 assert!(!self.tcx.is_const_fn(def_id));
871 match &self.tcx.item_name(def_id)[..] {
872 "size_of" | "min_align_of" | "type_id" => is_const_fn = Some(def_id),
874 name if name.starts_with("simd_shuffle") => {
882 if self.tcx.is_const_fn(def_id) {
883 is_const_fn = Some(def_id);
889 let constant_arguments = callee_def_id.and_then(|id| {
890 args_required_const(self.tcx, id)
892 for (i, arg) in args.iter().enumerate() {
894 this.visit_operand(arg, location);
895 if this.mode != Mode::Fn {
898 let candidate = Candidate::Argument { bb, index: i };
899 if is_shuffle && i == 2 {
900 if this.can_promote() {
901 this.promotion_candidates.push(candidate);
903 span_err!(this.tcx.sess, this.span, E0526,
904 "shuffle indices are not constant");
909 let constant_arguments = match constant_arguments.as_ref() {
913 if !constant_arguments.contains(&i) {
916 if this.can_promote() {
917 this.promotion_candidates.push(candidate);
919 this.tcx.sess.span_err(this.span,
920 &format!("argument {} is required to be a constant",
927 if let Some(def_id) = is_const_fn {
928 // find corresponding rustc_const_unstable feature
929 if let Some(&attr::Stability {
930 rustc_const_unstable: Some(attr::RustcConstUnstable {
931 feature: ref feature_name
933 .. }) = self.tcx.lookup_stability(def_id) {
935 // We are in a const or static initializer,
936 if self.mode != Mode::Fn &&
938 // feature-gate is not enabled,
939 !self.tcx.sess.features.borrow()
940 .declared_lib_features
942 .any(|&(ref sym, _)| sym == feature_name) &&
944 // this doesn't come from a crate with the feature-gate enabled,
945 self.def_id.is_local() &&
947 // this doesn't come from a macro that has #[allow_internal_unstable]
948 !self.span.allows_unstable()
950 let mut err = self.tcx.sess.struct_span_err(self.span,
951 &format!("`{}` is not yet stable as a const fn",
952 self.tcx.item_path_str(def_id)));
954 "in Nightly builds, add `#![feature({})]` \
955 to the crate attributes to enable",
961 self.qualif = Qualif::NOT_CONST;
962 if self.mode != Mode::Fn {
963 // FIXME(#24111) Remove this check when const fn stabilizes
964 let (msg, note) = if let UnstableFeatures::Disallow =
965 self.tcx.sess.opts.unstable_features {
966 (format!("calls in {}s are limited to \
967 struct and enum constructors",
969 Some("a limited form of compile-time function \
970 evaluation is available on a nightly \
971 compiler via `const fn`"))
973 (format!("calls in {}s are limited \
974 to constant functions, \
975 struct and enum constructors",
979 let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
980 if let Some(note) = note {
981 err.span_note(self.span, note);
987 if let Some((ref dest, _)) = *destination {
988 // Avoid propagating irrelevant callee/argument qualifications.
989 if self.qualif.intersects(Qualif::CONST_ERROR) {
990 self.qualif = Qualif::NOT_CONST;
992 // Be conservative about the returned value of a const fn.
994 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
995 self.qualif = Qualif::empty();
998 self.assign(dest, location);
1000 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1001 self.super_terminator_kind(bb, kind, location);
1003 // Deny *any* live drops anywhere other than functions.
1004 if self.mode != Mode::Fn {
1005 // HACK(eddyb) Emulate a bit of dataflow analysis,
1006 // conservatively, that drop elaboration will do.
1007 let needs_drop = if let Place::Local(local) = *place {
1008 if self.temp_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
1009 Some(self.mir.local_decls[local].source_info.span)
1017 if let Some(span) = needs_drop {
1018 // Double-check the type being dropped, to minimize false positives.
1019 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1020 if ty.needs_drop(self.tcx, self.param_env) {
1021 struct_span_err!(self.tcx.sess, span, E0493,
1022 "destructors cannot be evaluated at compile-time")
1023 .span_label(span, format!("{}s cannot evaluate destructors",
1030 // Qualify any operands inside other terminators.
1031 self.super_terminator_kind(bb, kind, location);
1035 fn visit_assign(&mut self,
1038 rvalue: &Rvalue<'tcx>,
1039 location: Location) {
1040 self.visit_rvalue(rvalue, location);
1042 // Check the allowed const fn argument forms.
1043 if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
1044 if self.mir.local_kind(index) == LocalKind::Var &&
1045 self.const_fn_arg_vars.insert(index.index()) {
1047 // Direct use of an argument is permitted.
1049 Rvalue::Use(Operand::Copy(Place::Local(local))) |
1050 Rvalue::Use(Operand::Move(Place::Local(local))) => {
1051 if self.mir.local_kind(local) == LocalKind::Arg {
1058 // Avoid a generic error for other uses of arguments.
1059 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
1060 let decl = &self.mir.local_decls[index];
1061 let mut err = struct_span_err!(
1063 decl.source_info.span,
1065 "arguments of constant functions can only be immutable by-value bindings"
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 \
1082 self.assign(dest, location);
1085 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1086 self.span = source_info.span;
1089 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
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);
1096 StatementKind::SetDiscriminant { .. } |
1097 StatementKind::StorageLive(_) |
1098 StatementKind::StorageDead(_) |
1099 StatementKind::InlineAsm {..} |
1100 StatementKind::EndRegion(_) |
1101 StatementKind::Validate(..) |
1102 StatementKind::Nop => {}
1107 fn visit_terminator(&mut self,
1109 terminator: &Terminator<'tcx>,
1110 location: Location) {
1111 self.nest(|this| this.super_terminator(bb, terminator, location));
1115 pub fn provide(providers: &mut Providers) {
1116 *providers = Providers {
1122 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1124 -> (u8, Lrc<IdxSetBuf<Local>>) {
1125 // NB: This `borrow()` is guaranteed to be valid (i.e., the value
1126 // cannot yet be stolen), because `mir_validated()`, which steals
1127 // from `mir_const(), forces this query to execute before
1128 // performing the steal.
1129 let mir = &tcx.mir_const(def_id).borrow();
1131 if mir.return_ty().references_error() {
1132 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1133 return (Qualif::NOT_CONST.bits(), Lrc::new(IdxSetBuf::new_empty(0)));
1136 let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const);
1137 let (qualif, promoted_temps) = qualifier.qualify_const();
1138 (qualif.bits(), promoted_temps)
1141 pub struct QualifyAndPromoteConstants;
1143 impl MirPass for QualifyAndPromoteConstants {
1144 fn run_pass<'a, 'tcx>(&self,
1145 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1147 mir: &mut Mir<'tcx>) {
1148 // There's not really any point in promoting errorful MIR.
1149 if mir.return_ty().references_error() {
1150 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1154 if src.promoted.is_some() {
1158 let def_id = src.def_id;
1159 let id = tcx.hir.as_local_node_id(def_id).unwrap();
1160 let mut const_promoted_temps = None;
1161 let mode = match tcx.hir.body_owner_kind(id) {
1162 hir::BodyOwnerKind::Fn => {
1163 if tcx.is_const_fn(def_id) {
1169 hir::BodyOwnerKind::Const => {
1170 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1173 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1174 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1177 if mode == Mode::Fn || mode == Mode::ConstFn {
1178 // This is ugly because Qualifier holds onto mir,
1179 // which can't be mutated until its scope ends.
1180 let (temps, candidates) = {
1181 let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
1182 if mode == Mode::ConstFn {
1183 // Enforce a constant-like CFG for `const fn`.
1184 qualifier.qualify_const();
1186 while let Some((bb, data)) = qualifier.rpo.next() {
1187 qualifier.visit_basic_block_data(bb, data);
1191 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1194 // Do the actual promotion, now that we know what's viable.
1195 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1197 let promoted_temps = if mode == Mode::Const {
1198 // Already computed by `mir_const_qualif`.
1199 const_promoted_temps.unwrap()
1201 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1204 // In `const` and `static` everything without `StorageDead`
1205 // is `'static`, we don't have to create promoted MIR fragments,
1206 // just remove `Drop` and `StorageDead` on "promoted" locals.
1207 for block in mir.basic_blocks_mut() {
1208 block.statements.retain(|statement| {
1209 match statement.kind {
1210 StatementKind::StorageDead(index) => {
1211 !promoted_temps.contains(&index)
1216 let terminator = block.terminator_mut();
1217 match terminator.kind {
1218 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1219 if promoted_temps.contains(&index) {
1220 terminator.kind = TerminatorKind::Goto {
1230 // Statics must be Sync.
1231 if mode == Mode::Static {
1232 // `#[thread_local]` statics don't have to be `Sync`.
1233 for attr in &tcx.get_attrs(def_id)[..] {
1234 if attr.check_name("thread_local") {
1238 let ty = mir.return_ty();
1239 tcx.infer_ctxt().enter(|infcx| {
1240 let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
1241 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1242 let mut fulfillment_cx = traits::FulfillmentContext::new();
1243 fulfillment_cx.register_bound(&infcx,
1246 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1248 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1249 infcx.report_fulfillment_errors(&err, None);
1256 fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
1257 let attrs = tcx.get_attrs(def_id);
1258 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1259 let mut ret = FxHashSet();
1260 for meta in attr.meta_item_list()? {
1261 match meta.literal()?.node {
1262 LitKind::Int(a, _) => { ret.insert(a as usize); }