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 => {
363 LocalKind::Var if self.mode == Mode::Fn => {
364 self.add(Qualif::NOT_CONST);
369 if let LocalKind::Arg = kind {
370 self.add(Qualif::FN_ARGUMENT);
373 if !self.temp_promotion_state[local].is_promotable() {
374 debug!("visit_local: (not promotable) local={:?}", local);
375 self.add(Qualif::NOT_PROMOTABLE);
378 if let Some(qualif) = self.local_qualif[local] {
387 fn visit_place(&mut self,
389 context: PlaceContext<'tcx>,
390 location: Location) {
391 debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
393 Place::Local(ref local) => self.visit_local(local, context, location),
394 Place::Promoted(_) => bug!("promoting already promoted MIR"),
395 Place::Static(ref global) => {
397 .get_attrs(global.def_id)
399 .any(|attr| attr.check_name("thread_local")) {
400 if self.mode != Mode::Fn {
401 span_err!(self.tcx.sess, self.span, E0625,
402 "thread-local statics cannot be \
403 accessed at compile-time");
405 self.add(Qualif::NOT_CONST);
409 // Only allow statics (not consts) to refer to other statics.
410 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
411 if self.mode == Mode::Static && context.is_mutating_use() {
412 // this is not strictly necessary as miri will also bail out
413 // For interior mutability we can't really catch this statically as that
414 // goes through raw pointers and intermediate temporaries, so miri has
415 // to catch this anyway
416 self.tcx.sess.span_err(
418 "cannot mutate statics in the initializer of another static",
423 self.add(Qualif::NOT_CONST);
425 if self.mode != Mode::Fn {
426 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
427 "{}s cannot refer to statics, use \
428 a constant instead", self.mode);
429 if self.tcx.sess.teach(&err.get_code().unwrap()) {
431 "Static and const variables can refer to other const variables. But a \
432 const variable cannot refer to a static variable."
435 "To fix this, the value can be extracted as a const and then used."
441 Place::Projection(ref proj) => {
443 this.super_place(place, context, location);
445 ProjectionElem::Deref => {
446 if context.is_mutating_use() {
447 // `not_const` errors out in const contexts
450 // just make sure this doesn't get promoted
451 this.add(Qualif::NOT_CONST);
453 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
457 if let ty::RawPtr(_) = base_ty.sty {
458 if !this.tcx.features().const_raw_ptr_deref {
460 &this.tcx.sess.parse_sess, "const_raw_ptr_deref",
461 this.span, GateIssue::Language,
463 "dereferencing raw pointers in {}s is unstable",
473 ProjectionElem::ConstantIndex {..} |
474 ProjectionElem::Subslice {..} |
475 ProjectionElem::Field(..) |
476 ProjectionElem::Index(_) => {
477 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
478 if let Some(def) = base_ty.ty_adt_def() {
481 Mode::Fn => this.not_const(),
483 if !this.tcx.features().const_fn_union {
485 &this.tcx.sess.parse_sess, "const_fn_union",
486 this.span, GateIssue::Language,
487 "unions in const fn are unstable",
500 let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
501 this.qualif.restrict(ty, this.tcx, this.param_env);
504 ProjectionElem::Downcast(..) => {
513 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
514 debug!("visit_operand: operand={:?} location={:?}", operand, location);
515 self.super_operand(operand, location);
519 Operand::Move(_) => {
520 // Mark the consumed locals to indicate later drops are noops.
521 if let Operand::Move(Place::Local(local)) = *operand {
522 self.local_qualif[local] = self.local_qualif[local].map(|q|
523 q - Qualif::NEEDS_DROP
527 Operand::Constant(ref constant) => {
528 if let ty::LazyConst::Unevaluated(def_id, _) = constant.literal {
529 // Don't peek inside trait associated constants.
530 if self.tcx.trait_of_item(*def_id).is_some() {
531 self.add_type(constant.ty);
533 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(*def_id);
535 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
538 // Just in case the type is more specific than
539 // the definition, e.g., impl associated const
540 // with type parameters, take it into account.
541 self.qualif.restrict(constant.ty, self.tcx, self.param_env);
548 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
549 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
550 // Recurse through operands and places.
551 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
552 let mut is_reborrow = false;
553 if let Place::Projection(ref proj) = *place {
554 if let ProjectionElem::Deref = proj.elem {
555 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
556 if let ty::Ref(..) = base_ty.sty {
563 let ctx = match kind {
564 BorrowKind::Shared =>
565 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(region)),
566 BorrowKind::Shallow =>
567 PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(region)),
568 BorrowKind::Unique =>
569 PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(region)),
570 BorrowKind::Mut { .. } =>
571 PlaceContext::MutatingUse(MutatingUseContext::Borrow(region)),
573 self.super_place(place, ctx, location);
575 self.super_rvalue(rvalue, location);
578 self.super_rvalue(rvalue, location);
584 Rvalue::UnaryOp(UnOp::Neg, _) |
585 Rvalue::UnaryOp(UnOp::Not, _) |
586 Rvalue::NullaryOp(NullOp::SizeOf, _) |
587 Rvalue::CheckedBinaryOp(..) |
588 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
589 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
590 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
591 Rvalue::Cast(CastKind::Unsize, ..) |
592 Rvalue::Discriminant(..) |
595 Rvalue::Ref(_, kind, ref place) => {
596 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
598 // Default to forbidding the borrow and/or its promotion,
599 // due to the potential for direct or interior mutability,
600 // and only proceed by setting `forbidden_mut` to `false`.
601 let mut forbidden_mut = true;
603 if let BorrowKind::Mut { .. } = kind {
604 // In theory, any zero-sized value could be borrowed
605 // mutably without consequences. However, only &mut []
606 // is allowed right now, and only in functions.
607 if self.mode == Mode::StaticMut {
608 // Inside a `static mut`, &mut [...] is also allowed.
610 ty::Array(..) | ty::Slice(_) => forbidden_mut = false,
613 } else if let ty::Array(_, len) = ty.sty {
614 // FIXME(eddyb) the `self.mode == Mode::Fn` condition
615 // seems unnecessary, given that this is merely a ZST.
616 if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
617 forbidden_mut = false;
622 self.add(Qualif::NOT_CONST);
623 if self.mode != Mode::Fn {
624 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
625 "references in {}s may only refer \
626 to immutable values", self.mode);
627 err.span_label(self.span, format!("{}s require immutable values",
629 if self.tcx.sess.teach(&err.get_code().unwrap()) {
630 err.note("References in statics and constants may only refer to \
631 immutable values.\n\n\
632 Statics are shared everywhere, and if they refer to \
633 mutable data one might violate memory safety since \
634 holding multiple mutable references to shared data is \
636 If you really want global mutable state, try using \
637 static mut or a global UnsafeCell.");
643 // Constants cannot be borrowed if they contain interior mutability as
644 // it means that our "silent insertion of statics" could change
645 // initializer values (very bad).
646 if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
647 // A reference of a MUTABLE_INTERIOR place is instead
648 // NOT_CONST (see `if forbidden_mut` below), to avoid
649 // duplicate errors (from reborrowing, for example).
650 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
651 if self.mode != Mode::Fn {
652 span_err!(self.tcx.sess, self.span, E0492,
653 "cannot borrow a constant which may contain \
654 interior mutability, create a static instead");
657 // We allow immutable borrows of frozen data.
658 forbidden_mut = false;
662 debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut);
664 self.add(Qualif::NOT_CONST);
666 // We might have a candidate for promotion.
667 let candidate = Candidate::Ref(location);
668 // We can only promote interior borrows of promotable temps.
669 let mut place = place;
670 while let Place::Projection(ref proj) = *place {
671 if proj.elem == ProjectionElem::Deref {
676 debug!("visit_rvalue: place={:?}", place);
677 if let Place::Local(local) = *place {
678 if self.mir.local_kind(local) == LocalKind::Temp {
679 debug!("visit_rvalue: local={:?}", local);
680 if let Some(qualif) = self.local_qualif[local] {
681 // `forbidden_mut` is false, so we can safely ignore
682 // `MUTABLE_INTERIOR` from the local's qualifications.
683 // This allows borrowing fields which don't have
684 // `MUTABLE_INTERIOR`, from a type that does, e.g.:
685 // `let _: &'static _ = &(Cell::new(1), 2).1;`
686 debug!("visit_rvalue: qualif={:?}", qualif);
687 if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
688 debug!("visit_rvalue: candidate={:?}", candidate);
689 self.promotion_candidates.push(candidate);
697 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
698 let operand_ty = operand.ty(self.mir, self.tcx);
699 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
700 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
701 match (cast_in, cast_out) {
702 (CastTy::Ptr(_), CastTy::Int(_)) |
703 (CastTy::FnPtr, CastTy::Int(_)) => {
704 if let Mode::Fn = self.mode {
705 // in normal functions, mark such casts as not promotable
706 self.add(Qualif::NOT_CONST);
707 } else if !self.tcx.features().const_raw_ptr_to_usize_cast {
708 // in const fn and constants require the feature gate
709 // FIXME: make it unsafe inside const fn and constants
711 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
712 self.span, GateIssue::Language,
714 "casting pointers to integers in {}s is unstable",
724 Rvalue::BinaryOp(op, ref lhs, _) => {
725 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
726 assert!(op == BinOp::Eq || op == BinOp::Ne ||
727 op == BinOp::Le || op == BinOp::Lt ||
728 op == BinOp::Ge || op == BinOp::Gt ||
729 op == BinOp::Offset);
731 if let Mode::Fn = self.mode {
732 // raw pointer operations are not allowed inside promoteds
733 self.add(Qualif::NOT_CONST);
734 } else if !self.tcx.features().const_compare_raw_pointers {
735 // require the feature gate inside constants and const fn
736 // FIXME: make it unsafe to use these operations
738 &self.tcx.sess.parse_sess,
739 "const_compare_raw_pointers",
742 &format!("comparing raw pointers inside {}", self.mode),
748 Rvalue::NullaryOp(NullOp::Box, _) => {
749 self.add(Qualif::NOT_CONST);
750 if self.mode != Mode::Fn {
751 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
752 "allocations are not allowed in {}s", self.mode);
753 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
754 if self.tcx.sess.teach(&err.get_code().unwrap()) {
756 "The value of statics and constants must be known at compile time, \
757 and they live for the entire lifetime of a program. Creating a boxed \
758 value allocates memory on the heap at runtime, and therefore cannot \
759 be done at compile time."
766 Rvalue::Aggregate(ref kind, _) => {
767 if let AggregateKind::Adt(def, ..) = **kind {
768 if def.has_dtor(self.tcx) {
769 self.add(Qualif::NEEDS_DROP);
772 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
773 let ty = rvalue.ty(self.mir, self.tcx);
775 assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
782 fn visit_terminator_kind(&mut self,
784 kind: &TerminatorKind<'tcx>,
785 location: Location) {
786 debug!("visit_terminator_kind: bb={:?} kind={:?} location={:?}", bb, kind, location);
787 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
788 self.visit_operand(func, location);
790 let fn_ty = func.ty(self.mir, self.tcx);
791 let mut callee_def_id = None;
792 let mut is_shuffle = false;
793 let mut is_const_fn = false;
794 let mut is_promotable_const_fn = false;
796 ty::FnDef(def_id, _) => {
797 callee_def_id = Some(def_id);
798 match self.tcx.fn_sig(def_id).abi() {
800 Abi::PlatformIntrinsic => {
801 assert!(!self.tcx.is_const_fn(def_id));
802 match &self.tcx.item_name(def_id).as_str()[..] {
821 | "add_with_overflow"
822 | "sub_with_overflow"
823 | "mul_with_overflow"
824 // no need to check feature gates, intrinsics are only callable
825 // from the libstd or with forever unstable feature gates
826 => is_const_fn = true,
827 // special intrinsic that can be called diretly without an intrinsic
828 // feature gate needs a language feature gate
830 // never promote transmute calls
831 if self.mode != Mode::Fn {
833 // const eval transmute calls only with the feature gate
834 if !self.tcx.features().const_transmute {
836 &self.tcx.sess.parse_sess, "const_transmute",
837 self.span, GateIssue::Language,
838 &format!("The use of std::mem::transmute() \
839 is gated in {}s", self.mode));
844 name if name.starts_with("simd_shuffle") => {
852 // In normal functions we only care about promotion.
853 if self.mode == Mode::Fn {
854 // Never promote const fn calls of
855 // functions without `#[rustc_promotable]`.
856 if self.tcx.is_promotable_const_fn(def_id) {
858 is_promotable_const_fn = true;
859 } else if self.tcx.is_const_fn(def_id) {
863 // stable const fns or unstable const fns with their feature gate
865 if self.tcx.is_const_fn(def_id) {
867 } else if self.is_const_panic_fn(def_id) {
868 // Check the const_panic feature gate.
869 // FIXME: cannot allow this inside `allow_internal_unstable`
870 // because that would make `panic!` insta stable in constants,
871 // since the macro is marked with the attribute.
872 if self.tcx.features().const_panic {
875 // Don't allow panics in constants without the feature gate.
877 &self.tcx.sess.parse_sess,
881 &format!("panicking in {}s is unstable", self.mode),
884 } else if let Some(feature)
885 = self.tcx.is_unstable_const_fn(def_id) {
886 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
887 // functions without the feature gate active in this crate in
888 // order to report a better error message than the one below.
889 if self.span.allows_unstable() {
890 // `allow_internal_unstable` can make such calls stable.
893 let mut err = self.tcx.sess.struct_span_err(self.span,
894 &format!("`{}` is not yet stable as a const fn",
895 self.tcx.item_path_str(def_id)));
896 if nightly_options::is_nightly_build() {
898 "add `#![feature({})]` to the \
899 crate attributes to enable",
905 // FIXME(#24111): remove this check when const fn stabilizes.
906 let (msg, note) = if let UnstableFeatures::Disallow =
907 self.tcx.sess.opts.unstable_features {
908 (format!("calls in {}s are limited to \
909 tuple structs and tuple variants",
911 Some("a limited form of compile-time function \
912 evaluation is available on a nightly \
913 compiler via `const fn`"))
915 (format!("calls in {}s are limited \
916 to constant functions, \
917 tuple structs and tuple variants",
921 let mut err = struct_span_err!(
928 if let Some(note) = note {
929 err.span_note(self.span, note);
938 if self.mode != Mode::Fn {
939 let mut err = self.tcx.sess.struct_span_err(
941 &format!("function pointers are not allowed in const fn"));
952 let constant_arguments = callee_def_id.and_then(|id| {
953 args_required_const(self.tcx, id)
955 for (i, arg) in args.iter().enumerate() {
957 this.visit_operand(arg, location);
958 if this.mode != Mode::Fn {
961 let candidate = Candidate::Argument { bb, index: i };
962 if is_shuffle && i == 2 {
963 if this.qualif.is_empty() {
964 debug!("visit_terminator_kind: candidate={:?}", candidate);
965 this.promotion_candidates.push(candidate);
967 span_err!(this.tcx.sess, this.span, E0526,
968 "shuffle indices are not constant");
973 let constant_arguments = match constant_arguments.as_ref() {
977 if !constant_arguments.contains(&i) {
980 // Since the argument is required to be constant,
981 // we care about constness, not promotability.
982 // If we checked for promotability, we'd miss out on
983 // the results of function calls (which are never promoted
985 // This is not a problem, because the argument explicitly
986 // requests constness, in contrast to regular promotion
987 // which happens even without the user requesting it.
988 // We can error out with a hard error if the argument is not
990 if (this.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
991 debug!("visit_terminator_kind: candidate={:?}", candidate);
992 this.promotion_candidates.push(candidate);
994 this.tcx.sess.span_err(this.span,
995 &format!("argument {} is required to be a constant",
1001 // non-const fn calls
1003 self.qualif = Qualif::NOT_CONST;
1004 if self.mode != Mode::Fn {
1005 self.tcx.sess.delay_span_bug(
1007 "should have reported an error about non-const fn calls in constants",
1012 if let Some((ref dest, _)) = *destination {
1013 // Avoid propagating irrelevant callee/argument qualifications.
1014 if self.qualif.intersects(Qualif::CONST_ERROR) {
1015 self.qualif = Qualif::NOT_CONST;
1017 // Be conservative about the returned value of a const fn.
1019 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
1020 if is_const_fn && !is_promotable_const_fn && self.mode == Mode::Fn {
1021 self.qualif = Qualif::NOT_PROMOTABLE;
1023 self.qualif = Qualif::empty();
1027 self.assign(dest, location);
1029 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1030 self.super_terminator_kind(bb, kind, location);
1032 // Deny *any* live drops anywhere other than functions.
1033 if self.mode != Mode::Fn {
1034 // HACK(eddyb): emulate a bit of dataflow analysis,
1035 // conservatively, that drop elaboration will do.
1036 let needs_drop = if let Place::Local(local) = *place {
1037 if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
1038 Some(self.mir.local_decls[local].source_info.span)
1046 if let Some(span) = needs_drop {
1047 // Double-check the type being dropped, to minimize false positives.
1048 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1049 if ty.needs_drop(self.tcx, self.param_env) {
1050 struct_span_err!(self.tcx.sess, span, E0493,
1051 "destructors cannot be evaluated at compile-time")
1052 .span_label(span, format!("{}s cannot evaluate destructors",
1059 // Qualify any operands inside other terminators.
1060 self.super_terminator_kind(bb, kind, location);
1064 fn visit_assign(&mut self,
1067 rvalue: &Rvalue<'tcx>,
1068 location: Location) {
1069 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1070 self.visit_rvalue(rvalue, location);
1072 self.assign(dest, location);
1075 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1076 debug!("visit_source_info: source_info={:?}", source_info);
1077 self.span = source_info.span;
1080 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1081 debug!("visit_statement: bb={:?} statement={:?} location={:?}", bb, statement, location);
1083 this.visit_source_info(&statement.source_info);
1084 match statement.kind {
1085 StatementKind::Assign(ref place, ref rvalue) => {
1086 this.visit_assign(bb, place, rvalue, location);
1088 StatementKind::FakeRead(..) |
1089 StatementKind::SetDiscriminant { .. } |
1090 StatementKind::StorageLive(_) |
1091 StatementKind::StorageDead(_) |
1092 StatementKind::InlineAsm {..} |
1093 StatementKind::Retag { .. } |
1094 StatementKind::AscribeUserType(..) |
1095 StatementKind::Nop => {}
1100 fn visit_terminator(&mut self,
1102 terminator: &Terminator<'tcx>,
1103 location: Location) {
1104 debug!("visit_terminator: bb={:?} terminator={:?} location={:?}", bb, terminator, location);
1105 self.nest(|this| this.super_terminator(bb, terminator, location));
1109 pub fn provide(providers: &mut Providers) {
1110 *providers = Providers {
1116 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1118 -> (u8, Lrc<BitSet<Local>>) {
1119 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1120 // cannot yet be stolen), because `mir_validated()`, which steals
1121 // from `mir_const(), forces this query to execute before
1122 // performing the steal.
1123 let mir = &tcx.mir_const(def_id).borrow();
1125 if mir.return_ty().references_error() {
1126 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1127 return (Qualif::NOT_CONST.bits(), Lrc::new(BitSet::new_empty(0)));
1130 let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const);
1131 let (qualif, promoted_temps) = qualifier.qualify_const();
1132 (qualif.bits(), promoted_temps)
1135 pub struct QualifyAndPromoteConstants;
1137 impl MirPass for QualifyAndPromoteConstants {
1138 fn run_pass<'a, 'tcx>(&self,
1139 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1141 mir: &mut Mir<'tcx>) {
1142 // There's not really any point in promoting errorful MIR.
1143 if mir.return_ty().references_error() {
1144 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1148 if src.promoted.is_some() {
1152 let def_id = src.def_id;
1153 let id = tcx.hir().as_local_node_id(def_id).unwrap();
1154 let mut const_promoted_temps = None;
1155 let mode = match tcx.hir().body_owner_kind(id) {
1156 hir::BodyOwnerKind::Fn => {
1157 if tcx.is_const_fn(def_id) {
1163 hir::BodyOwnerKind::Const => {
1164 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1167 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1168 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1171 debug!("run_pass: mode={:?}", mode);
1172 if mode == Mode::Fn || mode == Mode::ConstFn {
1173 // This is ugly because Qualifier holds onto mir,
1174 // which can't be mutated until its scope ends.
1175 let (temps, candidates) = {
1176 let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
1177 if mode == Mode::ConstFn {
1178 if tcx.is_min_const_fn(def_id) {
1179 // enforce `min_const_fn` for stable const fns
1180 use super::qualify_min_const_fn::is_min_const_fn;
1181 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1182 tcx.sess.span_err(span, &err);
1184 // this should not produce any errors, but better safe than sorry
1186 qualifier.qualify_const();
1189 // Enforce a constant-like CFG for `const fn`.
1190 qualifier.qualify_const();
1193 while let Some((bb, data)) = qualifier.rpo.next() {
1194 qualifier.visit_basic_block_data(bb, data);
1198 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1201 // Do the actual promotion, now that we know what's viable.
1202 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1204 if !mir.control_flow_destroyed.is_empty() {
1205 let mut locals = mir.vars_iter();
1206 if let Some(local) = locals.next() {
1207 let span = mir.local_decls[local].source_info.span;
1208 let mut error = tcx.sess.struct_span_err(
1211 "new features like let bindings are not permitted in {}s \
1212 which also use short circuiting operators",
1216 for (span, kind) in mir.control_flow_destroyed.iter() {
1219 &format!("use of {} here does not actually short circuit due to \
1220 the const evaluator presently not being able to do control flow. \
1221 See https://github.com/rust-lang/rust/issues/49146 for more \
1222 information.", kind),
1225 for local in locals {
1226 let span = mir.local_decls[local].source_info.span;
1229 "more locals defined here",
1235 let promoted_temps = if mode == Mode::Const {
1236 // Already computed by `mir_const_qualif`.
1237 const_promoted_temps.unwrap()
1239 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1242 // In `const` and `static` everything without `StorageDead`
1243 // is `'static`, we don't have to create promoted MIR fragments,
1244 // just remove `Drop` and `StorageDead` on "promoted" locals.
1245 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1246 for block in mir.basic_blocks_mut() {
1247 block.statements.retain(|statement| {
1248 match statement.kind {
1249 StatementKind::StorageDead(index) => {
1250 !promoted_temps.contains(index)
1255 let terminator = block.terminator_mut();
1256 match terminator.kind {
1257 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1258 if promoted_temps.contains(index) {
1259 terminator.kind = TerminatorKind::Goto {
1269 // Statics must be Sync.
1270 if mode == Mode::Static {
1271 // `#[thread_local]` statics don't have to be `Sync`.
1272 for attr in &tcx.get_attrs(def_id)[..] {
1273 if attr.check_name("thread_local") {
1277 let ty = mir.return_ty();
1278 tcx.infer_ctxt().enter(|infcx| {
1279 let param_env = ty::ParamEnv::empty();
1280 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1281 let mut fulfillment_cx = traits::FulfillmentContext::new();
1282 fulfillment_cx.register_bound(&infcx,
1285 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1287 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1288 infcx.report_fulfillment_errors(&err, None, false);
1295 fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
1296 let attrs = tcx.get_attrs(def_id);
1297 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1298 let mut ret = FxHashSet::default();
1299 for meta in attr.meta_item_list()? {
1300 match meta.literal()?.node {
1301 LitKind::Int(a, _) => { ret.insert(a as usize); }