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, TypeFoldable};
25 use rustc::ty::cast::CastTy;
26 use rustc::ty::maps::Providers;
28 use rustc::mir::traversal::ReversePostorder;
29 use rustc::mir::transform::{MirPass, MirSource};
30 use rustc::mir::visit::{LvalueContext, Visitor};
31 use rustc::middle::lang_items;
33 use syntax::feature_gate::UnstableFeatures;
34 use syntax_pos::{Span, DUMMY_SP};
39 use super::promote_consts::{self, Candidate, TempState};
43 // Constant containing interior mutability (UnsafeCell).
44 const MUTABLE_INTERIOR = 1 << 0,
46 // Constant containing an ADT that implements Drop.
47 const NEEDS_DROP = 1 << 1,
50 const FN_ARGUMENT = 1 << 2,
52 // Static lvalue or move from a static.
53 const STATIC = 1 << 3,
55 // Reference to a static.
56 const STATIC_REF = 1 << 4,
58 // Not constant at all - non-`const fn` calls, asm!,
59 // pointer comparisons, ptr-to-int casts, etc.
60 const NOT_CONST = 1 << 5,
62 // Refers to temporaries which cannot be promoted as
63 // promote_consts decided they weren't simple enough.
64 const NOT_PROMOTABLE = 1 << 6,
66 // Borrows of temporaries can be promoted only
67 // if they have none of the above qualifications.
68 const NEVER_PROMOTE = 0b111_1111,
70 // Const items can only have MUTABLE_INTERIOR
71 // and NOT_PROMOTABLE without producing an error.
72 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
73 !Qualif::NOT_PROMOTABLE.bits
77 impl<'a, 'tcx> Qualif {
78 /// Remove flags which are impossible for the given type.
79 fn restrict(&mut self, ty: Ty<'tcx>,
80 tcx: TyCtxt<'a, 'tcx, 'tcx>,
81 param_env: ty::ParamEnv<'tcx>) {
82 if ty.is_freeze(tcx, param_env, DUMMY_SP) {
83 *self = *self - Qualif::MUTABLE_INTERIOR;
85 if !ty.needs_drop(tcx, param_env) {
86 *self = *self - Qualif::NEEDS_DROP;
91 /// What kind of item we are in.
92 #[derive(Copy, Clone, PartialEq, Eq)]
101 impl fmt::Display for Mode {
102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 Mode::Const => write!(f, "constant"),
105 Mode::Static | Mode::StaticMut => write!(f, "static"),
106 Mode::ConstFn => write!(f, "constant function"),
107 Mode::Fn => write!(f, "function")
112 pub fn is_const_fn(tcx: TyCtxt, def_id: DefId) -> bool {
113 if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
114 if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
115 fn_like.constness() == hir::Constness::Const
120 tcx.sess.cstore.is_const_fn(def_id)
124 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
129 rpo: ReversePostorder<'a, 'tcx>,
130 tcx: TyCtxt<'a, 'gcx, 'tcx>,
131 param_env: ty::ParamEnv<'tcx>,
132 temp_qualif: IndexVec<Local, Option<Qualif>>,
133 return_qualif: Option<Qualif>,
135 const_fn_arg_vars: BitVector,
136 temp_promotion_state: IndexVec<Local, TempState>,
137 promotion_candidates: Vec<Candidate>
140 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
141 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
142 param_env: ty::ParamEnv<'tcx>,
146 -> Qualifier<'a, 'tcx, 'tcx> {
147 let mut rpo = traversal::reverse_postorder(mir);
148 let temps = promote_consts::collect_temps(mir, &mut rpo);
157 param_env: param_env,
158 temp_qualif: IndexVec::from_elem(None, &mir.local_decls),
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 span_err!(self.tcx.sess, self.span, E0019,
174 "{} contains unimplemented expression type", self.mode);
178 /// Error about extra statements in a constant.
179 fn statement_like(&mut self) {
180 self.add(Qualif::NOT_CONST);
181 if self.mode != Mode::Fn {
182 span_err!(self.tcx.sess, self.span, E0016,
183 "blocks in {}s are limited to items and tail expressions",
188 /// Add the given qualification to self.qualif.
189 fn add(&mut self, qualif: Qualif) {
190 self.qualif = self.qualif | qualif;
193 /// Add the given type's qualification to self.qualif.
194 fn add_type(&mut self, ty: Ty<'tcx>) {
195 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
196 self.qualif.restrict(ty, self.tcx, self.param_env);
199 /// Within the provided closure, self.qualif will start
200 /// out empty, and its value after the closure returns will
201 /// be combined with the value before the call to nest.
202 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
203 let original = self.qualif;
204 self.qualif = Qualif::empty();
209 /// Check for NEEDS_DROP (from an ADT or const fn call) and
210 /// error, unless we're in a function, or the feature-gate
211 /// for globals with destructors is enabled.
212 fn deny_drop(&self) {
213 if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) {
217 // Static and const fn's allow destructors, but they're feature-gated.
218 let msg = if self.mode != Mode::Const {
219 // Feature-gate for globals with destructors is enabled.
220 if self.tcx.sess.features.borrow().drop_types_in_const {
224 // This comes from a macro that has #[allow_internal_unstable].
225 if self.span.allows_unstable() {
229 format!("destructors in {}s are an unstable feature",
232 format!("{}s are not allowed to have destructors",
237 struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg);
239 if self.mode != Mode::Const {
241 "in Nightly builds, add `#![feature(drop_types_in_const)]` \
242 to the crate attributes to enable");
244 self.find_drop_implementation_method_span()
245 .map(|span| err.span_label(span, "destructor defined here"));
247 err.span_label(self.span, "constants cannot have destructors");
253 fn find_drop_implementation_method_span(&self) -> Option<Span> {
256 .and_then(|drop_trait_id| {
260 .trait_def(drop_trait_id)
261 .for_each_relevant_impl(self.tcx, self.mir.return_ty, |impl_did| {
263 .as_local_node_id(impl_did)
264 .and_then(|impl_node_id| self.tcx.hir.find(impl_node_id))
266 if let hir_map::NodeItem(item) = node {
267 if let hir::ItemImpl(.., ref impl_item_refs) = item.node {
268 span = impl_item_refs.first()
270 self.tcx.hir.impl_item(iiref.id)
282 /// Check if an Lvalue with the current qualifications could
283 /// be consumed, by either an operand or a Deref projection.
284 fn try_consume(&mut self) -> bool {
285 if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
286 let msg = if self.mode == Mode::Static ||
287 self.mode == Mode::StaticMut {
288 "cannot refer to other statics by value, use the \
289 address-of operator or a constant instead"
291 "cannot refer to statics by value, use a constant instead"
293 struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg)
294 .span_label(self.span, "referring to another static by value")
295 .note("use the address-of operator or a constant instead")
298 // Replace STATIC with NOT_CONST to avoid further errors.
299 self.qualif = self.qualif - Qualif::STATIC;
300 self.add(Qualif::NOT_CONST);
308 /// Assign the current qualification to the given destination.
309 fn assign(&mut self, dest: &Lvalue<'tcx>, location: Location) {
310 let qualif = self.qualif;
311 let span = self.span;
312 let store = |slot: &mut Option<Qualif>| {
314 span_bug!(span, "multiple assignments to {:?}", dest);
316 *slot = Some(qualif);
319 // Only handle promotable temps in non-const functions.
320 if self.mode == Mode::Fn {
321 if let Lvalue::Local(index) = *dest {
322 if self.mir.local_kind(index) == LocalKind::Temp
323 && self.temp_promotion_state[index].is_promotable() {
324 debug!("store to promotable temp {:?}", index);
325 store(&mut self.temp_qualif[index]);
332 Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::Temp => {
333 debug!("store to temp {:?}", index);
334 store(&mut self.temp_qualif[index])
336 Lvalue::Local(index) if self.mir.local_kind(index) == LocalKind::ReturnPointer => {
337 debug!("store to return pointer {:?}", index);
338 store(&mut self.return_qualif)
341 Lvalue::Projection(box Projection {
342 base: Lvalue::Local(index),
343 elem: ProjectionElem::Deref
344 }) if self.mir.local_kind(index) == LocalKind::Temp
345 && self.mir.local_decls[index].ty.is_box()
346 && self.temp_qualif[index].map_or(false, |qualif| {
347 qualif.intersects(Qualif::NOT_CONST)
349 // Part of `box expr`, we should've errored
350 // already for the Box allocation Rvalue.
353 // This must be an explicit assignment.
355 // Catch more errors in the destination.
356 self.visit_lvalue(dest, LvalueContext::Store, location);
357 self.statement_like();
362 /// Qualify a whole const, static initializer or const fn.
363 fn qualify_const(&mut self) -> Qualif {
364 debug!("qualifying {} {:?}", self.mode, self.def_id);
368 let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
369 let mut bb = START_BLOCK;
371 seen_blocks.insert(bb.index());
373 self.visit_basic_block_data(bb, &mir[bb]);
375 let target = match mir[bb].terminator().kind {
376 TerminatorKind::Goto { target } |
377 // Drops are considered noops.
378 TerminatorKind::Drop { target, .. } |
379 TerminatorKind::Assert { target, .. } |
380 TerminatorKind::Call { destination: Some((_, target)), .. } => {
384 // Non-terminating calls cannot produce any value.
385 TerminatorKind::Call { destination: None, .. } => {
386 return Qualif::empty();
389 TerminatorKind::SwitchInt {..} |
390 TerminatorKind::DropAndReplace { .. } |
391 TerminatorKind::Resume |
392 TerminatorKind::Unreachable => None,
394 TerminatorKind::Return => {
395 // Check for unused values. This usually means
396 // there are extra statements in the AST.
397 for temp in mir.temps_iter() {
398 if self.temp_qualif[temp].is_none() {
402 let state = self.temp_promotion_state[temp];
403 if let TempState::Defined { location, uses: 0 } = state {
404 let data = &mir[location.block];
405 let stmt_idx = location.statement_index;
407 // Get the span for the initialization.
408 let source_info = if stmt_idx < data.statements.len() {
409 data.statements[stmt_idx].source_info
411 data.terminator().source_info
413 self.span = source_info.span;
415 // Treat this as a statement in the AST.
416 self.statement_like();
420 // Make sure there are no extra unassigned variables.
421 self.qualif = Qualif::NOT_CONST;
422 for index in mir.vars_iter() {
423 if !self.const_fn_arg_vars.contains(index.index()) {
424 debug!("unassigned variable {:?}", index);
425 self.assign(&Lvalue::Local(index), Location {
427 statement_index: usize::MAX,
438 Some(target) if !seen_blocks.contains(target.index()) => {
448 let return_ty = mir.return_ty;
449 self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
453 // Check for destructors in static mut.
454 self.add_type(return_ty);
458 // Account for errors in consts by using the
459 // conservative type qualification instead.
460 if self.qualif.intersects(Qualif::CONST_ERROR) {
461 self.qualif = Qualif::empty();
462 self.add_type(return_ty);
470 /// Accumulates an Rvalue or Call's effects in self.qualif.
471 /// For functions (constant or not), it also records
472 /// candidates for promotion in promotion_candidates.
473 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
474 fn visit_lvalue(&mut self,
475 lvalue: &Lvalue<'tcx>,
476 context: LvalueContext<'tcx>,
477 location: Location) {
479 Lvalue::Local(local) => match self.mir.local_kind(local) {
480 LocalKind::ReturnPointer => {
484 self.add(Qualif::FN_ARGUMENT);
487 self.add(Qualif::NOT_CONST);
490 if !self.temp_promotion_state[local].is_promotable() {
491 self.add(Qualif::NOT_PROMOTABLE);
494 if let Some(qualif) = self.temp_qualif[local] {
501 Lvalue::Static(_) => {
502 self.add(Qualif::STATIC);
503 if self.mode == Mode::Const || self.mode == Mode::ConstFn {
504 span_err!(self.tcx.sess, self.span, E0013,
505 "{}s cannot refer to statics, use \
506 a constant instead", self.mode);
509 Lvalue::Projection(ref proj) => {
511 this.super_lvalue(lvalue, context, location);
513 ProjectionElem::Deref => {
514 if !this.try_consume() {
518 if this.qualif.intersects(Qualif::STATIC_REF) {
519 this.qualif = this.qualif - Qualif::STATIC_REF;
520 this.add(Qualif::STATIC);
523 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
524 if let ty::TyRawPtr(_) = base_ty.sty {
525 this.add(Qualif::NOT_CONST);
526 if this.mode != Mode::Fn {
527 struct_span_err!(this.tcx.sess,
529 "raw pointers cannot be dereferenced in {}s",
531 .span_label(this.span,
532 "dereference of raw pointer in constant")
538 ProjectionElem::Field(..) |
539 ProjectionElem::Index(_) => {
540 if this.mode != Mode::Fn &&
541 this.qualif.intersects(Qualif::STATIC) {
542 span_err!(this.tcx.sess, this.span, E0494,
543 "cannot refer to the interior of another \
544 static, use a constant instead");
546 let ty = lvalue.ty(this.mir, this.tcx).to_ty(this.tcx);
547 this.qualif.restrict(ty, this.tcx, this.param_env);
550 ProjectionElem::ConstantIndex {..} |
551 ProjectionElem::Subslice {..} |
552 ProjectionElem::Downcast(..) => {
561 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
563 Operand::Consume(_) => {
565 this.super_operand(operand, location);
569 Operand::Constant(ref constant) => {
570 if let Literal::Item { def_id, substs } = constant.literal {
571 // Don't peek inside generic (associated) constants.
572 if substs.types().next().is_some() {
573 self.add_type(constant.ty);
575 let bits = self.tcx.at(constant.span).mir_const_qualif(def_id);
577 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
581 // Let `const fn` transitively have destructors,
582 // but they do get stopped in `const` or `static`.
583 if self.mode != Mode::ConstFn {
591 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
592 // Recurse through operands and lvalues.
593 self.super_rvalue(rvalue, location);
598 Rvalue::UnaryOp(UnOp::Neg, _) |
599 Rvalue::UnaryOp(UnOp::Not, _) |
600 Rvalue::NullaryOp(NullOp::SizeOf, _) |
601 Rvalue::CheckedBinaryOp(..) |
602 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
603 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
604 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
605 Rvalue::Cast(CastKind::Unsize, ..) |
606 Rvalue::Discriminant(..) => {}
609 // Static lvalues in consts would have errored already,
610 // don't treat length checks as reads from statics.
611 self.qualif = self.qualif - Qualif::STATIC;
614 Rvalue::Ref(_, kind, ref lvalue) => {
615 // Static lvalues in consts would have errored already,
616 // only keep track of references to them here.
617 if self.qualif.intersects(Qualif::STATIC) {
618 self.qualif = self.qualif - Qualif::STATIC;
619 self.add(Qualif::STATIC_REF);
622 let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
623 if kind == BorrowKind::Mut {
624 // In theory, any zero-sized value could be borrowed
625 // mutably without consequences. However, only &mut []
626 // is allowed right now, and only in functions.
627 let allow = if self.mode == Mode::StaticMut {
628 // Inside a `static mut`, &mut [...] is also allowed.
630 ty::TyArray(..) | ty::TySlice(_) => {
631 // Mutating can expose drops, be conservative.
638 } else if let ty::TyArray(_, 0) = ty.sty {
639 self.mode == Mode::Fn
645 self.add(Qualif::NOT_CONST);
646 if self.mode != Mode::Fn {
647 struct_span_err!(self.tcx.sess, self.span, E0017,
648 "references in {}s may only refer \
649 to immutable values", self.mode)
650 .span_label(self.span, format!("{}s require immutable values",
656 // Constants cannot be borrowed if they contain interior mutability as
657 // it means that our "silent insertion of statics" could change
658 // initializer values (very bad).
659 if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
660 // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
661 // duplicate errors (from reborrowing, for example).
662 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
663 self.add(Qualif::NOT_CONST);
664 if self.mode != Mode::Fn {
665 span_err!(self.tcx.sess, self.span, E0492,
666 "cannot borrow a constant which contains \
667 interior mutability, create a static instead");
672 // We might have a candidate for promotion.
673 let candidate = Candidate::Ref(location);
674 if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
675 if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
676 // We can only promote direct borrows of temps.
677 if let Lvalue::Local(local) = *lvalue {
678 if self.mir.local_kind(local) == LocalKind::Temp {
679 self.promotion_candidates.push(candidate);
686 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
687 let operand_ty = operand.ty(self.mir, self.tcx);
688 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
689 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
690 match (cast_in, cast_out) {
691 (CastTy::Ptr(_), CastTy::Int(_)) |
692 (CastTy::FnPtr, CastTy::Int(_)) => {
693 self.add(Qualif::NOT_CONST);
694 if self.mode != Mode::Fn {
695 span_err!(self.tcx.sess, self.span, E0018,
696 "raw pointers cannot be cast to integers in {}s",
704 Rvalue::BinaryOp(op, ref lhs, _) => {
705 if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
706 assert!(op == BinOp::Eq || op == BinOp::Ne ||
707 op == BinOp::Le || op == BinOp::Lt ||
708 op == BinOp::Ge || op == BinOp::Gt ||
709 op == BinOp::Offset);
711 self.add(Qualif::NOT_CONST);
712 if self.mode != Mode::Fn {
714 self.tcx.sess, self.span, E0395,
715 "raw pointers cannot be compared in {}s",
719 "comparing raw pointers in static")
725 Rvalue::NullaryOp(NullOp::Box, _) => {
726 self.add(Qualif::NOT_CONST);
727 if self.mode != Mode::Fn {
728 struct_span_err!(self.tcx.sess, self.span, E0010,
729 "allocations are not allowed in {}s", self.mode)
730 .span_label(self.span, format!("allocation not allowed in {}s", self.mode))
735 Rvalue::Aggregate(ref kind, _) => {
736 if let AggregateKind::Adt(def, ..) = **kind {
737 if def.has_dtor(self.tcx) {
738 self.add(Qualif::NEEDS_DROP);
742 if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() {
743 let ty = rvalue.ty(self.mir, self.tcx);
745 assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
746 // Even if the value inside may not need dropping,
747 // mutating it would change that.
748 if !self.qualif.intersects(Qualif::NOT_CONST) {
757 fn visit_terminator_kind(&mut self,
759 kind: &TerminatorKind<'tcx>,
760 location: Location) {
761 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
762 self.visit_operand(func, location);
764 let fn_ty = func.ty(self.mir, self.tcx);
765 let (is_shuffle, is_const_fn) = match fn_ty.sty {
766 ty::TyFnDef(def_id, _, f) => {
767 (f.abi() == Abi::PlatformIntrinsic &&
768 self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
769 is_const_fn(self.tcx, def_id))
774 for (i, arg) in args.iter().enumerate() {
776 this.visit_operand(arg, location);
777 if is_shuffle && i == 2 && this.mode == Mode::Fn {
778 let candidate = Candidate::ShuffleIndices(bb);
779 if !this.qualif.intersects(Qualif::NEVER_PROMOTE) {
780 this.promotion_candidates.push(candidate);
782 span_err!(this.tcx.sess, this.span, E0526,
783 "shuffle indices are not constant");
791 // We are in a const or static initializer,
792 if self.mode != Mode::Fn &&
794 // feature-gate is not enabled,
795 !self.tcx.sess.features.borrow().const_fn &&
797 // this doesn't come from a crate with the feature-gate enabled,
798 self.def_id.is_local() &&
800 // this doesn't come from a macro that has #[allow_internal_unstable]
801 !self.span.allows_unstable()
803 let mut err = self.tcx.sess.struct_span_err(self.span,
804 "const fns are an unstable feature");
806 "in Nightly builds, add `#![feature(const_fn)]` \
807 to the crate attributes to enable");
811 self.qualif = Qualif::NOT_CONST;
812 if self.mode != Mode::Fn {
813 // FIXME(#24111) Remove this check when const fn stabilizes
814 let (msg, note) = if let UnstableFeatures::Disallow =
815 self.tcx.sess.opts.unstable_features {
816 (format!("calls in {}s are limited to \
817 struct and enum constructors",
819 Some("a limited form of compile-time function \
820 evaluation is available on a nightly \
821 compiler via `const fn`"))
823 (format!("calls in {}s are limited \
824 to constant functions, \
825 struct and enum constructors",
829 let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
830 if let Some(note) = note {
831 err.span_note(self.span, note);
837 if let Some((ref dest, _)) = *destination {
838 // Avoid propagating irrelevant callee/argument qualifications.
839 if self.qualif.intersects(Qualif::CONST_ERROR) {
840 self.qualif = Qualif::NOT_CONST;
842 // Be conservative about the returned value of a const fn.
844 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
845 self.qualif = Qualif::empty();
848 // Let `const fn` transitively have destructors,
849 // but they do get stopped in `const` or `static`.
850 if self.mode != Mode::ConstFn {
854 self.assign(dest, location);
857 // Qualify any operands inside other terminators.
858 self.super_terminator_kind(bb, kind, location);
862 fn visit_assign(&mut self,
865 rvalue: &Rvalue<'tcx>,
866 location: Location) {
867 self.visit_rvalue(rvalue, location);
869 // Check the allowed const fn argument forms.
870 if let (Mode::ConstFn, &Lvalue::Local(index)) = (self.mode, dest) {
871 if self.mir.local_kind(index) == LocalKind::Var &&
872 self.const_fn_arg_vars.insert(index.index()) {
874 // Direct use of an argument is permitted.
875 if let Rvalue::Use(Operand::Consume(Lvalue::Local(local))) = *rvalue {
876 if self.mir.local_kind(local) == LocalKind::Arg {
881 // Avoid a generic error for other uses of arguments.
882 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
883 let decl = &self.mir.local_decls[index];
884 span_err!(self.tcx.sess, decl.source_info.span, E0022,
885 "arguments of constant functions can only \
886 be immutable by-value bindings");
892 self.assign(dest, location);
895 fn visit_source_info(&mut self, source_info: &SourceInfo) {
896 self.span = source_info.span;
899 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
901 this.visit_source_info(&statement.source_info);
902 match statement.kind {
903 StatementKind::Assign(ref lvalue, ref rvalue) => {
904 this.visit_assign(bb, lvalue, rvalue, location);
906 StatementKind::SetDiscriminant { .. } |
907 StatementKind::StorageLive(_) |
908 StatementKind::StorageDead(_) |
909 StatementKind::InlineAsm {..} |
910 StatementKind::Nop => {}
915 fn visit_terminator(&mut self,
917 terminator: &Terminator<'tcx>,
918 location: Location) {
919 self.nest(|this| this.super_terminator(bb, terminator, location));
923 pub fn provide(providers: &mut Providers) {
924 *providers = Providers {
930 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
933 // NB: This `borrow()` is guaranteed to be valid (i.e., the value
934 // cannot yet be stolen), because `mir_validated()`, which steals
935 // from `mir_const(), forces this query to execute before
936 // performing the steal.
937 let mir = &tcx.mir_const(def_id).borrow();
939 if mir.return_ty.references_error() {
940 return Qualif::NOT_CONST.bits();
943 let param_env = tcx.param_env(def_id);
945 let mut qualifier = Qualifier::new(tcx, param_env, def_id, mir, Mode::Const);
946 qualifier.qualify_const().bits()
949 pub struct QualifyAndPromoteConstants;
951 impl MirPass for QualifyAndPromoteConstants {
952 fn run_pass<'a, 'tcx>(&self,
953 tcx: TyCtxt<'a, 'tcx, 'tcx>,
955 mir: &mut Mir<'tcx>) {
956 let id = src.item_id();
957 let def_id = tcx.hir.local_def_id(id);
958 let mode = match src {
959 MirSource::Fn(_) => {
960 if is_const_fn(tcx, def_id) {
966 MirSource::Static(_, hir::MutImmutable) => Mode::Static,
967 MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
968 MirSource::Const(_) |
969 MirSource::Promoted(..) => return
971 let param_env = tcx.param_env(def_id);
973 if mode == Mode::Fn || mode == Mode::ConstFn {
974 // This is ugly because Qualifier holds onto mir,
975 // which can't be mutated until its scope ends.
976 let (temps, candidates) = {
977 let mut qualifier = Qualifier::new(tcx, param_env,
979 if mode == Mode::ConstFn {
980 // Enforce a constant-like CFG for `const fn`.
981 qualifier.qualify_const();
983 while let Some((bb, data)) = qualifier.rpo.next() {
984 qualifier.visit_basic_block_data(bb, data);
988 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
991 // Do the actual promotion, now that we know what's viable.
992 promote_consts::promote_candidates(mir, tcx, temps, candidates);
994 let mut qualifier = Qualifier::new(tcx, param_env, def_id, mir, mode);
995 qualifier.qualify_const();
998 // Statics must be Sync.
999 if mode == Mode::Static {
1000 let ty = mir.return_ty;
1001 tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
1002 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1003 let mut fulfillment_cx = traits::FulfillmentContext::new();
1004 fulfillment_cx.register_bound(&infcx, ty,
1005 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1007 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1008 infcx.report_fulfillment_errors(&err);