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_vec::{IndexVec, Idx};
20 use rustc::hir::map as hir_map;
21 use rustc::hir::def_id::DefId;
22 use rustc::hir::map::blocks::FnLikeNode;
23 use rustc::traits::{self, Reveal};
24 use rustc::ty::{self, TyCtxt, Ty};
25 use rustc::ty::cast::CastTy;
27 use rustc::mir::traversal::ReversePostorder;
28 use rustc::mir::transform::{Pass, MirPass, MirSource};
29 use rustc::mir::visit::{LvalueContext, Visitor};
30 use rustc::util::nodemap::DefIdMap;
31 use rustc::middle::lang_items;
33 use syntax::feature_gate::UnstableFeatures;
36 use std::collections::hash_map::Entry;
40 use super::promote_consts::{self, Candidate, TempState};
44 // Const item's qualification while recursing.
45 // Recursive consts are an error.
46 const RECURSIVE = 1 << 0,
48 // Constant containing interior mutability (UnsafeCell).
49 const MUTABLE_INTERIOR = 1 << 1,
51 // Constant containing an ADT that implements Drop.
52 const NEEDS_DROP = 1 << 2,
55 const FN_ARGUMENT = 1 << 3,
57 // Static lvalue or move from a static.
58 const STATIC = 1 << 4,
60 // Reference to a static.
61 const STATIC_REF = 1 << 5,
63 // Not constant at all - non-`const fn` calls, asm!,
64 // pointer comparisons, ptr-to-int casts, etc.
65 const NOT_CONST = 1 << 6,
67 // Refers to temporaries which cannot be promoted as
68 // promote_consts decided they weren't simple enough.
69 const NOT_PROMOTABLE = 1 << 7,
71 // Borrows of temporaries can be promoted only
72 // if they have none of the above qualifications.
73 const NEVER_PROMOTE = !0,
75 // Const items can only have MUTABLE_INTERIOR
76 // and NOT_PROMOTABLE without producing an error.
77 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
78 !Qualif::NOT_PROMOTABLE.bits
82 impl<'a, 'tcx> Qualif {
83 /// Remove flags which are impossible for the given type.
84 fn restrict(&mut self, ty: Ty<'tcx>,
85 tcx: TyCtxt<'a, 'tcx, 'tcx>,
86 param_env: &ty::ParameterEnvironment<'tcx>) {
87 if !ty.type_contents(tcx).interior_unsafe() {
88 *self = *self - Qualif::MUTABLE_INTERIOR;
90 if !tcx.type_needs_drop_given_env(ty, param_env) {
91 *self = *self - Qualif::NEEDS_DROP;
96 /// What kind of item we are in.
97 #[derive(Copy, Clone, PartialEq, Eq)]
106 impl fmt::Display for Mode {
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 Mode::Const => write!(f, "constant"),
110 Mode::Static | Mode::StaticMut => write!(f, "static"),
111 Mode::ConstFn => write!(f, "constant function"),
112 Mode::Fn => write!(f, "function")
117 pub fn is_const_fn(tcx: TyCtxt, def_id: DefId) -> bool {
118 if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
119 if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
120 fn_like.constness() == hir::Constness::Const
125 tcx.sess.cstore.is_const_fn(def_id)
129 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
134 rpo: ReversePostorder<'a, 'tcx>,
135 tcx: TyCtxt<'a, 'gcx, 'tcx>,
136 param_env: ty::ParameterEnvironment<'tcx>,
137 qualif_map: &'a mut DefIdMap<Qualif>,
138 temp_qualif: IndexVec<Local, Option<Qualif>>,
139 return_qualif: Option<Qualif>,
141 const_fn_arg_vars: BitVector,
142 temp_promotion_state: IndexVec<Local, TempState>,
143 promotion_candidates: Vec<Candidate>
146 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
147 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
148 param_env: ty::ParameterEnvironment<'tcx>,
149 qualif_map: &'a mut DefIdMap<Qualif>,
153 -> Qualifier<'a, 'tcx, 'tcx> {
154 let mut rpo = traversal::reverse_postorder(mir);
155 let temps = promote_consts::collect_temps(mir, &mut rpo);
164 param_env: param_env,
165 qualif_map: qualif_map,
166 temp_qualif: IndexVec::from_elem(None, &mir.local_decls),
168 qualif: Qualif::empty(),
169 const_fn_arg_vars: BitVector::new(mir.local_decls.len()),
170 temp_promotion_state: temps,
171 promotion_candidates: vec![]
175 // FIXME(eddyb) we could split the errors into meaningful
176 // categories, but enabling full miri would make that
177 // slightly pointless (even with feature-gating).
178 fn not_const(&mut self) {
179 self.add(Qualif::NOT_CONST);
180 if self.mode != Mode::Fn {
181 span_err!(self.tcx.sess, self.span, E0019,
182 "{} contains unimplemented expression type", self.mode);
186 /// Error about extra statements in a constant.
187 fn statement_like(&mut self) {
188 self.add(Qualif::NOT_CONST);
189 if self.mode != Mode::Fn {
190 span_err!(self.tcx.sess, self.span, E0016,
191 "blocks in {}s are limited to items and tail expressions",
196 /// Add the given qualification to self.qualif.
197 fn add(&mut self, qualif: Qualif) {
198 self.qualif = self.qualif | qualif;
201 /// Add the given type's qualification to self.qualif.
202 fn add_type(&mut self, ty: Ty<'tcx>) {
203 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
204 self.qualif.restrict(ty, self.tcx, &self.param_env);
207 /// Within the provided closure, self.qualif will start
208 /// out empty, and its value after the closure returns will
209 /// be combined with the value before the call to nest.
210 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
211 let original = self.qualif;
212 self.qualif = Qualif::empty();
217 /// Check for NEEDS_DROP (from an ADT or const fn call) and
218 /// error, unless we're in a function, or the feature-gate
219 /// for globals with destructors is enabled.
220 fn deny_drop(&self) {
221 if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) {
225 // Static and const fn's allow destructors, but they're feature-gated.
226 let msg = if self.mode != Mode::Const {
227 // Feature-gate for globals with destructors is enabled.
228 if self.tcx.sess.features.borrow().drop_types_in_const {
232 // This comes from a macro that has #[allow_internal_unstable].
233 if self.tcx.sess.codemap().span_allows_unstable(self.span) {
237 format!("destructors in {}s are an unstable feature",
240 format!("{}s are not allowed to have destructors",
245 struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg);
247 if self.mode != Mode::Const {
249 "in Nightly builds, add `#![feature(drop_types_in_const)]` \
250 to the crate attributes to enable");
252 self.find_drop_implementation_method_span()
253 .map(|span| err.span_label(span, &format!("destructor defined here")));
255 err.span_label(self.span, &format!("constants cannot have destructors"));
261 fn find_drop_implementation_method_span(&self) -> Option<Span> {
264 .and_then(|drop_trait_id| {
268 .lookup_trait_def(drop_trait_id)
269 .for_each_relevant_impl(self.tcx, self.mir.return_ty, |impl_did| {
271 .as_local_node_id(impl_did)
272 .and_then(|impl_node_id| self.tcx.hir.find(impl_node_id))
274 if let hir_map::NodeItem(item) = node {
275 if let hir::ItemImpl(.., ref impl_item_refs) = item.node {
276 span = impl_item_refs.first()
278 self.tcx.hir.impl_item(iiref.id)
290 /// Check if an Lvalue with the current qualifications could
291 /// be consumed, by either an operand or a Deref projection.
292 fn try_consume(&mut self) -> bool {
293 if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
294 let msg = if self.mode == Mode::Static ||
295 self.mode == Mode::StaticMut {
296 "cannot refer to other statics by value, use the \
297 address-of operator or a constant instead"
299 "cannot refer to statics by value, use a constant instead"
301 struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg)
302 .span_label(self.span, &format!("referring to another static by value"))
303 .note(&format!("use the address-of operator or a constant instead"))
306 // Replace STATIC with NOT_CONST to avoid further errors.
307 self.qualif = self.qualif - Qualif::STATIC;
308 self.add(Qualif::NOT_CONST);
316 /// Assign the current qualification to the given destination.
317 fn assign(&mut self, dest: &Lvalue<'tcx>, location: Location) {
318 let qualif = self.qualif;
319 let span = self.span;
320 let store = |slot: &mut Option<Qualif>| {
322 span_bug!(span, "multiple assignments to {:?}", dest);
324 *slot = Some(qualif);
327 // Only handle promotable temps in non-const functions.
328 if self.mode == Mode::Fn {
329 if let Lvalue::Local(index) = *dest {
330 if self.mir.local_kind(index) == LocalKind::Temp
331 && self.temp_promotion_state[index].is_promotable() {
332 debug!("store to promotable temp {:?}", index);
333 store(&mut self.temp_qualif[index]);
340 Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
341 debug!("store to temp {:?}", index);
342 store(&mut self.temp_qualif[index])
344 Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
345 debug!("store to return pointer {:?}", index);
346 store(&mut self.return_qualif)
349 Lvalue::Projection(box Projection {
350 base: Lvalue::Local(index),
351 elem: ProjectionElem::Deref
352 }) if self.mir.local_kind(index) == LocalKind::Temp
353 && self.mir.local_decls[index].ty.is_box()
354 && self.temp_qualif[index].map_or(false, |qualif| {
355 qualif.intersects(Qualif::NOT_CONST)
357 // Part of `box expr`, we should've errored
358 // already for the Box allocation Rvalue.
361 // This must be an explicit assignment.
363 // Catch more errors in the destination.
364 self.visit_lvalue(dest, LvalueContext::Store, location);
365 self.statement_like();
370 /// Qualify a whole const, static initializer or const fn.
371 fn qualify_const(&mut self) -> Qualif {
372 debug!("qualifying {} {}", self.mode, self.tcx.item_path_str(self.def_id));
376 let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
377 let mut bb = START_BLOCK;
379 seen_blocks.insert(bb.index());
381 self.visit_basic_block_data(bb, &mir[bb]);
383 let target = match mir[bb].terminator().kind {
384 TerminatorKind::Goto { target } |
385 // Drops are considered noops.
386 TerminatorKind::Drop { target, .. } |
387 TerminatorKind::Assert { target, .. } |
388 TerminatorKind::Call { destination: Some((_, target)), .. } => {
392 // Non-terminating calls cannot produce any value.
393 TerminatorKind::Call { destination: None, .. } => {
394 return Qualif::empty();
397 TerminatorKind::If {..} |
398 TerminatorKind::Switch {..} |
399 TerminatorKind::SwitchInt {..} |
400 TerminatorKind::DropAndReplace { .. } |
401 TerminatorKind::Resume |
402 TerminatorKind::Unreachable => None,
404 TerminatorKind::Return => {
405 // Check for unused values. This usually means
406 // there are extra statements in the AST.
407 for temp in mir.temps_iter() {
408 if self.temp_qualif[temp].is_none() {
412 let state = self.temp_promotion_state[temp];
413 if let TempState::Defined { location, uses: 0 } = state {
414 let data = &mir[location.block];
415 let stmt_idx = location.statement_index;
417 // Get the span for the initialization.
418 let source_info = if stmt_idx < data.statements.len() {
419 data.statements[stmt_idx].source_info
421 data.terminator().source_info
423 self.span = source_info.span;
425 // Treat this as a statement in the AST.
426 self.statement_like();
430 // Make sure there are no extra unassigned variables.
431 self.qualif = Qualif::NOT_CONST;
432 for index in mir.vars_iter() {
433 if !self.const_fn_arg_vars.contains(index.index()) {
434 debug!("unassigned variable {:?}", index);
435 self.assign(&Lvalue::Local(index), Location {
437 statement_index: usize::MAX,
448 Some(target) if !seen_blocks.contains(target.index()) => {
458 let return_ty = mir.return_ty;
459 self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
463 // Check for destructors in static mut.
464 self.add_type(return_ty);
468 // Account for errors in consts by using the
469 // conservative type qualification instead.
470 if self.qualif.intersects(Qualif::CONST_ERROR) {
471 self.qualif = Qualif::empty();
472 self.add_type(return_ty);
480 /// Accumulates an Rvalue or Call's effects in self.qualif.
481 /// For functions (constant or not), it also records
482 /// candidates for promotion in promotion_candidates.
483 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
484 fn visit_lvalue(&mut self,
485 lvalue: &Lvalue<'tcx>,
486 context: LvalueContext<'tcx>,
487 location: Location) {
489 Lvalue::Local(local) => match self.mir.local_kind(local) {
490 LocalKind::ReturnPointer => {
494 self.add(Qualif::FN_ARGUMENT);
497 self.add(Qualif::NOT_CONST);
500 if !self.temp_promotion_state[local].is_promotable() {
501 self.add(Qualif::NOT_PROMOTABLE);
504 if let Some(qualif) = self.temp_qualif[local] {
511 Lvalue::Static(_) => {
512 self.add(Qualif::STATIC);
513 if self.mode == Mode::Const || self.mode == Mode::ConstFn {
514 span_err!(self.tcx.sess, self.span, E0013,
515 "{}s cannot refer to statics, use \
516 a constant instead", self.mode);
519 Lvalue::Projection(ref proj) => {
521 this.super_lvalue(lvalue, context, location);
523 ProjectionElem::Deref => {
524 if !this.try_consume() {
528 if this.qualif.intersects(Qualif::STATIC_REF) {
529 this.qualif = this.qualif - Qualif::STATIC_REF;
530 this.add(Qualif::STATIC);
533 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
534 if let ty::TyRawPtr(_) = base_ty.sty {
535 this.add(Qualif::NOT_CONST);
536 if this.mode != Mode::Fn {
537 struct_span_err!(this.tcx.sess,
539 "raw pointers cannot be dereferenced in {}s",
541 .span_label(this.span,
542 &format!("dereference of raw pointer in constant"))
548 ProjectionElem::Field(..) |
549 ProjectionElem::Index(_) => {
550 if this.mode != Mode::Fn &&
551 this.qualif.intersects(Qualif::STATIC) {
552 span_err!(this.tcx.sess, this.span, E0494,
553 "cannot refer to the interior of another \
554 static, use a constant instead");
556 let ty = lvalue.ty(this.mir, this.tcx).to_ty(this.tcx);
557 this.qualif.restrict(ty, this.tcx, &this.param_env);
560 ProjectionElem::ConstantIndex {..} |
561 ProjectionElem::Subslice {..} |
562 ProjectionElem::Downcast(..) => {
571 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
573 Operand::Consume(_) => {
575 this.super_operand(operand, location);
579 Operand::Constant(ref constant) => {
580 // Only functions and methods can have these types.
581 if let ty::TyFnDef(..) = constant.ty.sty {
585 if let Literal::Item { def_id, substs } = constant.literal {
586 // Don't peek inside generic (associated) constants.
587 if substs.types().next().is_some() {
588 self.add_type(constant.ty);
590 let qualif = qualify_const_item_cached(self.tcx,
596 // FIXME(eddyb) check recursive constants here,
597 // instead of rustc_passes::static_recursion.
598 if self.qualif.intersects(Qualif::RECURSIVE) {
599 span_bug!(constant.span,
600 "recursive constant wasn't caught earlier");
603 // Let `const fn` transitively have destructors,
604 // but they do get stopped in `const` or `static`.
605 if self.mode != Mode::ConstFn {
613 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
614 // Recurse through operands and lvalues.
615 self.super_rvalue(rvalue, location);
620 Rvalue::UnaryOp(..) |
621 Rvalue::CheckedBinaryOp(..) |
622 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
623 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
624 Rvalue::Cast(CastKind::Unsize, ..) => {}
627 // Static lvalues in consts would have errored already,
628 // don't treat length checks as reads from statics.
629 self.qualif = self.qualif - Qualif::STATIC;
632 Rvalue::Ref(_, kind, ref lvalue) => {
633 // Static lvalues in consts would have errored already,
634 // only keep track of references to them here.
635 if self.qualif.intersects(Qualif::STATIC) {
636 self.qualif = self.qualif - Qualif::STATIC;
637 self.add(Qualif::STATIC_REF);
640 let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
641 if kind == BorrowKind::Mut {
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 let allow = if self.mode == Mode::StaticMut {
646 // Inside a `static mut`, &mut [...] is also allowed.
648 ty::TyArray(..) | ty::TySlice(_) => {
649 // Mutating can expose drops, be conservative.
656 } else if let ty::TyArray(_, 0) = ty.sty {
657 self.mode == Mode::Fn
663 self.add(Qualif::NOT_CONST);
664 if self.mode != Mode::Fn {
665 struct_span_err!(self.tcx.sess, self.span, E0017,
666 "references in {}s may only refer \
667 to immutable values", self.mode)
668 .span_label(self.span, &format!("{}s require immutable values",
674 // Constants cannot be borrowed if they contain interior mutability as
675 // it means that our "silent insertion of statics" could change
676 // initializer values (very bad).
677 if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
678 // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
679 // duplicate errors (from reborrowing, for example).
680 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
681 self.add(Qualif::NOT_CONST);
682 if self.mode != Mode::Fn {
683 span_err!(self.tcx.sess, self.span, E0492,
684 "cannot borrow a constant which contains \
685 interior mutability, create a static instead");
690 // We might have a candidate for promotion.
691 let candidate = Candidate::Ref(location);
692 if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
693 if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
694 // We can only promote direct borrows of temps.
695 if let Lvalue::Local(local) = *lvalue {
696 if self.mir.local_kind(local) == LocalKind::Temp {
697 self.promotion_candidates.push(candidate);
704 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
705 let operand_ty = operand.ty(self.mir, self.tcx);
706 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
707 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
708 match (cast_in, cast_out) {
709 (CastTy::Ptr(_), CastTy::Int(_)) |
710 (CastTy::FnPtr, CastTy::Int(_)) => {
711 self.add(Qualif::NOT_CONST);
712 if self.mode != Mode::Fn {
713 span_err!(self.tcx.sess, self.span, E0018,
714 "raw pointers cannot be cast to integers in {}s",
722 Rvalue::BinaryOp(op, ref lhs, _) => {
723 if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
724 assert!(op == BinOp::Eq || op == BinOp::Ne ||
725 op == BinOp::Le || op == BinOp::Lt ||
726 op == BinOp::Ge || op == BinOp::Gt);
728 self.add(Qualif::NOT_CONST);
729 if self.mode != Mode::Fn {
731 self.tcx.sess, self.span, E0395,
732 "raw pointers cannot be compared in {}s",
736 &format!("comparing raw pointers in static"))
743 self.add(Qualif::NOT_CONST);
744 if self.mode != Mode::Fn {
745 struct_span_err!(self.tcx.sess, self.span, E0010,
746 "allocations are not allowed in {}s", self.mode)
747 .span_label(self.span, &format!("allocation not allowed in {}s", self.mode))
752 Rvalue::Aggregate(ref kind, _) => {
753 if let AggregateKind::Adt(def, ..) = *kind {
755 self.add(Qualif::NEEDS_DROP);
759 if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() {
760 let ty = rvalue.ty(self.mir, self.tcx).unwrap();
762 assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
763 // Even if the value inside may not need dropping,
764 // mutating it would change that.
765 if !self.qualif.intersects(Qualif::NOT_CONST) {
772 Rvalue::InlineAsm {..} => {
778 fn visit_terminator_kind(&mut self,
780 kind: &TerminatorKind<'tcx>,
781 location: Location) {
782 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
783 self.visit_operand(func, location);
785 let fn_ty = func.ty(self.mir, self.tcx);
786 let (is_shuffle, is_const_fn) = match fn_ty.sty {
787 ty::TyFnDef(def_id, _, f) => {
788 (f.abi == Abi::PlatformIntrinsic &&
789 self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
790 is_const_fn(self.tcx, def_id))
795 for (i, arg) in args.iter().enumerate() {
797 this.visit_operand(arg, location);
798 if is_shuffle && i == 2 && this.mode == Mode::Fn {
799 let candidate = Candidate::ShuffleIndices(bb);
800 if !this.qualif.intersects(Qualif::NEVER_PROMOTE) {
801 this.promotion_candidates.push(candidate);
803 span_err!(this.tcx.sess, this.span, E0526,
804 "shuffle indices are not constant");
812 // We are in a const or static initializer,
813 if self.mode != Mode::Fn &&
815 // feature-gate is not enabled,
816 !self.tcx.sess.features.borrow().const_fn &&
818 // this doesn't come from a crate with the feature-gate enabled,
819 self.def_id.is_local() &&
821 // this doesn't come from a macro that has #[allow_internal_unstable]
822 !self.tcx.sess.codemap().span_allows_unstable(self.span)
824 let mut err = self.tcx.sess.struct_span_err(self.span,
825 "const fns are an unstable feature");
827 "in Nightly builds, add `#![feature(const_fn)]` \
828 to the crate attributes to enable");
832 self.qualif = Qualif::NOT_CONST;
833 if self.mode != Mode::Fn {
834 // FIXME(#24111) Remove this check when const fn stabilizes
835 let (msg, note) = if let UnstableFeatures::Disallow =
836 self.tcx.sess.opts.unstable_features {
837 (format!("calls in {}s are limited to \
838 struct and enum constructors",
840 Some("a limited form of compile-time function \
841 evaluation is available on a nightly \
842 compiler via `const fn`"))
844 (format!("calls in {}s are limited \
845 to constant functions, \
846 struct and enum constructors",
850 let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
851 if let Some(note) = note {
852 err.span_note(self.span, note);
858 if let Some((ref dest, _)) = *destination {
859 // Avoid propagating irrelevant callee/argument qualifications.
860 if self.qualif.intersects(Qualif::CONST_ERROR) {
861 self.qualif = Qualif::NOT_CONST;
863 // Be conservative about the returned value of a const fn.
865 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
866 self.qualif = Qualif::empty();
869 // Let `const fn` transitively have destructors,
870 // but they do get stopped in `const` or `static`.
871 if self.mode != Mode::ConstFn {
875 self.assign(dest, location);
878 // Qualify any operands inside other terminators.
879 self.super_terminator_kind(bb, kind, location);
883 fn visit_assign(&mut self,
886 rvalue: &Rvalue<'tcx>,
887 location: Location) {
888 self.visit_rvalue(rvalue, location);
890 // Check the allowed const fn argument forms.
891 if let (Mode::ConstFn, &Lvalue::Local(index)) = (self.mode, dest) {
892 if self.mir.local_kind(index) == LocalKind::Var &&
893 self.const_fn_arg_vars.insert(index.index()) {
895 // Direct use of an argument is permitted.
896 if let Rvalue::Use(Operand::Consume(Lvalue::Local(local))) = *rvalue {
897 if self.mir.local_kind(local) == LocalKind::Arg {
902 // Avoid a generic error for other uses of arguments.
903 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
904 let decl = &self.mir.local_decls[index];
905 span_err!(self.tcx.sess, decl.source_info.unwrap().span, E0022,
906 "arguments of constant functions can only \
907 be immutable by-value bindings");
913 self.assign(dest, location);
916 fn visit_source_info(&mut self, source_info: &SourceInfo) {
917 self.span = source_info.span;
920 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
922 this.visit_source_info(&statement.source_info);
923 match statement.kind {
924 StatementKind::Assign(ref lvalue, ref rvalue) => {
925 this.visit_assign(bb, lvalue, rvalue, location);
927 StatementKind::SetDiscriminant { .. } |
928 StatementKind::StorageLive(_) |
929 StatementKind::StorageDead(_) |
930 StatementKind::Nop => {}
935 fn visit_terminator(&mut self,
937 terminator: &Terminator<'tcx>,
938 location: Location) {
939 self.nest(|this| this.super_terminator(bb, terminator, location));
943 fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
944 qualif_map: &mut DefIdMap<Qualif>,
947 match qualif_map.entry(def_id) {
948 Entry::Occupied(entry) => return *entry.get(),
949 Entry::Vacant(entry) => {
950 // Guard against `const` recursion.
951 entry.insert(Qualif::RECURSIVE);
955 let param_env = if def_id.is_local() {
956 let node_id = tcx.hir.as_local_node_id(def_id).unwrap();
957 ty::ParameterEnvironment::for_item(tcx, node_id)
959 // These should only be monomorphic constants.
960 tcx.empty_parameter_environment()
963 let mir = &tcx.item_mir(def_id);
964 let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, def_id, mir, Mode::Const);
965 let qualif = qualifier.qualify_const();
966 qualifier.qualif_map.insert(def_id, qualif);
971 pub struct QualifyAndPromoteConstants {
972 qualif_map: DefIdMap<Qualif>
975 impl Pass for QualifyAndPromoteConstants {}
977 impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants {
978 fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
979 src: MirSource, mir: &mut Mir<'tcx>) {
980 let id = src.item_id();
981 let def_id = tcx.hir.local_def_id(id);
982 let mode = match src {
983 MirSource::Fn(_) => {
984 if is_const_fn(tcx, def_id) {
990 MirSource::Const(_) => {
991 match self.qualif_map.entry(def_id) {
992 Entry::Occupied(_) => return,
993 Entry::Vacant(entry) => {
994 // Guard against `const` recursion.
995 entry.insert(Qualif::RECURSIVE);
1000 MirSource::Static(_, hir::MutImmutable) => Mode::Static,
1001 MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
1002 MirSource::Promoted(..) => return
1004 let param_env = ty::ParameterEnvironment::for_item(tcx, id);
1006 if mode == Mode::Fn || mode == Mode::ConstFn {
1007 // This is ugly because Qualifier holds onto mir,
1008 // which can't be mutated until its scope ends.
1009 let (temps, candidates) = {
1010 let mut qualifier = Qualifier::new(tcx, param_env,
1011 &mut self.qualif_map,
1013 if mode == Mode::ConstFn {
1014 // Enforce a constant-like CFG for `const fn`.
1015 qualifier.qualify_const();
1017 while let Some((bb, data)) = qualifier.rpo.next() {
1018 qualifier.visit_basic_block_data(bb, data);
1022 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1025 // Do the actual promotion, now that we know what's viable.
1026 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1028 let mut qualifier = Qualifier::new(tcx, param_env,
1029 &mut self.qualif_map,
1031 let qualif = qualifier.qualify_const();
1033 if mode == Mode::Const {
1034 qualifier.qualif_map.insert(def_id, qualif);
1038 // Statics must be Sync.
1039 if mode == Mode::Static {
1040 let ty = mir.return_ty;
1041 tcx.infer_ctxt((), Reveal::NotSpecializable).enter(|infcx| {
1042 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1043 let mut fulfillment_cx = traits::FulfillmentContext::new();
1044 fulfillment_cx.register_bound(&infcx, ty,
1045 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1047 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1048 infcx.report_fulfillment_errors(&err);