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};
19 use rustc::dep_graph::DepNode;
21 use rustc::hir::def_id::DefId;
22 use rustc::hir::intravisit::FnKind;
23 use rustc::hir::map::blocks::FnLikeNode;
24 use rustc::traits::{self, Reveal};
25 use rustc::ty::{self, TyCtxt, Ty};
26 use rustc::ty::cast::CastTy;
27 use rustc::mir::repr::*;
28 use rustc::mir::mir_map::MirMap;
29 use rustc::mir::traversal::{self, ReversePostorder};
30 use rustc::mir::transform::{Pass, MirMapPass, MirPassHook, MirSource};
31 use rustc::mir::visit::{LvalueContext, Visitor};
32 use rustc::util::nodemap::DefIdMap;
34 use syntax::feature_gate::UnstableFeatures;
37 use std::collections::hash_map::Entry;
41 use super::promote_consts::{self, Candidate, TempState};
45 // Const item's qualification while recursing.
46 // Recursive consts are an error.
47 const RECURSIVE = 1 << 0,
49 // Constant containing interior mutability (UnsafeCell).
50 const MUTABLE_INTERIOR = 1 << 1,
52 // Constant containing an ADT that implements Drop.
53 const NEEDS_DROP = 1 << 2,
56 const FN_ARGUMENT = 1 << 3,
58 // Static lvalue or move from a static.
59 const STATIC = 1 << 4,
61 // Reference to a static.
62 const STATIC_REF = 1 << 5,
64 // Not constant at all - non-`const fn` calls, asm!,
65 // pointer comparisons, ptr-to-int casts, etc.
66 const NOT_CONST = 1 << 6,
68 // Refers to temporaries which cannot be promoted as
69 // promote_consts decided they weren't simple enough.
70 const NOT_PROMOTABLE = 1 << 7,
72 // Borrows of temporaries can be promoted only
73 // if they have none of the above qualifications.
74 const NEVER_PROMOTE = !0,
76 // Const items can only have MUTABLE_INTERIOR
77 // and NOT_PROMOTABLE without producing an error.
78 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
79 !Qualif::NOT_PROMOTABLE.bits
83 impl<'a, 'tcx> Qualif {
84 /// Remove flags which are impossible for the given type.
85 fn restrict(&mut self, ty: Ty<'tcx>,
86 tcx: TyCtxt<'a, 'tcx, 'tcx>,
87 param_env: &ty::ParameterEnvironment<'tcx>) {
88 if !ty.type_contents(tcx).interior_unsafe() {
89 *self = *self - Qualif::MUTABLE_INTERIOR;
91 if !tcx.type_needs_drop_given_env(ty, param_env) {
92 *self = *self - Qualif::NEEDS_DROP;
97 /// What kind of item we are in.
98 #[derive(Copy, Clone, PartialEq, Eq)]
107 impl fmt::Display for Mode {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 Mode::Const => write!(f, "constant"),
111 Mode::Static | Mode::StaticMut => write!(f, "static"),
112 Mode::ConstFn => write!(f, "constant function"),
113 Mode::Fn => write!(f, "function")
118 fn is_const_fn(tcx: TyCtxt, def_id: DefId) -> bool {
119 if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
120 let fn_like = FnLikeNode::from_node(tcx.map.get(node_id));
121 match fn_like.map(|f| f.kind()) {
122 Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => {
123 c == hir::Constness::Const
125 Some(FnKind::Method(_, m, _, _)) => {
126 m.constness == hir::Constness::Const
131 tcx.sess.cstore.is_const_fn(def_id)
135 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
140 rpo: ReversePostorder<'a, 'tcx>,
141 tcx: TyCtxt<'a, 'gcx, 'tcx>,
142 param_env: ty::ParameterEnvironment<'tcx>,
143 qualif_map: &'a mut DefIdMap<Qualif>,
144 mir_map: Option<&'a MirMap<'tcx>>,
145 temp_qualif: IndexVec<Temp, Option<Qualif>>,
146 return_qualif: Option<Qualif>,
148 const_fn_arg_vars: BitVector,
149 temp_promotion_state: IndexVec<Temp, TempState>,
150 promotion_candidates: Vec<Candidate>
153 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
154 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
155 param_env: ty::ParameterEnvironment<'tcx>,
156 qualif_map: &'a mut DefIdMap<Qualif>,
157 mir_map: Option<&'a MirMap<'tcx>>,
161 -> Qualifier<'a, 'tcx, 'tcx> {
162 let mut rpo = traversal::reverse_postorder(mir);
163 let temps = promote_consts::collect_temps(mir, &mut rpo);
172 param_env: param_env,
173 qualif_map: qualif_map,
175 temp_qualif: IndexVec::from_elem(None, &mir.temp_decls),
177 qualif: Qualif::empty(),
178 const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
179 temp_promotion_state: temps,
180 promotion_candidates: vec![]
184 // FIXME(eddyb) we could split the errors into meaningful
185 // categories, but enabling full miri would make that
186 // slightly pointless (even with feature-gating).
187 fn not_const(&mut self) {
188 self.add(Qualif::NOT_CONST);
189 if self.mode != Mode::Fn {
190 span_err!(self.tcx.sess, self.span, E0019,
191 "{} contains unimplemented expression type", self.mode);
195 /// Error about extra statements in a constant.
196 fn statement_like(&mut self) {
197 self.add(Qualif::NOT_CONST);
198 if self.mode != Mode::Fn {
199 span_err!(self.tcx.sess, self.span, E0016,
200 "blocks in {}s are limited to items and tail expressions",
205 /// Add the given qualification to self.qualif.
206 fn add(&mut self, qualif: Qualif) {
207 self.qualif = self.qualif | qualif;
210 /// Add the given type's qualification to self.qualif.
211 fn add_type(&mut self, ty: Ty<'tcx>) {
212 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
213 self.qualif.restrict(ty, self.tcx, &self.param_env);
216 /// Within the provided closure, self.qualif will start
217 /// out empty, and its value after the closure returns will
218 /// be combined with the value before the call to nest.
219 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
220 let original = self.qualif;
221 self.qualif = Qualif::empty();
226 /// Check for NEEDS_DROP (from an ADT or const fn call) and
227 /// error, unless we're in a function, or the feature-gate
228 /// for globals with destructors is enabled.
229 fn deny_drop(&self) {
230 if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) {
234 // Static and const fn's allow destructors, but they're feature-gated.
235 let msg = if self.mode != Mode::Const {
236 // Feature-gate for globals with destructors is enabled.
237 if self.tcx.sess.features.borrow().drop_types_in_const {
241 // This comes from a macro that has #[allow_internal_unstable].
242 if self.tcx.sess.codemap().span_allows_unstable(self.span) {
246 format!("destructors in {}s are an unstable feature",
249 format!("{}s are not allowed to have destructors",
254 struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg);
255 if self.mode != Mode::Const {
257 "in Nightly builds, add `#![feature(drop_types_in_const)]` \
258 to the crate attributes to enable");
263 /// Check if an Lvalue with the current qualifications could
264 /// be consumed, by either an operand or a Deref projection.
265 fn try_consume(&mut self) -> bool {
266 if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
267 let msg = if self.mode == Mode::Static ||
268 self.mode == Mode::StaticMut {
269 "cannot refer to other statics by value, use the \
270 address-of operator or a constant instead"
272 "cannot refer to statics by value, use a constant instead"
274 struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg)
275 .span_label(self.span, &format!("referring to another static by value"))
276 .note(&format!("use the address-of operator or a constant instead"))
279 // Replace STATIC with NOT_CONST to avoid further errors.
280 self.qualif = self.qualif - Qualif::STATIC;
281 self.add(Qualif::NOT_CONST);
289 /// Assign the current qualification to the given destination.
290 fn assign(&mut self, dest: &Lvalue<'tcx>, location: Location) {
291 let qualif = self.qualif;
292 let span = self.span;
293 let store = |slot: &mut Option<Qualif>| {
295 span_bug!(span, "multiple assignments to {:?}", dest);
297 *slot = Some(qualif);
300 // Only handle promotable temps in non-const functions.
301 if self.mode == Mode::Fn {
302 if let Lvalue::Temp(index) = *dest {
303 if self.temp_promotion_state[index].is_promotable() {
304 store(&mut self.temp_qualif[index]);
311 Lvalue::Temp(index) => store(&mut self.temp_qualif[index]),
312 Lvalue::ReturnPointer => store(&mut self.return_qualif),
314 Lvalue::Projection(box Projection {
315 base: Lvalue::Temp(index),
316 elem: ProjectionElem::Deref
317 }) if self.mir.temp_decls[index].ty.is_unique()
318 && self.temp_qualif[index].map_or(false, |qualif| {
319 qualif.intersects(Qualif::NOT_CONST)
321 // Part of `box expr`, we should've errored
322 // already for the Box allocation Rvalue.
325 // This must be an explicit assignment.
327 // Catch more errors in the destination.
328 self.visit_lvalue(dest, LvalueContext::Store, location);
329 self.statement_like();
334 /// Qualify a whole const, static initializer or const fn.
335 fn qualify_const(&mut self) -> Qualif {
338 let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
339 let mut bb = START_BLOCK;
341 seen_blocks.insert(bb.index());
343 self.visit_basic_block_data(bb, &mir[bb]);
345 let target = match mir[bb].terminator().kind {
346 TerminatorKind::Goto { target } |
347 // Drops are considered noops.
348 TerminatorKind::Drop { target, .. } |
349 TerminatorKind::Assert { target, .. } |
350 TerminatorKind::Call { destination: Some((_, target)), .. } => {
354 // Non-terminating calls cannot produce any value.
355 TerminatorKind::Call { destination: None, .. } => {
356 return Qualif::empty();
359 TerminatorKind::If {..} |
360 TerminatorKind::Switch {..} |
361 TerminatorKind::SwitchInt {..} |
362 TerminatorKind::DropAndReplace { .. } |
363 TerminatorKind::Resume |
364 TerminatorKind::Unreachable => None,
366 TerminatorKind::Return => {
367 // Check for unused values. This usually means
368 // there are extra statements in the AST.
369 for temp in mir.temp_decls.indices() {
370 if self.temp_qualif[temp].is_none() {
374 let state = self.temp_promotion_state[temp];
375 if let TempState::Defined { location, uses: 0 } = state {
376 let data = &mir[location.block];
377 let stmt_idx = location.statement_index;
379 // Get the span for the initialization.
380 let source_info = if stmt_idx < data.statements.len() {
381 data.statements[stmt_idx].source_info
383 data.terminator().source_info
385 self.span = source_info.span;
387 // Treat this as a statement in the AST.
388 self.statement_like();
392 // Make sure there are no extra unassigned variables.
393 self.qualif = Qualif::NOT_CONST;
394 for index in 0..mir.var_decls.len() {
395 if !self.const_fn_arg_vars.contains(index) {
396 self.assign(&Lvalue::Var(Var::new(index)), Location {
398 statement_index: usize::MAX,
409 Some(target) if !seen_blocks.contains(target.index()) => {
419 let return_ty = mir.return_ty;
420 self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
424 // Check for destructors in static mut.
425 self.add_type(return_ty);
429 // Account for errors in consts by using the
430 // conservative type qualification instead.
431 if self.qualif.intersects(Qualif::CONST_ERROR) {
432 self.qualif = Qualif::empty();
433 self.add_type(return_ty);
441 /// Accumulates an Rvalue or Call's effects in self.qualif.
442 /// For functions (constant or not), it also records
443 /// candidates for promotion in promotion_candidates.
444 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
445 fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext, location: Location) {
448 self.add(Qualif::FN_ARGUMENT);
451 self.add(Qualif::NOT_CONST);
453 Lvalue::Temp(index) => {
454 if !self.temp_promotion_state[index].is_promotable() {
455 self.add(Qualif::NOT_PROMOTABLE);
458 if let Some(qualif) = self.temp_qualif[index] {
464 Lvalue::Static(_) => {
465 self.add(Qualif::STATIC);
466 if self.mode == Mode::Const || self.mode == Mode::ConstFn {
467 span_err!(self.tcx.sess, self.span, E0013,
468 "{}s cannot refer to statics, use \
469 a constant instead", self.mode);
472 Lvalue::ReturnPointer => {
475 Lvalue::Projection(ref proj) => {
477 this.super_lvalue(lvalue, context, location);
479 ProjectionElem::Deref => {
480 if !this.try_consume() {
484 if this.qualif.intersects(Qualif::STATIC_REF) {
485 this.qualif = this.qualif - Qualif::STATIC_REF;
486 this.add(Qualif::STATIC);
489 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
490 if let ty::TyRawPtr(_) = base_ty.sty {
491 this.add(Qualif::NOT_CONST);
492 if this.mode != Mode::Fn {
493 struct_span_err!(this.tcx.sess,
495 "raw pointers cannot be dereferenced in {}s",
497 .span_label(this.span,
498 &format!("dereference of raw pointer in constant"))
504 ProjectionElem::Field(..) |
505 ProjectionElem::Index(_) => {
506 if this.mode != Mode::Fn &&
507 this.qualif.intersects(Qualif::STATIC) {
508 span_err!(this.tcx.sess, this.span, E0494,
509 "cannot refer to the interior of another \
510 static, use a constant instead");
512 let ty = lvalue.ty(this.mir, this.tcx).to_ty(this.tcx);
513 this.qualif.restrict(ty, this.tcx, &this.param_env);
516 ProjectionElem::ConstantIndex {..} |
517 ProjectionElem::Subslice {..} |
518 ProjectionElem::Downcast(..) => {
527 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
529 Operand::Consume(_) => {
531 this.super_operand(operand, location);
535 Operand::Constant(ref constant) => {
536 // Only functions and methods can have these types.
537 if let ty::TyFnDef(..) = constant.ty.sty {
541 if let Literal::Item { def_id, substs } = constant.literal {
542 // Don't peek inside generic (associated) constants.
543 if !substs.types.is_empty() {
544 self.add_type(constant.ty);
546 let qualif = qualify_const_item_cached(self.tcx,
553 // FIXME(eddyb) check recursive constants here,
554 // instead of rustc_passes::static_recursion.
555 if self.qualif.intersects(Qualif::RECURSIVE) {
556 span_bug!(constant.span,
557 "recursive constant wasn't caught earlier");
560 // Let `const fn` transitively have destructors,
561 // but they do get stopped in `const` or `static`.
562 if self.mode != Mode::ConstFn {
570 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
571 // Recurse through operands and lvalues.
572 self.super_rvalue(rvalue, location);
577 Rvalue::UnaryOp(..) |
578 Rvalue::CheckedBinaryOp(..) |
579 Rvalue::Cast(CastKind::ReifyFnPointer, _, _) |
580 Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
581 Rvalue::Cast(CastKind::Unsize, _, _) => {}
584 // Static lvalues in consts would have errored already,
585 // don't treat length checks as reads from statics.
586 self.qualif = self.qualif - Qualif::STATIC;
589 Rvalue::Ref(_, kind, ref lvalue) => {
590 // Static lvalues in consts would have errored already,
591 // only keep track of references to them here.
592 if self.qualif.intersects(Qualif::STATIC) {
593 self.qualif = self.qualif - Qualif::STATIC;
594 self.add(Qualif::STATIC_REF);
597 let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
598 if kind == BorrowKind::Mut {
599 // In theory, any zero-sized value could be borrowed
600 // mutably without consequences. However, only &mut []
601 // is allowed right now, and only in functions.
602 let allow = if self.mode == Mode::StaticMut {
603 // Inside a `static mut`, &mut [...] is also allowed.
605 ty::TyArray(..) | ty::TySlice(_) => {
606 // Mutating can expose drops, be conservative.
613 } else if let ty::TyArray(_, 0) = ty.sty {
614 self.mode == Mode::Fn
620 self.add(Qualif::NOT_CONST);
621 if self.mode != Mode::Fn {
622 struct_span_err!(self.tcx.sess, self.span, E0017,
623 "references in {}s may only refer \
624 to immutable values", self.mode)
625 .span_label(self.span, &format!("{}s require immutable values",
631 // Constants cannot be borrowed if they contain interior mutability as
632 // it means that our "silent insertion of statics" could change
633 // initializer values (very bad).
634 if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
635 // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
636 // duplicate errors (from reborrowing, for example).
637 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
638 self.add(Qualif::NOT_CONST);
639 if self.mode != Mode::Fn {
640 span_err!(self.tcx.sess, self.span, E0492,
641 "cannot borrow a constant which contains \
642 interior mutability, create a static instead");
647 // We might have a candidate for promotion.
648 let candidate = Candidate::Ref(location);
649 if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
650 if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
651 // We can only promote direct borrows of temps.
652 if let Lvalue::Temp(_) = *lvalue {
653 self.promotion_candidates.push(candidate);
659 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
660 let operand_ty = operand.ty(self.mir, self.tcx);
661 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
662 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
663 match (cast_in, cast_out) {
664 (CastTy::Ptr(_), CastTy::Int(_)) |
665 (CastTy::FnPtr, CastTy::Int(_)) => {
666 self.add(Qualif::NOT_CONST);
667 if self.mode != Mode::Fn {
668 span_err!(self.tcx.sess, self.span, E0018,
669 "raw pointers cannot be cast to integers in {}s",
677 Rvalue::BinaryOp(op, ref lhs, _) => {
678 if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
679 assert!(op == BinOp::Eq || op == BinOp::Ne ||
680 op == BinOp::Le || op == BinOp::Lt ||
681 op == BinOp::Ge || op == BinOp::Gt);
683 self.add(Qualif::NOT_CONST);
684 if self.mode != Mode::Fn {
686 self.tcx.sess, self.span, E0395,
687 "raw pointers cannot be compared in {}s",
691 &format!("comparing raw pointers in static"))
698 self.add(Qualif::NOT_CONST);
699 if self.mode != Mode::Fn {
700 struct_span_err!(self.tcx.sess, self.span, E0010,
701 "allocations are not allowed in {}s", self.mode)
702 .span_label(self.span, &format!("allocation not allowed in {}s", self.mode))
707 Rvalue::Aggregate(ref kind, _) => {
708 if let AggregateKind::Adt(def, _, _) = *kind {
710 self.add(Qualif::NEEDS_DROP);
714 if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() {
715 let ty = rvalue.ty(self.mir, self.tcx).unwrap();
717 assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
718 // Even if the value inside may not need dropping,
719 // mutating it would change that.
720 if !self.qualif.intersects(Qualif::NOT_CONST) {
727 Rvalue::InlineAsm {..} => {
733 fn visit_terminator_kind(&mut self,
735 kind: &TerminatorKind<'tcx>,
736 location: Location) {
737 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
738 self.visit_operand(func, location);
740 let fn_ty = func.ty(self.mir, self.tcx);
741 let (is_shuffle, is_const_fn) = match fn_ty.sty {
742 ty::TyFnDef(def_id, _, f) => {
743 (f.abi == Abi::PlatformIntrinsic &&
744 self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
745 is_const_fn(self.tcx, def_id))
750 for (i, arg) in args.iter().enumerate() {
752 this.visit_operand(arg, location);
753 if is_shuffle && i == 2 && this.mode == Mode::Fn {
754 let candidate = Candidate::ShuffleIndices(bb);
755 if !this.qualif.intersects(Qualif::NEVER_PROMOTE) {
756 this.promotion_candidates.push(candidate);
758 span_err!(this.tcx.sess, this.span, E0526,
759 "shuffle indices are not constant");
767 // We are in a const or static initializer,
768 if self.mode != Mode::Fn &&
770 // feature-gate is not enabled,
771 !self.tcx.sess.features.borrow().const_fn &&
773 // this doesn't come from a crate with the feature-gate enabled,
774 self.def_id.is_local() &&
776 // this doesn't come from a macro that has #[allow_internal_unstable]
777 !self.tcx.sess.codemap().span_allows_unstable(self.span)
779 let mut err = self.tcx.sess.struct_span_err(self.span,
780 "const fns are an unstable feature");
782 "in Nightly builds, add `#![feature(const_fn)]` \
783 to the crate attributes to enable");
787 self.qualif = Qualif::NOT_CONST;
788 if self.mode != Mode::Fn {
789 // FIXME(#24111) Remove this check when const fn stabilizes
790 let (msg, note) = if let UnstableFeatures::Disallow =
791 self.tcx.sess.opts.unstable_features {
792 (format!("calls in {}s are limited to \
793 struct and enum constructors",
795 Some("a limited form of compile-time function \
796 evaluation is available on a nightly \
797 compiler via `const fn`"))
799 (format!("calls in {}s are limited \
800 to constant functions, \
801 struct and enum constructors",
805 let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
806 if let Some(note) = note {
807 err.span_note(self.span, note);
813 if let Some((ref dest, _)) = *destination {
814 // Avoid propagating irrelevant callee/argument qualifications.
815 if self.qualif.intersects(Qualif::CONST_ERROR) {
816 self.qualif = Qualif::NOT_CONST;
818 // Be conservative about the returned value of a const fn.
820 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
821 self.qualif = Qualif::empty();
824 // Let `const fn` transitively have destructors,
825 // but they do get stopped in `const` or `static`.
826 if self.mode != Mode::ConstFn {
830 self.assign(dest, location);
833 // Qualify any operands inside other terminators.
834 self.super_terminator_kind(bb, kind, location);
838 fn visit_assign(&mut self,
841 rvalue: &Rvalue<'tcx>,
842 location: Location) {
843 self.visit_rvalue(rvalue, location);
845 // Check the allowed const fn argument forms.
846 if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
847 if self.const_fn_arg_vars.insert(index.index()) {
848 // Direct use of an argument is permitted.
849 if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
853 // Avoid a generic error for other uses of arguments.
854 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
855 let decl = &self.mir.var_decls[index];
856 span_err!(self.tcx.sess, decl.source_info.span, E0022,
857 "arguments of constant functions can only \
858 be immutable by-value bindings");
864 self.assign(dest, location);
867 fn visit_source_info(&mut self, source_info: &SourceInfo) {
868 self.span = source_info.span;
871 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
873 this.visit_source_info(&statement.source_info);
874 match statement.kind {
875 StatementKind::Assign(ref lvalue, ref rvalue) => {
876 this.visit_assign(bb, lvalue, rvalue, location);
878 StatementKind::SetDiscriminant { .. } |
879 StatementKind::StorageLive(_) |
880 StatementKind::StorageDead(_) => {}
885 fn visit_terminator(&mut self,
887 terminator: &Terminator<'tcx>,
888 location: Location) {
889 self.nest(|this| this.super_terminator(bb, terminator, location));
893 fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
894 qualif_map: &mut DefIdMap<Qualif>,
895 mir_map: Option<&MirMap<'tcx>>,
898 match qualif_map.entry(def_id) {
899 Entry::Occupied(entry) => return *entry.get(),
900 Entry::Vacant(entry) => {
901 // Guard against `const` recursion.
902 entry.insert(Qualif::RECURSIVE);
907 let param_env_and_mir = if def_id.is_local() {
908 mir_map.and_then(|map| map.map.get(&def_id)).map(|mir| {
909 let node_id = tcx.map.as_local_node_id(def_id).unwrap();
910 (ty::ParameterEnvironment::for_item(tcx, node_id), mir)
912 } else if let Some(mir) = tcx.sess.cstore.maybe_get_item_mir(tcx, def_id) {
913 // These should only be monomorphic constants.
915 Some((tcx.empty_parameter_environment(), &extern_mir))
920 let (param_env, mir) = param_env_and_mir.unwrap_or_else(|| {
921 bug!("missing constant MIR for {}", tcx.item_path_str(def_id))
924 let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, mir_map,
925 def_id, mir, Mode::Const);
926 let qualif = qualifier.qualify_const();
927 qualifier.qualif_map.insert(def_id, qualif);
931 pub struct QualifyAndPromoteConstants;
933 impl Pass for QualifyAndPromoteConstants {}
935 impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
936 fn run_pass<'a>(&mut self,
937 tcx: TyCtxt<'a, 'tcx, 'tcx>,
938 map: &mut MirMap<'tcx>,
939 hooks: &mut [Box<for<'s> MirPassHook<'s>>]) {
940 let mut qualif_map = DefIdMap();
942 // First, visit `const` items, potentially recursing, to get
943 // accurate MUTABLE_INTERIOR and NEEDS_DROP qualifications.
944 let keys = map.map.keys();
945 for &def_id in &keys {
946 let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
947 let id = tcx.map.as_local_node_id(def_id).unwrap();
948 let src = MirSource::from_node(tcx, id);
949 if let MirSource::Const(_) = src {
950 qualify_const_item_cached(tcx, &mut qualif_map, Some(map), def_id);
954 // Then, handle everything else, without recursing,
955 // as the MIR map is not shared, since promotion
956 // in functions (including `const fn`) mutates it.
957 for &def_id in &keys {
958 let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
959 let id = tcx.map.as_local_node_id(def_id).unwrap();
960 let src = MirSource::from_node(tcx, id);
961 let mode = match src {
962 MirSource::Fn(_) => {
963 if is_const_fn(tcx, def_id) {
969 MirSource::Const(_) => continue,
970 MirSource::Static(_, hir::MutImmutable) => Mode::Static,
971 MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
972 MirSource::Promoted(..) => bug!()
974 let param_env = ty::ParameterEnvironment::for_item(tcx, id);
976 let mir = map.map.get_mut(&def_id).unwrap();
977 for hook in &mut *hooks {
978 hook.on_mir_pass(tcx, src, mir, self, false);
981 if mode == Mode::Fn || mode == Mode::ConstFn {
982 // This is ugly because Qualifier holds onto mir,
983 // which can't be mutated until its scope ends.
984 let (temps, candidates) = {
985 let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
986 None, def_id, mir, mode);
987 if mode == Mode::ConstFn {
988 // Enforce a constant-like CFG for `const fn`.
989 qualifier.qualify_const();
991 while let Some((bb, data)) = qualifier.rpo.next() {
992 qualifier.visit_basic_block_data(bb, data);
996 (qualifier.temp_promotion_state,
997 qualifier.promotion_candidates)
1000 // Do the actual promotion, now that we know what's viable.
1001 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1003 let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
1004 None, def_id, mir, mode);
1005 qualifier.qualify_const();
1008 for hook in &mut *hooks {
1009 hook.on_mir_pass(tcx, src, mir, self, true);
1012 // Statics must be Sync.
1013 if mode == Mode::Static {
1014 let ty = mir.return_ty;
1015 tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
1016 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1017 let mut fulfillment_cx = traits::FulfillmentContext::new();
1018 fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
1019 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1020 infcx.report_fulfillment_errors(&err);
1023 if let Err(errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
1024 infcx.report_fulfillment_errors_as_warnings(&errors, id);