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::BitArray;
18 use rustc_data_structures::indexed_set::IdxSet;
19 use rustc_data_structures::indexed_vec::IndexVec;
20 use rustc_data_structures::fx::FxHashSet;
22 use rustc::hir::def_id::DefId;
23 use rustc::mir::interpret::ConstValue;
24 use rustc::traits::{self, TraitEngine};
25 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
26 use rustc::ty::cast::CastTy;
27 use rustc::ty::query::Providers;
29 use rustc::mir::traversal::ReversePostorder;
30 use rustc::mir::visit::{PlaceContext, Visitor};
31 use rustc::middle::lang_items;
32 use rustc_target::spec::abi::Abi;
34 use syntax::ast::LitKind;
35 use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue};
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 // Not constant at all - non-`const fn` calls, asm!,
60 // pointer comparisons, ptr-to-int casts, etc.
61 const NOT_CONST = 1 << 3;
63 // Refers to temporaries which cannot be promoted as
64 // promote_consts decided they weren't simple enough.
65 const NOT_PROMOTABLE = 1 << 4;
67 // Const items can only have MUTABLE_INTERIOR
68 // and NOT_PROMOTABLE without producing an error.
69 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
70 !Qualif::NOT_PROMOTABLE.bits;
74 impl<'a, 'tcx> Qualif {
75 /// Remove flags which are impossible for the given type.
76 fn restrict(&mut self, ty: Ty<'tcx>,
77 tcx: TyCtxt<'a, 'tcx, 'tcx>,
78 param_env: ty::ParamEnv<'tcx>) {
79 if ty.is_freeze(tcx, param_env, DUMMY_SP) {
80 *self = *self - Qualif::MUTABLE_INTERIOR;
82 if !ty.needs_drop(tcx, param_env) {
83 *self = *self - Qualif::NEEDS_DROP;
88 /// What kind of item we are in.
89 #[derive(Copy, Clone, PartialEq, Eq)]
98 impl fmt::Display for Mode {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 Mode::Const => write!(f, "constant"),
102 Mode::Static | Mode::StaticMut => write!(f, "static"),
103 Mode::ConstFn => write!(f, "constant function"),
104 Mode::Fn => write!(f, "function")
109 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
114 rpo: ReversePostorder<'a, 'tcx>,
115 tcx: TyCtxt<'a, 'gcx, 'tcx>,
116 param_env: ty::ParamEnv<'tcx>,
117 local_qualif: IndexVec<Local, Option<Qualif>>,
119 const_fn_arg_vars: BitArray<Local>,
120 temp_promotion_state: IndexVec<Local, TempState>,
121 promotion_candidates: Vec<Candidate>
124 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
125 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
129 -> Qualifier<'a, 'tcx, 'tcx> {
130 assert!(def_id.is_local());
131 let mut rpo = traversal::reverse_postorder(mir);
132 let temps = promote_consts::collect_temps(mir, &mut rpo);
135 let param_env = tcx.param_env(def_id);
137 let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
138 for arg in mir.args_iter() {
139 let mut qualif = Qualif::NEEDS_DROP;
140 qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
141 local_qualif[arg] = Some(qualif);
153 qualif: Qualif::empty(),
154 const_fn_arg_vars: BitArray::new(mir.local_decls.len()),
155 temp_promotion_state: temps,
156 promotion_candidates: vec![]
160 // FIXME(eddyb) we could split the errors into meaningful
161 // categories, but enabling full miri would make that
162 // slightly pointless (even with feature-gating).
163 fn not_const(&mut self) {
164 self.add(Qualif::NOT_CONST);
165 if self.mode != Mode::Fn {
166 let mut err = struct_span_err!(
170 "{} contains unimplemented expression type",
173 if self.tcx.sess.teach(&err.get_code().unwrap()) {
174 err.note("A function call isn't allowed in the const's initialization expression \
175 because the expression's value must be known at compile-time.");
176 err.note("Remember: you can't use a function call inside a const's initialization \
177 expression! However, you can use it anywhere else.");
183 /// Error about extra statements in a constant.
184 fn statement_like(&mut self) {
185 self.add(Qualif::NOT_CONST);
186 if self.mode != Mode::Fn {
187 let mut err = feature_err(
188 &self.tcx.sess.parse_sess,
192 &format!("statements in {}s are unstable", self.mode),
194 if self.tcx.sess.teach(&err.get_code().unwrap()) {
195 err.note("Blocks in constants may only contain items (such as constant, function \
196 definition, etc...) and a tail expression.");
197 err.help("To avoid it, you have to replace the non-item object.");
203 /// Add the given qualification to self.qualif.
204 fn add(&mut self, qualif: Qualif) {
205 self.qualif = self.qualif | qualif;
208 /// Add the given type's qualification to self.qualif.
209 fn add_type(&mut self, ty: Ty<'tcx>) {
210 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
211 self.qualif.restrict(ty, self.tcx, self.param_env);
214 /// Within the provided closure, self.qualif will start
215 /// out empty, and its value after the closure returns will
216 /// be combined with the value before the call to nest.
217 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
218 let original = self.qualif;
219 self.qualif = Qualif::empty();
224 /// Assign the current qualification to the given destination.
225 fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
226 trace!("assign: {:?}", dest);
227 let qualif = self.qualif;
228 let span = self.span;
229 let store = |slot: &mut Option<Qualif>| {
231 span_bug!(span, "multiple assignments to {:?}", dest);
233 *slot = Some(qualif);
236 // Only handle promotable temps in non-const functions.
237 if self.mode == Mode::Fn {
238 if let Place::Local(index) = *dest {
239 if self.mir.local_kind(index) == LocalKind::Temp
240 && self.temp_promotion_state[index].is_promotable() {
241 debug!("store to promotable temp {:?} ({:?})", index, qualif);
242 store(&mut self.local_qualif[index]);
249 Place::Local(index) if (self.mir.local_kind(index) == LocalKind::Var ||
250 self.mir.local_kind(index) == LocalKind::Arg) &&
251 self.tcx.sess.features_untracked().const_let => {
252 debug!("store to var {:?}", index);
253 self.local_qualif[index] = Some(self.qualif);
255 Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp ||
256 self.mir.local_kind(index) == LocalKind::ReturnPointer => {
257 debug!("store to {:?} (temp or return pointer)", index);
258 store(&mut self.local_qualif[index])
261 Place::Projection(box Projection {
262 base: Place::Local(index),
263 elem: ProjectionElem::Deref
264 }) if self.mir.local_kind(index) == LocalKind::Temp
265 && self.mir.local_decls[index].ty.is_box()
266 && self.local_qualif[index].map_or(false, |qualif| {
267 qualif.contains(Qualif::NOT_CONST)
269 // Part of `box expr`, we should've errored
270 // already for the Box allocation Rvalue.
273 // This must be an explicit assignment.
275 // Catch more errors in the destination.
276 self.visit_place(dest, PlaceContext::Store, location);
277 self.statement_like();
282 /// Qualify a whole const, static initializer or const fn.
283 fn qualify_const(&mut self) -> (Qualif, Lrc<IdxSet<Local>>) {
284 debug!("qualifying {} {:?}", self.mode, self.def_id);
288 let mut seen_blocks = BitArray::new(mir.basic_blocks().len());
289 let mut bb = START_BLOCK;
291 seen_blocks.insert(bb.index());
293 self.visit_basic_block_data(bb, &mir[bb]);
295 let target = match mir[bb].terminator().kind {
296 TerminatorKind::Goto { target } |
297 TerminatorKind::Drop { target, .. } |
298 TerminatorKind::Assert { target, .. } |
299 TerminatorKind::Call { destination: Some((_, target)), .. } => {
303 // Non-terminating calls cannot produce any value.
304 TerminatorKind::Call { destination: None, .. } => {
308 TerminatorKind::SwitchInt {..} |
309 TerminatorKind::DropAndReplace { .. } |
310 TerminatorKind::Resume |
311 TerminatorKind::Abort |
312 TerminatorKind::GeneratorDrop |
313 TerminatorKind::Yield { .. } |
314 TerminatorKind::Unreachable |
315 TerminatorKind::FalseEdges { .. } |
316 TerminatorKind::FalseUnwind { .. } => None,
318 TerminatorKind::Return => {
319 if !self.tcx.sess.features_untracked().const_let {
320 // Check for unused values. This usually means
321 // there are extra statements in the AST.
322 for temp in mir.temps_iter() {
323 if self.local_qualif[temp].is_none() {
327 let state = self.temp_promotion_state[temp];
328 if let TempState::Defined { location, uses: 0 } = state {
329 let data = &mir[location.block];
330 let stmt_idx = location.statement_index;
332 // Get the span for the initialization.
333 let source_info = if stmt_idx < data.statements.len() {
334 data.statements[stmt_idx].source_info
336 data.terminator().source_info
338 self.span = source_info.span;
340 // Treat this as a statement in the AST.
341 self.statement_like();
345 // Make sure there are no extra unassigned variables.
346 self.qualif = Qualif::NOT_CONST;
347 for index in mir.vars_iter() {
348 if !self.const_fn_arg_vars.contains(index) {
349 debug!("unassigned variable {:?}", index);
350 self.assign(&Place::Local(index), Location {
352 statement_index: usize::MAX,
364 Some(target) if !seen_blocks.contains(target.index()) => {
374 self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
376 // Account for errors in consts by using the
377 // conservative type qualification instead.
378 if self.qualif.intersects(Qualif::CONST_ERROR) {
379 self.qualif = Qualif::empty();
380 let return_ty = mir.return_ty();
381 self.add_type(return_ty);
385 // Collect all the temps we need to promote.
386 let mut promoted_temps = IdxSet::new_empty(self.temp_promotion_state.len());
388 for candidate in &self.promotion_candidates {
390 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
391 match self.mir[bb].statements[stmt_idx].kind {
392 StatementKind::Assign(_, Rvalue::Ref(_, _, Place::Local(index))) => {
393 promoted_temps.add(&index);
398 Candidate::Argument { .. } => {}
402 (self.qualif, Lrc::new(promoted_temps))
405 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
406 Some(def_id) == self.tcx.lang_items().panic_fn() ||
407 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
411 /// Accumulates an Rvalue or Call's effects in self.qualif.
412 /// For functions (constant or not), it also records
413 /// candidates for promotion in promotion_candidates.
414 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
415 fn visit_local(&mut self,
417 _: PlaceContext<'tcx>,
419 let kind = self.mir.local_kind(local);
421 LocalKind::ReturnPointer => {
424 LocalKind::Var if !self.tcx.sess.features_untracked().const_let => {
425 if self.mode != Mode::Fn {
426 emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
427 self.span, GateIssue::Language,
428 &format!("let bindings in {}s are unstable",self.mode));
430 self.add(Qualif::NOT_CONST);
435 if let LocalKind::Arg = kind {
436 self.add(Qualif::FN_ARGUMENT);
439 if !self.temp_promotion_state[local].is_promotable() {
440 self.add(Qualif::NOT_PROMOTABLE);
443 if let Some(qualif) = self.local_qualif[local] {
452 fn visit_place(&mut self,
454 context: PlaceContext<'tcx>,
455 location: Location) {
457 Place::Local(ref local) => self.visit_local(local, context, location),
458 Place::Promoted(_) => bug!("promoting already promoted MIR"),
459 Place::Static(ref global) => {
461 .get_attrs(global.def_id)
463 .any(|attr| attr.check_name("thread_local")) {
464 if self.mode != Mode::Fn {
465 span_err!(self.tcx.sess, self.span, E0625,
466 "thread-local statics cannot be \
467 accessed at compile-time");
469 self.add(Qualif::NOT_CONST);
473 // Only allow statics (not consts) to refer to other statics.
474 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
477 self.add(Qualif::NOT_CONST);
479 if self.mode != Mode::Fn {
480 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
481 "{}s cannot refer to statics, use \
482 a constant instead", self.mode);
483 if self.tcx.sess.teach(&err.get_code().unwrap()) {
485 "Static and const variables can refer to other const variables. But a \
486 const variable cannot refer to a static variable."
489 "To fix this, the value can be extracted as a const and then used."
495 Place::Projection(ref proj) => {
497 this.super_place(place, context, location);
499 ProjectionElem::Deref => {
500 if let Mode::Fn = this.mode {
501 this.add(Qualif::NOT_CONST);
503 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
504 if let ty::RawPtr(_) = base_ty.sty {
505 if !this.tcx.sess.features_untracked().const_raw_ptr_deref {
507 &this.tcx.sess.parse_sess, "const_raw_ptr_deref",
508 this.span, GateIssue::Language,
510 "dereferencing raw pointers in {}s is unstable",
519 ProjectionElem::Field(..) |
520 ProjectionElem::Index(_) => {
521 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
522 if let Some(def) = base_ty.ty_adt_def() {
525 Mode::Fn => this.not_const(),
527 if !this.tcx.sess.features_untracked().const_fn_union {
529 &this.tcx.sess.parse_sess, "const_fn_union",
530 this.span, GateIssue::Language,
531 "unions in const fn are unstable",
544 let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
545 this.qualif.restrict(ty, this.tcx, this.param_env);
548 ProjectionElem::ConstantIndex {..} |
549 ProjectionElem::Subslice {..} |
550 ProjectionElem::Downcast(..) => {
559 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
560 self.super_operand(operand, location);
564 Operand::Move(_) => {
565 // Mark the consumed locals to indicate later drops are noops.
566 if let Operand::Move(Place::Local(local)) = *operand {
567 self.local_qualif[local] = self.local_qualif[local].map(|q|
568 q - Qualif::NEEDS_DROP
572 Operand::Constant(ref constant) => {
573 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
574 // Don't peek inside trait associated constants.
575 if self.tcx.trait_of_item(def_id).is_some() {
576 self.add_type(constant.literal.ty);
578 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
580 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
583 // Just in case the type is more specific than
584 // the definition, e.g. impl associated const
585 // with type parameters, take it into account.
586 self.qualif.restrict(constant.literal.ty, self.tcx, self.param_env);
593 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
594 // Recurse through operands and places.
595 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
596 let mut is_reborrow = false;
597 if let Place::Projection(ref proj) = *place {
598 if let ProjectionElem::Deref = proj.elem {
599 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
600 if let ty::Ref(..) = base_ty.sty {
607 self.super_place(place, PlaceContext::Borrow {
612 self.super_rvalue(rvalue, location);
615 self.super_rvalue(rvalue, location);
621 Rvalue::UnaryOp(UnOp::Neg, _) |
622 Rvalue::UnaryOp(UnOp::Not, _) |
623 Rvalue::NullaryOp(NullOp::SizeOf, _) |
624 Rvalue::CheckedBinaryOp(..) |
625 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
626 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
627 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
628 Rvalue::Cast(CastKind::Unsize, ..) |
629 Rvalue::Discriminant(..) |
632 Rvalue::Ref(_, kind, ref place) => {
633 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
635 // Default to forbidding the borrow and/or its promotion,
636 // due to the potential for direct or interior mutability,
637 // and only proceed by setting `forbidden_mut` to `false`.
638 let mut forbidden_mut = true;
640 if let BorrowKind::Mut { .. } = kind {
641 // In theory, any zero-sized value could be borrowed
642 // mutably without consequences. However, only &mut []
643 // is allowed right now, and only in functions.
644 if self.mode == Mode::StaticMut {
645 // Inside a `static mut`, &mut [...] is also allowed.
647 ty::Array(..) | ty::Slice(_) => forbidden_mut = false,
650 } else if let ty::Array(_, len) = ty.sty {
651 // FIXME(eddyb) the `self.mode == Mode::Fn` condition
652 // seems unnecessary, given that this is merely a ZST.
653 if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
654 forbidden_mut = false;
659 self.add(Qualif::NOT_CONST);
660 if self.mode != Mode::Fn {
661 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
662 "references in {}s may only refer \
663 to immutable values", self.mode);
664 err.span_label(self.span, format!("{}s require immutable values",
666 if self.tcx.sess.teach(&err.get_code().unwrap()) {
667 err.note("References in statics and constants may only refer to \
668 immutable values.\n\n\
669 Statics are shared everywhere, and if they refer to \
670 mutable data one might violate memory safety since \
671 holding multiple mutable references to shared data is \
673 If you really want global mutable state, try using \
674 static mut or a global UnsafeCell.");
680 // Constants cannot be borrowed if they contain interior mutability as
681 // it means that our "silent insertion of statics" could change
682 // initializer values (very bad).
683 if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
684 // A reference of a MUTABLE_INTERIOR place is instead
685 // NOT_CONST (see `if forbidden_mut` below), to avoid
686 // duplicate errors (from reborrowing, for example).
687 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
688 if self.mode != Mode::Fn {
689 span_err!(self.tcx.sess, self.span, E0492,
690 "cannot borrow a constant which may contain \
691 interior mutability, create a static instead");
694 // We allow immutable borrows of frozen data.
695 forbidden_mut = false;
700 self.add(Qualif::NOT_CONST);
702 // We might have a candidate for promotion.
703 let candidate = Candidate::Ref(location);
704 // We can only promote interior borrows of promotable temps.
705 let mut place = place;
706 while let Place::Projection(ref proj) = *place {
707 if proj.elem == ProjectionElem::Deref {
712 if let Place::Local(local) = *place {
713 if self.mir.local_kind(local) == LocalKind::Temp {
714 if let Some(qualif) = self.local_qualif[local] {
715 // `forbidden_mut` is false, so we can safely ignore
716 // `MUTABLE_INTERIOR` from the local's qualifications.
717 // This allows borrowing fields which don't have
718 // `MUTABLE_INTERIOR`, from a type that does, e.g.:
719 // `let _: &'static _ = &(Cell::new(1), 2).1;`
720 if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
721 self.promotion_candidates.push(candidate);
729 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
730 let operand_ty = operand.ty(self.mir, self.tcx);
731 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
732 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
733 match (cast_in, cast_out) {
734 (CastTy::Ptr(_), CastTy::Int(_)) |
735 (CastTy::FnPtr, CastTy::Int(_)) => {
736 if let Mode::Fn = self.mode {
737 self.add(Qualif::NOT_CONST);
738 } else if !self.tcx.sess.features_untracked().const_raw_ptr_to_usize_cast {
740 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
741 self.span, GateIssue::Language,
743 "casting pointers to integers in {}s is unstable",
753 Rvalue::BinaryOp(op, ref lhs, _) => {
754 if let ty::RawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
755 assert!(op == BinOp::Eq || op == BinOp::Ne ||
756 op == BinOp::Le || op == BinOp::Lt ||
757 op == BinOp::Ge || op == BinOp::Gt ||
758 op == BinOp::Offset);
760 if let Mode::Fn = self.mode {
761 self.add(Qualif::NOT_CONST);
762 } else if !self.tcx.sess.features_untracked().const_compare_raw_pointers {
764 &self.tcx.sess.parse_sess,
765 "const_compare_raw_pointers",
768 &format!("comparing raw pointers inside {}", self.mode),
774 Rvalue::NullaryOp(NullOp::Box, _) => {
775 self.add(Qualif::NOT_CONST);
776 if self.mode != Mode::Fn {
777 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
778 "allocations are not allowed in {}s", self.mode);
779 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
780 if self.tcx.sess.teach(&err.get_code().unwrap()) {
782 "The value of statics and constants must be known at compile time, \
783 and they live for the entire lifetime of a program. Creating a boxed \
784 value allocates memory on the heap at runtime, and therefore cannot \
785 be done at compile time."
792 Rvalue::Aggregate(ref kind, _) => {
793 if let AggregateKind::Adt(def, ..) = **kind {
794 if def.has_dtor(self.tcx) {
795 self.add(Qualif::NEEDS_DROP);
798 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
799 let ty = rvalue.ty(self.mir, self.tcx);
801 assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
808 fn visit_terminator_kind(&mut self,
810 kind: &TerminatorKind<'tcx>,
811 location: Location) {
812 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
813 self.visit_operand(func, location);
815 let fn_ty = func.ty(self.mir, self.tcx);
816 let mut callee_def_id = None;
817 let (mut is_shuffle, mut is_const_fn) = (false, None);
818 if let ty::FnDef(def_id, _) = fn_ty.sty {
819 callee_def_id = Some(def_id);
820 match self.tcx.fn_sig(def_id).abi() {
822 Abi::PlatformIntrinsic => {
823 assert!(!self.tcx.is_const_fn(def_id));
824 match &self.tcx.item_name(def_id).as_str()[..] {
840 | "add_with_overflow"
841 | "sub_with_overflow"
842 | "mul_with_overflow" => is_const_fn = Some(def_id),
844 if self.mode != Mode::Fn {
845 is_const_fn = Some(def_id);
846 if !self.tcx.sess.features_untracked().const_transmute {
848 &self.tcx.sess.parse_sess, "const_transmute",
849 self.span, GateIssue::Language,
850 &format!("The use of std::mem::transmute() \
851 is gated in {}s", self.mode));
856 name if name.starts_with("simd_shuffle") => {
864 if self.tcx.is_const_fn(def_id) || self.is_const_panic_fn(def_id) {
865 is_const_fn = Some(def_id);
871 let constant_arguments = callee_def_id.and_then(|id| {
872 args_required_const(self.tcx, id)
874 for (i, arg) in args.iter().enumerate() {
876 this.visit_operand(arg, location);
877 if this.mode != Mode::Fn {
880 let candidate = Candidate::Argument { bb, index: i };
881 if is_shuffle && i == 2 {
882 if this.qualif.is_empty() {
883 this.promotion_candidates.push(candidate);
885 span_err!(this.tcx.sess, this.span, E0526,
886 "shuffle indices are not constant");
891 let constant_arguments = match constant_arguments.as_ref() {
895 if !constant_arguments.contains(&i) {
898 if this.qualif.is_empty() {
899 this.promotion_candidates.push(candidate);
901 this.tcx.sess.span_err(this.span,
902 &format!("argument {} is required to be a constant",
909 if let Some(def_id) = is_const_fn {
910 // check the const_panic feature gate or
911 // find corresponding rustc_const_unstable feature
912 // FIXME: cannot allow this inside `allow_internal_unstable` because that would make
913 // `panic!` insta stable in constants, since the macro is marked with the attr
914 if self.is_const_panic_fn(def_id) {
915 if self.mode == Mode::Fn {
916 // never promote panics
917 self.qualif = Qualif::NOT_CONST;
918 } else if !self.tcx.sess.features_untracked().const_panic {
919 // don't allow panics in constants without the feature gate
921 &self.tcx.sess.parse_sess,
925 &format!("panicking in {}s is unstable", self.mode),
928 } else if let Some(&attr::Stability {
929 const_stability: Some(ref feature_name),
930 .. }) = self.tcx.lookup_stability(def_id) {
932 // feature-gate is not enabled,
934 .declared_lib_features
936 .any(|&(ref sym, _)| sym == feature_name) &&
938 // this doesn't come from a macro that has #[allow_internal_unstable]
939 !self.span.allows_unstable()
941 self.qualif = Qualif::NOT_CONST;
942 if self.mode != Mode::Fn {
943 // inside a constant environment, not having the feature gate is
945 let mut err = self.tcx.sess.struct_span_err(self.span,
946 &format!("`{}` is not yet stable as a const fn",
947 self.tcx.item_path_str(def_id)));
949 "in Nightly builds, add `#![feature({})]` \
950 to the crate attributes to enable",
957 self.qualif = Qualif::NOT_CONST;
958 if self.mode != Mode::Fn {
959 // FIXME(#24111) Remove this check when const fn stabilizes
960 let (msg, note) = if let UnstableFeatures::Disallow =
961 self.tcx.sess.opts.unstable_features {
962 (format!("calls in {}s are limited to \
963 tuple structs and tuple variants",
965 Some("a limited form of compile-time function \
966 evaluation is available on a nightly \
967 compiler via `const fn`"))
969 (format!("calls in {}s are limited \
970 to constant functions, \
971 tuple structs and tuple variants",
975 let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
976 if let Some(note) = note {
977 err.span_note(self.span, note);
983 if let Some((ref dest, _)) = *destination {
984 // Avoid propagating irrelevant callee/argument qualifications.
985 if self.qualif.intersects(Qualif::CONST_ERROR) {
986 self.qualif = Qualif::NOT_CONST;
988 // Be conservative about the returned value of a const fn.
990 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
991 self.qualif = Qualif::empty();
994 self.assign(dest, location);
996 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
997 self.super_terminator_kind(bb, kind, location);
999 // Deny *any* live drops anywhere other than functions.
1000 if self.mode != Mode::Fn {
1001 // HACK(eddyb) Emulate a bit of dataflow analysis,
1002 // conservatively, that drop elaboration will do.
1003 let needs_drop = if let Place::Local(local) = *place {
1004 if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
1005 Some(self.mir.local_decls[local].source_info.span)
1013 if let Some(span) = needs_drop {
1014 // Double-check the type being dropped, to minimize false positives.
1015 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1016 if ty.needs_drop(self.tcx, self.param_env) {
1017 struct_span_err!(self.tcx.sess, span, E0493,
1018 "destructors cannot be evaluated at compile-time")
1019 .span_label(span, format!("{}s cannot evaluate destructors",
1026 // Qualify any operands inside other terminators.
1027 self.super_terminator_kind(bb, kind, location);
1031 fn visit_assign(&mut self,
1034 rvalue: &Rvalue<'tcx>,
1035 location: Location) {
1036 self.visit_rvalue(rvalue, location);
1038 // Check the allowed const fn argument forms.
1039 if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
1040 if self.mir.local_kind(index) == LocalKind::Var &&
1041 self.const_fn_arg_vars.insert(index) &&
1042 !self.tcx.sess.features_untracked().const_let {
1044 // Direct use of an argument is permitted.
1046 Rvalue::Use(Operand::Copy(Place::Local(local))) |
1047 Rvalue::Use(Operand::Move(Place::Local(local))) => {
1048 if self.mir.local_kind(local) == LocalKind::Arg {
1055 // Avoid a generic error for other uses of arguments.
1056 if self.qualif.contains(Qualif::FN_ARGUMENT) {
1057 let decl = &self.mir.local_decls[index];
1058 let mut err = feature_err(
1059 &self.tcx.sess.parse_sess,
1061 decl.source_info.span,
1062 GateIssue::Language,
1063 "arguments of constant functions can only be immutable by-value bindings"
1065 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1066 err.note("Constant functions are not allowed to mutate anything. Thus, \
1067 binding to an argument with a mutable pattern is not allowed.");
1068 err.note("Remove any mutable bindings from the argument list to fix this \
1069 error. In case you need to mutate the argument, try lazily \
1070 initializing a global variable instead of using a const fn, or \
1071 refactoring the code to a functional style to avoid mutation if \
1080 self.assign(dest, location);
1083 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1084 self.span = source_info.span;
1087 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1089 this.visit_source_info(&statement.source_info);
1090 match statement.kind {
1091 StatementKind::Assign(ref place, ref rvalue) => {
1092 this.visit_assign(bb, place, rvalue, location);
1094 StatementKind::ReadForMatch(..) |
1095 StatementKind::SetDiscriminant { .. } |
1096 StatementKind::StorageLive(_) |
1097 StatementKind::StorageDead(_) |
1098 StatementKind::InlineAsm {..} |
1099 StatementKind::EndRegion(_) |
1100 StatementKind::Validate(..) |
1101 StatementKind::AscribeUserType(..) |
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<IdxSet<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(IdxSet::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 if tcx.is_min_const_fn(def_id) {
1184 // enforce `min_const_fn` for stable const fns
1185 use super::qualify_min_const_fn::is_min_const_fn;
1186 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1187 tcx.sess.span_err(span, &err);
1189 // this should not produce any errors, but better safe than sorry
1191 qualifier.qualify_const();
1194 // Enforce a constant-like CFG for `const fn`.
1195 qualifier.qualify_const();
1198 while let Some((bb, data)) = qualifier.rpo.next() {
1199 qualifier.visit_basic_block_data(bb, data);
1203 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1206 // Do the actual promotion, now that we know what's viable.
1207 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1209 let promoted_temps = if mode == Mode::Const {
1210 // Already computed by `mir_const_qualif`.
1211 const_promoted_temps.unwrap()
1213 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1216 // In `const` and `static` everything without `StorageDead`
1217 // is `'static`, we don't have to create promoted MIR fragments,
1218 // just remove `Drop` and `StorageDead` on "promoted" locals.
1219 for block in mir.basic_blocks_mut() {
1220 block.statements.retain(|statement| {
1221 match statement.kind {
1222 StatementKind::StorageDead(index) => {
1223 !promoted_temps.contains(&index)
1228 let terminator = block.terminator_mut();
1229 match terminator.kind {
1230 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1231 if promoted_temps.contains(&index) {
1232 terminator.kind = TerminatorKind::Goto {
1242 // Statics must be Sync.
1243 if mode == Mode::Static {
1244 // `#[thread_local]` statics don't have to be `Sync`.
1245 for attr in &tcx.get_attrs(def_id)[..] {
1246 if attr.check_name("thread_local") {
1250 let ty = mir.return_ty();
1251 tcx.infer_ctxt().enter(|infcx| {
1252 let param_env = ty::ParamEnv::empty();
1253 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1254 let mut fulfillment_cx = traits::FulfillmentContext::new();
1255 fulfillment_cx.register_bound(&infcx,
1258 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1260 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1261 infcx.report_fulfillment_errors(&err, None, false);
1268 fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
1269 let attrs = tcx.get_attrs(def_id);
1270 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1271 let mut ret = FxHashSet();
1272 for meta in attr.meta_item_list()? {
1273 match meta.literal()?.node {
1274 LitKind::Int(a, _) => { ret.insert(a as usize); }