1 //! A pass that qualifies constness of temporaries in constants,
2 //! static initializers and functions and also drives promotion.
4 //! The Qualif flags below can be used to also provide better
5 //! diagnostics as to why a constant rvalue wasn't promoted.
7 use rustc_data_structures::bit_set::BitSet;
8 use rustc_data_structures::indexed_vec::IndexVec;
9 use rustc_data_structures::fx::FxHashSet;
10 use rustc_data_structures::sync::Lrc;
11 use rustc_target::spec::abi::Abi;
13 use rustc::hir::def_id::DefId;
14 use rustc::traits::{self, TraitEngine};
15 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
16 use rustc::ty::cast::CastTy;
17 use rustc::ty::query::Providers;
19 use rustc::mir::traversal::ReversePostorder;
20 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
21 use rustc::middle::lang_items;
22 use rustc::session::config::nightly_options;
23 use syntax::ast::LitKind;
24 use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue};
25 use syntax_pos::{Span, DUMMY_SP};
30 use transform::{MirPass, MirSource};
31 use super::promote_consts::{self, Candidate, TempState};
34 // Borrows of temporaries can be promoted only if
35 // they have none of these qualifications, with
36 // the exception of `STATIC_REF` (in statics only).
38 // Constant containing interior mutability (UnsafeCell).
39 const MUTABLE_INTERIOR = 1 << 0;
41 // Constant containing an ADT that implements Drop.
42 const NEEDS_DROP = 1 << 1;
45 const FN_ARGUMENT = 1 << 2;
47 // Not constant at all - non-`const fn` calls, asm!,
48 // pointer comparisons, ptr-to-int casts, etc.
49 const NOT_CONST = 1 << 3;
51 // Refers to temporaries which cannot be promoted as
52 // promote_consts decided they weren't simple enough.
53 const NOT_PROMOTABLE = 1 << 4;
55 // Const items can only have MUTABLE_INTERIOR
56 // and NOT_PROMOTABLE without producing an error.
57 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
58 !Qualif::NOT_PROMOTABLE.bits;
62 impl<'a, 'tcx> Qualif {
63 /// Remove flags which are impossible for the given type.
64 fn restrict(&mut self, ty: Ty<'tcx>,
65 tcx: TyCtxt<'a, 'tcx, 'tcx>,
66 param_env: ty::ParamEnv<'tcx>) {
67 if ty.is_freeze(tcx, param_env, DUMMY_SP) {
68 *self = *self - Qualif::MUTABLE_INTERIOR;
70 if !ty.needs_drop(tcx, param_env) {
71 *self = *self - Qualif::NEEDS_DROP;
76 /// What kind of item we are in.
77 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
86 impl fmt::Display for Mode {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 Mode::Const => write!(f, "constant"),
90 Mode::Static | Mode::StaticMut => write!(f, "static"),
91 Mode::ConstFn => write!(f, "constant function"),
92 Mode::Fn => write!(f, "function")
97 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
102 rpo: ReversePostorder<'a, 'tcx>,
103 tcx: TyCtxt<'a, 'gcx, 'tcx>,
104 param_env: ty::ParamEnv<'tcx>,
105 local_qualif: IndexVec<Local, Option<Qualif>>,
107 temp_promotion_state: IndexVec<Local, TempState>,
108 promotion_candidates: Vec<Candidate>
111 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
112 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
116 -> Qualifier<'a, 'tcx, 'tcx> {
117 assert!(def_id.is_local());
118 let mut rpo = traversal::reverse_postorder(mir);
119 let temps = promote_consts::collect_temps(mir, &mut rpo);
122 let param_env = tcx.param_env(def_id);
124 let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
125 for arg in mir.args_iter() {
126 let mut qualif = Qualif::NEEDS_DROP;
127 qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
128 local_qualif[arg] = Some(qualif);
140 qualif: Qualif::empty(),
141 temp_promotion_state: temps,
142 promotion_candidates: vec![]
146 // FIXME(eddyb) we could split the errors into meaningful
147 // categories, but enabling full miri would make that
148 // slightly pointless (even with feature-gating).
149 fn not_const(&mut self) {
150 self.add(Qualif::NOT_CONST);
151 if self.mode != Mode::Fn {
152 let mut err = struct_span_err!(
156 "{} contains unimplemented expression type",
159 if self.tcx.sess.teach(&err.get_code().unwrap()) {
160 err.note("A function call isn't allowed in the const's initialization expression \
161 because the expression's value must be known at compile-time.");
162 err.note("Remember: you can't use a function call inside a const's initialization \
163 expression! However, you can use it anywhere else.");
169 /// Add the given qualification to self.qualif.
170 fn add(&mut self, qualif: Qualif) {
171 self.qualif = self.qualif | qualif;
174 /// Add the given type's qualification to self.qualif.
175 fn add_type(&mut self, ty: Ty<'tcx>) {
176 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
177 self.qualif.restrict(ty, self.tcx, self.param_env);
180 /// Within the provided closure, self.qualif will start
181 /// out empty, and its value after the closure returns will
182 /// be combined with the value before the call to nest.
183 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
184 let original = self.qualif;
185 self.qualif = Qualif::empty();
190 /// Assign the current qualification to the given destination.
191 fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
192 trace!("assign: {:?}", dest);
193 let qualif = self.qualif;
194 let span = self.span;
195 let store = |slot: &mut Option<Qualif>| {
197 span_bug!(span, "multiple assignments to {:?}", dest);
199 *slot = Some(qualif);
202 // Only handle promotable temps in non-const functions.
203 if self.mode == Mode::Fn {
204 if let Place::Local(index) = *dest {
205 if self.mir.local_kind(index) == LocalKind::Temp
206 && self.temp_promotion_state[index].is_promotable() {
207 debug!("store to promotable temp {:?} ({:?})", index, qualif);
208 store(&mut self.local_qualif[index]);
217 // We treat all locals equal in constants
218 Place::Local(index) => break *index,
219 // projections are transparent for assignments
220 // we qualify the entire destination at once, even if just a field would have
221 // stricter qualification
222 Place::Projection(proj) => {
223 // Catch more errors in the destination. `visit_place` also checks various
224 // projection rules like union field access and raw pointer deref
227 PlaceContext::MutatingUse(MutatingUseContext::Store),
232 Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
233 Place::Static(..) => {
234 // Catch more errors in the destination. `visit_place` also checks that we
235 // do not try to access statics from constants or try to mutate statics
238 PlaceContext::MutatingUse(MutatingUseContext::Store),
245 debug!("store to var {:?}", index);
246 match &mut self.local_qualif[index] {
247 // this is overly restrictive, because even full assignments do not clear the qualif
248 // While we could special case full assignments, this would be inconsistent with
249 // aggregates where we overwrite all fields via assignments, which would not get
251 Some(ref mut qualif) => *qualif = *qualif | self.qualif,
252 // insert new qualification
253 qualif @ None => *qualif = Some(self.qualif),
257 /// Qualify a whole const, static initializer or const fn.
258 fn qualify_const(&mut self) -> (Qualif, Lrc<BitSet<Local>>) {
259 debug!("qualifying {} {:?}", self.mode, self.def_id);
263 let mut seen_blocks = BitSet::new_empty(mir.basic_blocks().len());
264 let mut bb = START_BLOCK;
266 seen_blocks.insert(bb.index());
268 self.visit_basic_block_data(bb, &mir[bb]);
270 let target = match mir[bb].terminator().kind {
271 TerminatorKind::Goto { target } |
272 TerminatorKind::Drop { target, .. } |
273 TerminatorKind::Assert { target, .. } |
274 TerminatorKind::Call { destination: Some((_, target)), .. } => {
278 // Non-terminating calls cannot produce any value.
279 TerminatorKind::Call { destination: None, .. } => {
283 TerminatorKind::SwitchInt {..} |
284 TerminatorKind::DropAndReplace { .. } |
285 TerminatorKind::Resume |
286 TerminatorKind::Abort |
287 TerminatorKind::GeneratorDrop |
288 TerminatorKind::Yield { .. } |
289 TerminatorKind::Unreachable |
290 TerminatorKind::FalseEdges { .. } |
291 TerminatorKind::FalseUnwind { .. } => None,
293 TerminatorKind::Return => {
300 Some(target) if !seen_blocks.contains(target.index()) => {
310 self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
312 // Account for errors in consts by using the
313 // conservative type qualification instead.
314 if self.qualif.intersects(Qualif::CONST_ERROR) {
315 self.qualif = Qualif::empty();
316 let return_ty = mir.return_ty();
317 self.add_type(return_ty);
321 // Collect all the temps we need to promote.
322 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
324 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
325 for candidate in &self.promotion_candidates {
327 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
328 match self.mir[bb].statements[stmt_idx].kind {
329 StatementKind::Assign(_, box Rvalue::Ref(_, _, Place::Local(index))) => {
330 promoted_temps.insert(index);
335 Candidate::Argument { .. } => {}
339 (self.qualif, Lrc::new(promoted_temps))
342 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
343 Some(def_id) == self.tcx.lang_items().panic_fn() ||
344 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
348 /// Accumulates an Rvalue or Call's effects in self.qualif.
349 /// For functions (constant or not), it also records
350 /// candidates for promotion in promotion_candidates.
351 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
352 fn visit_local(&mut self,
354 _: PlaceContext<'tcx>,
356 debug!("visit_local: local={:?}", local);
357 let kind = self.mir.local_kind(local);
359 LocalKind::ReturnPointer => {
362 LocalKind::Var if self.mode == Mode::Fn => {
363 self.add(Qualif::NOT_CONST);
368 if let LocalKind::Arg = kind {
369 self.add(Qualif::FN_ARGUMENT);
372 if !self.temp_promotion_state[local].is_promotable() {
373 debug!("visit_local: (not promotable) local={:?}", local);
374 self.add(Qualif::NOT_PROMOTABLE);
377 if let Some(qualif) = self.local_qualif[local] {
386 fn visit_place(&mut self,
388 context: PlaceContext<'tcx>,
389 location: Location) {
390 debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
392 Place::Local(ref local) => self.visit_local(local, context, location),
393 Place::Promoted(_) => bug!("promoting already promoted MIR"),
394 Place::Static(ref global) => {
396 .get_attrs(global.def_id)
398 .any(|attr| attr.check_name("thread_local")) {
399 if self.mode != Mode::Fn {
400 span_err!(self.tcx.sess, self.span, E0625,
401 "thread-local statics cannot be \
402 accessed at compile-time");
404 self.add(Qualif::NOT_CONST);
408 // Only allow statics (not consts) to refer to other statics.
409 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
410 if self.mode == Mode::Static && context.is_mutating_use() {
411 // this is not strictly necessary as miri will also bail out
412 // For interior mutability we can't really catch this statically as that
413 // goes through raw pointers and intermediate temporaries, so miri has
414 // to catch this anyway
415 self.tcx.sess.span_err(
417 "cannot mutate statics in the initializer of another static",
422 self.add(Qualif::NOT_CONST);
424 if self.mode != Mode::Fn {
425 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
426 "{}s cannot refer to statics, use \
427 a constant instead", self.mode);
428 if self.tcx.sess.teach(&err.get_code().unwrap()) {
430 "Static and const variables can refer to other const variables. But a \
431 const variable cannot refer to a static variable."
434 "To fix this, the value can be extracted as a const and then used."
440 Place::Projection(ref proj) => {
442 this.super_place(place, context, location);
444 ProjectionElem::Deref => {
445 if context.is_mutating_use() {
446 // `not_const` errors out in const contexts
449 // just make sure this doesn't get promoted
450 this.add(Qualif::NOT_CONST);
452 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
456 if let ty::RawPtr(_) = base_ty.sty {
457 if !this.tcx.features().const_raw_ptr_deref {
459 &this.tcx.sess.parse_sess, "const_raw_ptr_deref",
460 this.span, GateIssue::Language,
462 "dereferencing raw pointers in {}s is unstable",
472 ProjectionElem::ConstantIndex {..} |
473 ProjectionElem::Subslice {..} |
474 ProjectionElem::Field(..) |
475 ProjectionElem::Index(_) => {
476 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
477 if let Some(def) = base_ty.ty_adt_def() {
480 Mode::Fn => this.not_const(),
482 if !this.tcx.features().const_fn_union {
484 &this.tcx.sess.parse_sess, "const_fn_union",
485 this.span, GateIssue::Language,
486 "unions in const fn are unstable",
499 let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
500 this.qualif.restrict(ty, this.tcx, this.param_env);
503 ProjectionElem::Downcast(..) => {
512 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
513 debug!("visit_operand: operand={:?} location={:?}", operand, location);
514 self.super_operand(operand, location);
518 Operand::Move(_) => {
519 // Mark the consumed locals to indicate later drops are noops.
520 if let Operand::Move(Place::Local(local)) = *operand {
521 self.local_qualif[local] = self.local_qualif[local].map(|q|
522 q - Qualif::NEEDS_DROP
526 Operand::Constant(ref constant) => {
527 if let ty::LazyConst::Unevaluated(def_id, _) = constant.literal {
528 // Don't peek inside trait associated constants.
529 if self.tcx.trait_of_item(*def_id).is_some() {
530 self.add_type(constant.ty);
532 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(*def_id);
534 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
537 // Just in case the type is more specific than
538 // the definition, e.g., impl associated const
539 // with type parameters, take it into account.
540 self.qualif.restrict(constant.ty, self.tcx, self.param_env);
547 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
548 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
549 // Recurse through operands and places.
550 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
551 let mut is_reborrow = false;
552 if let Place::Projection(ref proj) = *place {
553 if let ProjectionElem::Deref = proj.elem {
554 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
555 if let ty::Ref(..) = base_ty.sty {
562 let ctx = match kind {
563 BorrowKind::Shared =>
564 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(region)),
565 BorrowKind::Shallow =>
566 PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(region)),
567 BorrowKind::Unique =>
568 PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(region)),
569 BorrowKind::Mut { .. } =>
570 PlaceContext::MutatingUse(MutatingUseContext::Borrow(region)),
572 self.super_place(place, ctx, location);
574 self.super_rvalue(rvalue, location);
577 self.super_rvalue(rvalue, location);
583 Rvalue::UnaryOp(UnOp::Neg, _) |
584 Rvalue::UnaryOp(UnOp::Not, _) |
585 Rvalue::NullaryOp(NullOp::SizeOf, _) |
586 Rvalue::CheckedBinaryOp(..) |
587 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
588 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
589 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
590 Rvalue::Cast(CastKind::Unsize, ..) |
591 Rvalue::Discriminant(..) |
594 Rvalue::Ref(_, kind, ref place) => {
595 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
597 // Default to forbidding the borrow and/or its promotion,
598 // due to the potential for direct or interior mutability,
599 // and only proceed by setting `forbidden_mut` to `false`.
600 let mut forbidden_mut = true;
602 if let BorrowKind::Mut { .. } = kind {
603 // In theory, any zero-sized value could be borrowed
604 // mutably without consequences. However, only &mut []
605 // is allowed right now, and only in functions.
606 if self.mode == Mode::StaticMut {
607 // Inside a `static mut`, &mut [...] is also allowed.
609 ty::Array(..) | ty::Slice(_) => forbidden_mut = false,
612 } else if let ty::Array(_, len) = ty.sty {
613 // FIXME(eddyb) the `self.mode == Mode::Fn` condition
614 // seems unnecessary, given that this is merely a ZST.
615 if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
616 forbidden_mut = false;
621 self.add(Qualif::NOT_CONST);
622 if self.mode != Mode::Fn {
623 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
624 "references in {}s may only refer \
625 to immutable values", self.mode);
626 err.span_label(self.span, format!("{}s require immutable values",
628 if self.tcx.sess.teach(&err.get_code().unwrap()) {
629 err.note("References in statics and constants may only refer to \
630 immutable values.\n\n\
631 Statics are shared everywhere, and if they refer to \
632 mutable data one might violate memory safety since \
633 holding multiple mutable references to shared data is \
635 If you really want global mutable state, try using \
636 static mut or a global UnsafeCell.");
642 // Constants cannot be borrowed if they contain interior mutability as
643 // it means that our "silent insertion of statics" could change
644 // initializer values (very bad).
645 if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
646 // A reference of a MUTABLE_INTERIOR place is instead
647 // NOT_CONST (see `if forbidden_mut` below), to avoid
648 // duplicate errors (from reborrowing, for example).
649 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
650 if self.mode != Mode::Fn {
651 span_err!(self.tcx.sess, self.span, E0492,
652 "cannot borrow a constant which may contain \
653 interior mutability, create a static instead");
656 // We allow immutable borrows of frozen data.
657 forbidden_mut = false;
661 debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut);
663 self.add(Qualif::NOT_CONST);
665 // We might have a candidate for promotion.
666 let candidate = Candidate::Ref(location);
667 // We can only promote interior borrows of promotable temps.
668 let mut place = place;
669 while let Place::Projection(ref proj) = *place {
670 if proj.elem == ProjectionElem::Deref {
675 debug!("visit_rvalue: place={:?}", place);
676 if let Place::Local(local) = *place {
677 if self.mir.local_kind(local) == LocalKind::Temp {
678 debug!("visit_rvalue: local={:?}", local);
679 if let Some(qualif) = self.local_qualif[local] {
680 // `forbidden_mut` is false, so we can safely ignore
681 // `MUTABLE_INTERIOR` from the local's qualifications.
682 // This allows borrowing fields which don't have
683 // `MUTABLE_INTERIOR`, from a type that does, e.g.:
684 // `let _: &'static _ = &(Cell::new(1), 2).1;`
685 debug!("visit_rvalue: qualif={:?}", qualif);
686 if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
687 debug!("visit_rvalue: candidate={:?}", candidate);
688 self.promotion_candidates.push(candidate);
696 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
697 let operand_ty = operand.ty(self.mir, self.tcx);
698 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
699 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
700 match (cast_in, cast_out) {
701 (CastTy::Ptr(_), CastTy::Int(_)) |
702 (CastTy::FnPtr, CastTy::Int(_)) => {
703 if let Mode::Fn = self.mode {
704 // in normal functions, mark such casts as not promotable
705 self.add(Qualif::NOT_CONST);
706 } else if !self.tcx.features().const_raw_ptr_to_usize_cast {
707 // in const fn and constants require the feature gate
708 // FIXME: make it unsafe inside const fn and constants
710 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
711 self.span, GateIssue::Language,
713 "casting pointers to integers in {}s is unstable",
723 Rvalue::BinaryOp(op, ref lhs, _) => {
724 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
725 assert!(op == BinOp::Eq || op == BinOp::Ne ||
726 op == BinOp::Le || op == BinOp::Lt ||
727 op == BinOp::Ge || op == BinOp::Gt ||
728 op == BinOp::Offset);
730 if let Mode::Fn = self.mode {
731 // raw pointer operations are not allowed inside promoteds
732 self.add(Qualif::NOT_CONST);
733 } else if !self.tcx.features().const_compare_raw_pointers {
734 // require the feature gate inside constants and const fn
735 // FIXME: make it unsafe to use these operations
737 &self.tcx.sess.parse_sess,
738 "const_compare_raw_pointers",
741 &format!("comparing raw pointers inside {}", self.mode),
747 Rvalue::NullaryOp(NullOp::Box, _) => {
748 self.add(Qualif::NOT_CONST);
749 if self.mode != Mode::Fn {
750 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
751 "allocations are not allowed in {}s", self.mode);
752 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
753 if self.tcx.sess.teach(&err.get_code().unwrap()) {
755 "The value of statics and constants must be known at compile time, \
756 and they live for the entire lifetime of a program. Creating a boxed \
757 value allocates memory on the heap at runtime, and therefore cannot \
758 be done at compile time."
765 Rvalue::Aggregate(ref kind, _) => {
766 if let AggregateKind::Adt(def, ..) = **kind {
767 if def.has_dtor(self.tcx) {
768 self.add(Qualif::NEEDS_DROP);
771 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
772 let ty = rvalue.ty(self.mir, self.tcx);
774 assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
781 fn visit_terminator_kind(&mut self,
783 kind: &TerminatorKind<'tcx>,
784 location: Location) {
785 debug!("visit_terminator_kind: bb={:?} kind={:?} location={:?}", bb, kind, location);
786 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
787 self.visit_operand(func, location);
789 let fn_ty = func.ty(self.mir, self.tcx);
790 let mut callee_def_id = None;
791 let mut is_shuffle = false;
792 let mut is_const_fn = false;
793 let mut is_promotable_const_fn = false;
795 ty::FnDef(def_id, _) => {
796 callee_def_id = Some(def_id);
797 match self.tcx.fn_sig(def_id).abi() {
799 Abi::PlatformIntrinsic => {
800 assert!(!self.tcx.is_const_fn(def_id));
801 match &self.tcx.item_name(def_id).as_str()[..] {
820 | "add_with_overflow"
821 | "sub_with_overflow"
822 | "mul_with_overflow"
823 // no need to check feature gates, intrinsics are only callable
824 // from the libstd or with forever unstable feature gates
825 => is_const_fn = true,
826 // special intrinsic that can be called diretly without an intrinsic
827 // feature gate needs a language feature gate
829 // never promote transmute calls
830 if self.mode != Mode::Fn {
832 // const eval transmute calls only with the feature gate
833 if !self.tcx.features().const_transmute {
835 &self.tcx.sess.parse_sess, "const_transmute",
836 self.span, GateIssue::Language,
837 &format!("The use of std::mem::transmute() \
838 is gated in {}s", self.mode));
843 name if name.starts_with("simd_shuffle") => {
851 // In normal functions we only care about promotion.
852 if self.mode == Mode::Fn {
853 // Never promote const fn calls of
854 // functions without `#[rustc_promotable]`.
855 if self.tcx.is_promotable_const_fn(def_id) {
857 is_promotable_const_fn = true;
858 } else if self.tcx.is_const_fn(def_id) {
862 // stable const fns or unstable const fns with their feature gate
864 if self.tcx.is_const_fn(def_id) {
866 } else if self.is_const_panic_fn(def_id) {
867 // Check the const_panic feature gate.
868 // FIXME: cannot allow this inside `allow_internal_unstable`
869 // because that would make `panic!` insta stable in constants,
870 // since the macro is marked with the attribute.
871 if self.tcx.features().const_panic {
874 // Don't allow panics in constants without the feature gate.
876 &self.tcx.sess.parse_sess,
880 &format!("panicking in {}s is unstable", self.mode),
883 } else if let Some(feature)
884 = self.tcx.is_unstable_const_fn(def_id) {
885 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
886 // functions without the feature gate active in this crate in
887 // order to report a better error message than the one below.
888 if self.span.allows_unstable() {
889 // `allow_internal_unstable` can make such calls stable.
892 let mut err = self.tcx.sess.struct_span_err(self.span,
893 &format!("`{}` is not yet stable as a const fn",
894 self.tcx.item_path_str(def_id)));
895 if nightly_options::is_nightly_build() {
897 "add `#![feature({})]` to the \
898 crate attributes to enable",
904 // FIXME(#57563): remove this check when const fn stabilizes.
905 let (msg, note) = if let UnstableFeatures::Disallow =
906 self.tcx.sess.opts.unstable_features {
907 (format!("calls in {}s are limited to \
908 tuple structs and tuple variants",
910 Some("a limited form of compile-time function \
911 evaluation is available on a nightly \
912 compiler via `const fn`"))
914 (format!("calls in {}s are limited \
915 to constant functions, \
916 tuple structs and tuple variants",
920 let mut err = struct_span_err!(
927 if let Some(note) = note {
928 err.span_note(self.span, note);
937 if self.mode != Mode::Fn {
938 let mut err = self.tcx.sess.struct_span_err(
940 &format!("function pointers are not allowed in const fn"));
951 let constant_arguments = callee_def_id.and_then(|id| {
952 args_required_const(self.tcx, id)
954 for (i, arg) in args.iter().enumerate() {
956 this.visit_operand(arg, location);
957 if this.mode != Mode::Fn {
960 let candidate = Candidate::Argument { bb, index: i };
961 if is_shuffle && i == 2 {
962 if this.qualif.is_empty() {
963 debug!("visit_terminator_kind: candidate={:?}", candidate);
964 this.promotion_candidates.push(candidate);
966 span_err!(this.tcx.sess, this.span, E0526,
967 "shuffle indices are not constant");
972 let constant_arguments = match constant_arguments.as_ref() {
976 if !constant_arguments.contains(&i) {
979 // Since the argument is required to be constant,
980 // we care about constness, not promotability.
981 // If we checked for promotability, we'd miss out on
982 // the results of function calls (which are never promoted
984 // This is not a problem, because the argument explicitly
985 // requests constness, in contrast to regular promotion
986 // which happens even without the user requesting it.
987 // We can error out with a hard error if the argument is not
989 if (this.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
990 debug!("visit_terminator_kind: candidate={:?}", candidate);
991 this.promotion_candidates.push(candidate);
993 this.tcx.sess.span_err(this.span,
994 &format!("argument {} is required to be a constant",
1000 // non-const fn calls
1002 self.qualif = Qualif::NOT_CONST;
1003 if self.mode != Mode::Fn {
1004 self.tcx.sess.delay_span_bug(
1006 "should have reported an error about non-const fn calls in constants",
1011 if let Some((ref dest, _)) = *destination {
1012 // Avoid propagating irrelevant callee/argument qualifications.
1013 if self.qualif.intersects(Qualif::CONST_ERROR) {
1014 self.qualif = Qualif::NOT_CONST;
1016 // Be conservative about the returned value of a const fn.
1018 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
1019 if is_const_fn && !is_promotable_const_fn && self.mode == Mode::Fn {
1020 self.qualif = Qualif::NOT_PROMOTABLE;
1022 self.qualif = Qualif::empty();
1026 self.assign(dest, location);
1028 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1029 self.super_terminator_kind(bb, kind, location);
1031 // Deny *any* live drops anywhere other than functions.
1032 if self.mode != Mode::Fn {
1033 // HACK(eddyb): emulate a bit of dataflow analysis,
1034 // conservatively, that drop elaboration will do.
1035 let needs_drop = if let Place::Local(local) = *place {
1036 if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
1037 Some(self.mir.local_decls[local].source_info.span)
1045 if let Some(span) = needs_drop {
1046 // Double-check the type being dropped, to minimize false positives.
1047 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1048 if ty.needs_drop(self.tcx, self.param_env) {
1049 struct_span_err!(self.tcx.sess, span, E0493,
1050 "destructors cannot be evaluated at compile-time")
1051 .span_label(span, format!("{}s cannot evaluate destructors",
1058 // Qualify any operands inside other terminators.
1059 self.super_terminator_kind(bb, kind, location);
1063 fn visit_assign(&mut self,
1066 rvalue: &Rvalue<'tcx>,
1067 location: Location) {
1068 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1069 self.visit_rvalue(rvalue, location);
1071 self.assign(dest, location);
1074 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1075 debug!("visit_source_info: source_info={:?}", source_info);
1076 self.span = source_info.span;
1079 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1080 debug!("visit_statement: bb={:?} statement={:?} location={:?}", bb, statement, location);
1082 this.visit_source_info(&statement.source_info);
1083 match statement.kind {
1084 StatementKind::Assign(ref place, ref rvalue) => {
1085 this.visit_assign(bb, place, rvalue, location);
1087 StatementKind::FakeRead(..) |
1088 StatementKind::SetDiscriminant { .. } |
1089 StatementKind::StorageLive(_) |
1090 StatementKind::StorageDead(_) |
1091 StatementKind::InlineAsm {..} |
1092 StatementKind::Retag { .. } |
1093 StatementKind::AscribeUserType(..) |
1094 StatementKind::Nop => {}
1099 fn visit_terminator(&mut self,
1101 terminator: &Terminator<'tcx>,
1102 location: Location) {
1103 debug!("visit_terminator: bb={:?} terminator={:?} location={:?}", bb, terminator, location);
1104 self.nest(|this| this.super_terminator(bb, terminator, location));
1108 pub fn provide(providers: &mut Providers) {
1109 *providers = Providers {
1115 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1117 -> (u8, Lrc<BitSet<Local>>) {
1118 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1119 // cannot yet be stolen), because `mir_validated()`, which steals
1120 // from `mir_const(), forces this query to execute before
1121 // performing the steal.
1122 let mir = &tcx.mir_const(def_id).borrow();
1124 if mir.return_ty().references_error() {
1125 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1126 return (Qualif::NOT_CONST.bits(), Lrc::new(BitSet::new_empty(0)));
1129 let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const);
1130 let (qualif, promoted_temps) = qualifier.qualify_const();
1131 (qualif.bits(), promoted_temps)
1134 pub struct QualifyAndPromoteConstants;
1136 impl MirPass for QualifyAndPromoteConstants {
1137 fn run_pass<'a, 'tcx>(&self,
1138 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1140 mir: &mut Mir<'tcx>) {
1141 // There's not really any point in promoting errorful MIR.
1142 if mir.return_ty().references_error() {
1143 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1147 if src.promoted.is_some() {
1151 let def_id = src.def_id;
1152 let id = tcx.hir().as_local_node_id(def_id).unwrap();
1153 let mut const_promoted_temps = None;
1154 let mode = match tcx.hir().body_owner_kind(id) {
1155 hir::BodyOwnerKind::Fn => {
1156 if tcx.is_const_fn(def_id) {
1162 hir::BodyOwnerKind::Const => {
1163 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1166 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1167 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1170 debug!("run_pass: mode={:?}", mode);
1171 if mode == Mode::Fn || mode == Mode::ConstFn {
1172 // This is ugly because Qualifier holds onto mir,
1173 // which can't be mutated until its scope ends.
1174 let (temps, candidates) = {
1175 let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
1176 if mode == Mode::ConstFn {
1177 if tcx.is_min_const_fn(def_id) {
1178 // enforce `min_const_fn` for stable const fns
1179 use super::qualify_min_const_fn::is_min_const_fn;
1180 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1181 tcx.sess.span_err(span, &err);
1183 // this should not produce any errors, but better safe than sorry
1185 qualifier.qualify_const();
1188 // Enforce a constant-like CFG for `const fn`.
1189 qualifier.qualify_const();
1192 while let Some((bb, data)) = qualifier.rpo.next() {
1193 qualifier.visit_basic_block_data(bb, data);
1197 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1200 // Do the actual promotion, now that we know what's viable.
1201 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1203 if !mir.control_flow_destroyed.is_empty() {
1204 let mut locals = mir.vars_iter();
1205 if let Some(local) = locals.next() {
1206 let span = mir.local_decls[local].source_info.span;
1207 let mut error = tcx.sess.struct_span_err(
1210 "new features like let bindings are not permitted in {}s \
1211 which also use short circuiting operators",
1215 for (span, kind) in mir.control_flow_destroyed.iter() {
1218 &format!("use of {} here does not actually short circuit due to \
1219 the const evaluator presently not being able to do control flow. \
1220 See https://github.com/rust-lang/rust/issues/49146 for more \
1221 information.", kind),
1224 for local in locals {
1225 let span = mir.local_decls[local].source_info.span;
1228 "more locals defined here",
1234 let promoted_temps = if mode == Mode::Const {
1235 // Already computed by `mir_const_qualif`.
1236 const_promoted_temps.unwrap()
1238 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1241 // In `const` and `static` everything without `StorageDead`
1242 // is `'static`, we don't have to create promoted MIR fragments,
1243 // just remove `Drop` and `StorageDead` on "promoted" locals.
1244 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1245 for block in mir.basic_blocks_mut() {
1246 block.statements.retain(|statement| {
1247 match statement.kind {
1248 StatementKind::StorageDead(index) => {
1249 !promoted_temps.contains(index)
1254 let terminator = block.terminator_mut();
1255 match terminator.kind {
1256 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1257 if promoted_temps.contains(index) {
1258 terminator.kind = TerminatorKind::Goto {
1268 // Statics must be Sync.
1269 if mode == Mode::Static {
1270 // `#[thread_local]` statics don't have to be `Sync`.
1271 for attr in &tcx.get_attrs(def_id)[..] {
1272 if attr.check_name("thread_local") {
1276 let ty = mir.return_ty();
1277 tcx.infer_ctxt().enter(|infcx| {
1278 let param_env = ty::ParamEnv::empty();
1279 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1280 let mut fulfillment_cx = traits::FulfillmentContext::new();
1281 fulfillment_cx.register_bound(&infcx,
1284 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1286 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1287 infcx.report_fulfillment_errors(&err, None, false);
1294 fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
1295 let attrs = tcx.get_attrs(def_id);
1296 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1297 let mut ret = FxHashSet::default();
1298 for meta in attr.meta_item_list()? {
1299 match meta.literal()?.node {
1300 LitKind::Int(a, _) => { ret.insert(a as usize); }